x86_64: rewrite

This commit is contained in:
Jacob Young 2024-11-25 02:58:30 -05:00
parent 257054a146
commit af1191ea8b
12 changed files with 1910 additions and 973 deletions

View File

@ -893,14 +893,38 @@ pub const Inst = struct {
pub const Index = enum(u32) {
_,
pub fn toRef(i: Index) Inst.Ref {
assert(@intFromEnum(i) >> 31 == 0);
return @enumFromInt((1 << 31) | @intFromEnum(i));
pub fn unwrap(index: Index) union(enum) { ref: Inst.Ref, target: u31 } {
const low_index: u31 = @truncate(@intFromEnum(index));
return switch (@as(u1, @intCast(@intFromEnum(index) >> 31))) {
0 => .{ .ref = @enumFromInt(@as(u32, 1 << 31) | low_index) },
1 => .{ .target = low_index },
};
}
pub fn toTargetIndex(i: Index) u31 {
assert(@intFromEnum(i) >> 31 == 1);
return @truncate(@intFromEnum(i));
pub fn toRef(index: Index) Inst.Ref {
return index.unwrap().ref;
}
pub fn fromTargetIndex(index: u31) Index {
return @enumFromInt((1 << 31) | @as(u32, index));
}
pub fn toTargetIndex(index: Index) u31 {
return index.unwrap().target;
}
pub fn format(
index: Index,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
try writer.writeByte('%');
switch (index.unwrap()) {
.ref => {},
.target => try writer.writeByte('t'),
}
try writer.print("{d}", .{@as(u31, @truncate(@intFromEnum(index)))});
}
};

View File

@ -3067,6 +3067,7 @@ pub fn saveState(comp: *Compilation) !void {
// linker state
switch (lf.tag) {
.wasm => {
dev.check(link.File.Tag.wasm.devFeature());
const wasm = lf.cast(.wasm).?;
const is_obj = comp.config.output_mode == .Obj;
try bufs.ensureUnusedCapacity(85);

View File

@ -202,14 +202,6 @@ pub fn operandDies(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) bool
return (l.tomb_bits[usize_index] & mask) != 0;
}
pub fn clearOperandDeath(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) void {
assert(operand < bpi - 1);
const usize_index = (@intFromEnum(inst) * bpi) / @bitSizeOf(usize);
const mask = @as(usize, 1) <<
@as(Log2Int(usize), @intCast((@intFromEnum(inst) % (@bitSizeOf(usize) / bpi)) * bpi + operand));
l.tomb_bits[usize_index] &= ~mask;
}
const OperandCategory = enum {
/// The operand lives on, but this instruction cannot possibly mutate memory.
none,
@ -844,12 +836,6 @@ const Analysis = struct {
special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
extra: std.ArrayListUnmanaged(u32),
fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void {
const usize_index = (inst * bpi) / @bitSizeOf(usize);
a.tomb_bits[usize_index] |= @as(usize, tomb_bits) <<
@as(Log2Int(usize), @intCast((inst % (@bitSizeOf(usize) / bpi)) * bpi));
}
fn addExtra(a: *Analysis, extra: anytype) Allocator.Error!u32 {
const fields = std.meta.fields(@TypeOf(extra));
try a.extra.ensureUnusedCapacity(a.gpa, fields.len);

View File

@ -71,6 +71,8 @@ end_di_column: u32,
/// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined,
/// We postpone the creation of debug info for function args and locals
/// until after all Mir instructions have been generated. Only then we
/// will know saved_regs_stack_space which is necessary in order to
@ -646,6 +648,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
self.reused_operands = @TypeOf(self.reused_operands).initEmpty();
switch (air_tags[@intFromEnum(inst)]) {
// zig fmt: off
.add => try self.airBinOp(inst, .add),
@ -927,16 +930,13 @@ fn finishAirBookkeeping(self: *Self) void {
}
fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
var tomb_bits = self.liveness.getTombBits(inst);
for (operands) |op| {
const dies = @as(u1, @truncate(tomb_bits)) != 0;
tomb_bits >>= 1;
if (!dies) continue;
const op_index = op.toIndex() orelse continue;
self.processDeath(op_index);
const tomb_bits = self.liveness.getTombBits(inst);
for (0.., operands) |op_index, op| {
if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
if (self.reused_operands.isSet(op_index)) continue;
self.processDeath(op.toIndexAllowNone() orelse continue);
}
const is_used = @as(u1, @truncate(tomb_bits)) == 0;
if (is_used) {
if (tomb_bits & 1 << (Liveness.bpi - 1) == 0) {
log.debug("%{d} => {}", .{ inst, result });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
@ -3614,7 +3614,7 @@ fn reuseOperand(
}
// Prevent the operand deaths processing code from deallocating it.
self.liveness.clearOperandDeath(inst, op_index);
self.reused_operands.set(op_index);
// That makes us responsible for doing the rest of the stuff that processDeath would have done.
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];

View File

@ -72,6 +72,8 @@ end_di_column: u32,
/// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined,
/// We postpone the creation of debug info for function args and locals
/// until after all Mir instructions have been generated. Only then we
/// will know saved_regs_stack_space which is necessary in order to
@ -635,6 +637,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
self.reused_operands = @TypeOf(self.reused_operands).initEmpty();
switch (air_tags[@intFromEnum(inst)]) {
// zig fmt: off
.add, => try self.airBinOp(inst, .add),
@ -918,16 +921,13 @@ fn finishAirBookkeeping(self: *Self) void {
}
fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
var tomb_bits = self.liveness.getTombBits(inst);
for (operands) |op| {
const dies = @as(u1, @truncate(tomb_bits)) != 0;
tomb_bits >>= 1;
if (!dies) continue;
const op_index = op.toIndex() orelse continue;
self.processDeath(op_index);
const tomb_bits = self.liveness.getTombBits(inst);
for (0.., operands) |op_index, op| {
if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
if (self.reused_operands.isSet(op_index)) continue;
self.processDeath(op.toIndexAllowNone() orelse continue);
}
const is_used = @as(u1, @truncate(tomb_bits)) == 0;
if (is_used) {
if (tomb_bits & 1 << (Liveness.bpi - 1) == 0) {
log.debug("%{d} => {}", .{ inst, result });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
@ -2650,7 +2650,7 @@ fn reuseOperand(
}
// Prevent the operand deaths processing code from deallocating it.
self.liveness.clearOperandDeath(inst, op_index);
self.reused_operands.set(op_index);
// That makes us responsible for doing the rest of the stuff that processDeath would have done.
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];

View File

@ -82,6 +82,8 @@ scope_generation: u32,
/// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined,
/// Whenever there is a runtime branch, we push a Branch onto this stack,
/// and pop it off when the runtime branch joins. This provides an "overlay"
/// of the table of mappings from instructions to `MCValue` from within the branch.
@ -1443,8 +1445,11 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
verbose_tracking_log.debug("{}", .{func.fmtTracking()});
const old_air_bookkeeping = func.air_bookkeeping;
try func.ensureProcessDeathCapacity(Liveness.bpi);
func.reused_operands = @TypeOf(func.reused_operands).initEmpty();
try func.inst_tracking.ensureUnusedCapacity(func.gpa, 1);
const tag: Air.Inst.Tag = air_tags[@intFromEnum(inst)];
const tag = air_tags[@intFromEnum(inst)];
switch (tag) {
// zig fmt: off
.add,
@ -1783,11 +1788,10 @@ fn finishAir(
result: MCValue,
operands: [Liveness.bpi - 1]Air.Inst.Ref,
) !void {
var tomb_bits = func.liveness.getTombBits(inst);
for (operands) |op| {
const dies = @as(u1, @truncate(tomb_bits)) != 0;
tomb_bits >>= 1;
if (!dies) continue;
const tomb_bits = func.liveness.getTombBits(inst);
for (0.., operands) |op_index, op| {
if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
if (func.reused_operands.isSet(op_index)) continue;
try func.processDeath(op.toIndexAllowNone() orelse continue);
}
func.finishAirResult(inst, result);
@ -4424,7 +4428,7 @@ fn reuseOperandAdvanced(
}
// Prevent the operand deaths processing code from deallocating it.
func.liveness.clearOperandDeath(inst, op_index);
func.reused_operands.set(op_index);
const op_inst = operand.toIndex().?;
func.getResolvedInstValue(op_inst).reuse(func, maybe_tracked_inst, op_inst);

View File

@ -78,6 +78,8 @@ end_di_column: u32,
/// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined,
/// Whenever there is a runtime branch, we push a Branch onto this stack,
/// and pop it off when the runtime branch joins. This provides an "overlay"
/// of the table of mappings from instructions to `MCValue` from within the branch.
@ -493,6 +495,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
self.reused_operands = @TypeOf(self.reused_operands).initEmpty();
switch (air_tags[@intFromEnum(inst)]) {
// zig fmt: off
.ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
@ -3523,16 +3526,13 @@ fn finishAirBookkeeping(self: *Self) void {
}
fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
var tomb_bits = self.liveness.getTombBits(inst);
for (operands) |op| {
const dies = @as(u1, @truncate(tomb_bits)) != 0;
tomb_bits >>= 1;
if (!dies) continue;
const op_index = op.toIndex() orelse continue;
self.processDeath(op_index);
const tomb_bits = self.liveness.getTombBits(inst);
for (0.., operands) |op_index, op| {
if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
if (self.reused_operands.isSet(op_index)) continue;
self.processDeath(op.toIndexAllowNone() orelse continue);
}
const is_used = @as(u1, @truncate(tomb_bits)) == 0;
if (is_used) {
if (tomb_bits & 1 << (Liveness.bpi - 1) == 0) {
log.debug("%{d} => {}", .{ inst, result });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
@ -4568,7 +4568,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
}
// Prevent the operand deaths processing code from deallocating it.
self.liveness.clearOperandDeath(inst, op_index);
self.reused_operands.set(op_index);
// That makes us responsible for doing the rest of the stuff that processDeath would have done.
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];

File diff suppressed because it is too large Load Diff

View File

@ -250,9 +250,8 @@ pub fn classifySystemV(ty: Type, zcu: *Zcu, target: std.Target, ctx: Context) [8
return memory_class;
},
.optional => {
if (ty.isPtrLikeOptional(zcu)) {
result[0] = .integer;
return result;
if (ty.optionalReprIsPayload(zcu)) {
return classifySystemV(ty.optionalChild(zcu), zcu, target, ctx);
}
return memory_class;
},

View File

@ -547,7 +547,39 @@ pub const Memory = struct {
}
};
pub const Scale = enum(u2) { @"1", @"2", @"4", @"8" };
pub const Scale = enum(u2) {
@"1",
@"2",
@"4",
@"8",
pub fn fromFactor(factor: u4) Scale {
return switch (factor) {
else => unreachable,
1 => .@"1",
2 => .@"2",
4 => .@"4",
8 => .@"8",
};
}
pub fn toFactor(scale: Scale) u4 {
return switch (scale) {
.@"1" => 1,
.@"2" => 2,
.@"4" => 4,
.@"8" => 8,
};
}
pub fn fromLog2(log2: u2) Scale {
return @enumFromInt(log2);
}
pub fn toLog2(scale: Scale) u2 {
return @intFromEnum(scale);
}
};
};
pub const Immediate = union(enum) {

View File

@ -96,8 +96,8 @@ const Writer = struct {
fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const tag = w.air.instructions.items(.tag)[@intFromEnum(inst)];
try s.writeByteNTimes(' ', w.indent);
try s.print("%{d}{c}= {s}(", .{
@intFromEnum(inst),
try s.print("{}{c}= {s}(", .{
inst,
@as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '),
@tagName(tag),
});
@ -409,7 +409,7 @@ const Writer = struct {
try s.writeAll("}");
for (liveness_block.deaths) |operand| {
try s.print(" %{d}!", .{@intFromEnum(operand)});
try s.print(" {}!", .{operand});
}
}
@ -728,7 +728,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -739,7 +739,7 @@ const Writer = struct {
try s.writeAll("}");
for (liveness_condbr.then_deaths) |operand| {
try s.print(" %{d}!", .{@intFromEnum(operand)});
try s.print(" {}!", .{operand});
}
}
@ -765,7 +765,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -776,7 +776,7 @@ const Writer = struct {
try s.writeAll("}");
for (liveness_condbr.then_deaths) |operand| {
try s.print(" %{d}!", .{@intFromEnum(operand)});
try s.print(" {}!", .{operand});
}
}
@ -807,7 +807,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.then_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -827,7 +827,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -884,7 +884,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -910,7 +910,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -994,7 +994,7 @@ const Writer = struct {
dies: bool,
) @TypeOf(s).Error!void {
_ = w;
try s.print("%{d}", .{@intFromEnum(inst)});
try s.print("{}", .{inst});
if (dies) try s.writeByte('!');
}

View File

@ -383,7 +383,7 @@ def InstRef_SummaryProvider(value, _=None):
'InternPool.Index(%d)' % value.unsigned if value.unsigned < 0x80000000 else 'instructions[%d]' % (value.unsigned - 0x80000000))
def InstIndex_SummaryProvider(value, _=None):
return 'instructions[%d]' % value.unsigned
return 'instructions[%d]' % value.unsigned if value.unsigned < 0x80000000 else 'temps[%d]' % (value.unsigned - 0x80000000)
class zig_DeclIndex_SynthProvider:
def __init__(self, value, _=None): self.value = value