mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-05-29 12:27:25 +03:00
Merge pull request #17359 from jacobly0/x86_64
x86_64: improve C abi support
This commit is contained in:
@@ -156,7 +156,7 @@ pub var elf_aux_maybe: ?[*]std.elf.Auxv = null;
|
||||
|
||||
pub usingnamespace if (switch (builtin.zig_backend) {
|
||||
// Calling extern functions is not yet supported with these backends
|
||||
.stage2_x86_64, .stage2_aarch64, .stage2_arm, .stage2_riscv64, .stage2_sparc64 => false,
|
||||
.stage2_aarch64, .stage2_arm, .stage2_riscv64, .stage2_sparc64 => false,
|
||||
else => !builtin.link_libc,
|
||||
}) struct {
|
||||
/// See `std.elf` for the constants.
|
||||
|
||||
+10
-12
@@ -445,25 +445,23 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
|
||||
},
|
||||
.plan9 => |dbg_out| {
|
||||
if (delta_pc <= 0) return; // only do this when the pc changes
|
||||
// we have already checked the target in the linker to make sure it is compatable
|
||||
const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
|
||||
|
||||
// increasing the line number
|
||||
try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
|
||||
try link.File.Plan9.changeLine(&dbg_out.dbg_line, delta_line);
|
||||
// increasing the pc
|
||||
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - quant;
|
||||
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
|
||||
if (d_pc_p9 > 0) {
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one quanta
|
||||
try dbg_out.dbg_line.append(@as(u8, @intCast(@divExact(d_pc_p9, quant) + 128)) - quant);
|
||||
if (dbg_out.pcop_change_index.*) |pci|
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one pc quanta
|
||||
try dbg_out.dbg_line.append(@as(u8, @intCast(@divExact(d_pc_p9, dbg_out.pc_quanta) + 128)) - dbg_out.pc_quanta);
|
||||
if (dbg_out.pcop_change_index) |pci|
|
||||
dbg_out.dbg_line.items[pci] += 1;
|
||||
dbg_out.pcop_change_index.* = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
|
||||
dbg_out.pcop_change_index = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
|
||||
} else if (d_pc_p9 == 0) {
|
||||
// we don't need to do anything, because adding the quant does it for us
|
||||
// we don't need to do anything, because adding the pc quanta does it for us
|
||||
} else unreachable;
|
||||
if (dbg_out.start_line.* == null)
|
||||
dbg_out.start_line.* = self.prev_di_line;
|
||||
dbg_out.end_line.* = line;
|
||||
if (dbg_out.start_line == null)
|
||||
dbg_out.start_line = self.prev_di_line;
|
||||
dbg_out.end_line = line;
|
||||
// only do this if the pc changed
|
||||
self.prev_di_line = line;
|
||||
self.prev_di_column = column;
|
||||
|
||||
+10
-12
@@ -362,25 +362,23 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
|
||||
},
|
||||
.plan9 => |dbg_out| {
|
||||
if (delta_pc <= 0) return; // only do this when the pc changes
|
||||
// we have already checked the target in the linker to make sure it is compatable
|
||||
const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
|
||||
|
||||
// increasing the line number
|
||||
try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
|
||||
try link.File.Plan9.changeLine(&dbg_out.dbg_line, delta_line);
|
||||
// increasing the pc
|
||||
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - quant;
|
||||
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
|
||||
if (d_pc_p9 > 0) {
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one quanta
|
||||
try dbg_out.dbg_line.append(@as(u8, @intCast(@divExact(d_pc_p9, quant) + 128)) - quant);
|
||||
if (dbg_out.pcop_change_index.*) |pci|
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one pc quanta
|
||||
try dbg_out.dbg_line.append(@as(u8, @intCast(@divExact(d_pc_p9, dbg_out.pc_quanta) + 128)) - dbg_out.pc_quanta);
|
||||
if (dbg_out.pcop_change_index) |pci|
|
||||
dbg_out.dbg_line.items[pci] += 1;
|
||||
dbg_out.pcop_change_index.* = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
|
||||
dbg_out.pcop_change_index = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
|
||||
} else if (d_pc_p9 == 0) {
|
||||
// we don't need to do anything, because adding the quant does it for us
|
||||
// we don't need to do anything, because adding the pc quanta does it for us
|
||||
} else unreachable;
|
||||
if (dbg_out.start_line.* == null)
|
||||
dbg_out.start_line.* = self.prev_di_line;
|
||||
dbg_out.end_line.* = line;
|
||||
if (dbg_out.start_line == null)
|
||||
dbg_out.start_line = self.prev_di_line;
|
||||
dbg_out.end_line = line;
|
||||
// only do this if the pc changed
|
||||
self.prev_di_line = line;
|
||||
self.prev_di_column = column;
|
||||
|
||||
+10
-12
@@ -96,25 +96,23 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
|
||||
},
|
||||
.plan9 => |dbg_out| {
|
||||
if (delta_pc <= 0) return; // only do this when the pc changes
|
||||
// we have already checked the target in the linker to make sure it is compatable
|
||||
const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
|
||||
|
||||
// increasing the line number
|
||||
try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
|
||||
try link.File.Plan9.changeLine(&dbg_out.dbg_line, delta_line);
|
||||
// increasing the pc
|
||||
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - quant;
|
||||
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
|
||||
if (d_pc_p9 > 0) {
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one quanta
|
||||
try dbg_out.dbg_line.append(@as(u8, @intCast(@divExact(d_pc_p9, quant) + 128)) - quant);
|
||||
if (dbg_out.pcop_change_index.*) |pci|
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one pc quanta
|
||||
try dbg_out.dbg_line.append(@as(u8, @intCast(@divExact(d_pc_p9, dbg_out.pc_quanta) + 128)) - dbg_out.pc_quanta);
|
||||
if (dbg_out.pcop_change_index) |pci|
|
||||
dbg_out.dbg_line.items[pci] += 1;
|
||||
dbg_out.pcop_change_index.* = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
|
||||
dbg_out.pcop_change_index = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
|
||||
} else if (d_pc_p9 == 0) {
|
||||
// we don't need to do anything, because adding the quant does it for us
|
||||
// we don't need to do anything, because adding the pc quanta does it for us
|
||||
} else unreachable;
|
||||
if (dbg_out.start_line.* == null)
|
||||
dbg_out.start_line.* = self.prev_di_line;
|
||||
dbg_out.end_line.* = line;
|
||||
if (dbg_out.start_line == null)
|
||||
dbg_out.start_line = self.prev_di_line;
|
||||
dbg_out.end_line = line;
|
||||
// only do this if the pc changed
|
||||
self.prev_di_line = line;
|
||||
self.prev_di_column = column;
|
||||
|
||||
+621
-294
@@ -178,6 +178,8 @@ pub const MCValue = union(enum) {
|
||||
eflags: Condition,
|
||||
/// The value is in a register.
|
||||
register: Register,
|
||||
/// The value is split across two registers.
|
||||
register_pair: [2]Register,
|
||||
/// The value is a constant offset from the value in a register.
|
||||
register_offset: RegisterOffset,
|
||||
/// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.
|
||||
@@ -255,6 +257,16 @@ pub const MCValue = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
fn getRegs(mcv: *const MCValue) []const Register {
|
||||
return switch (mcv.*) {
|
||||
.register => |*reg| @as(*const [1]Register, reg),
|
||||
.register_pair => |*regs| regs,
|
||||
.register_offset, .indirect => |*ro| @as(*const [1]Register, &ro.reg),
|
||||
.register_overflow => |*ro| @as(*const [1]Register, &ro.reg),
|
||||
else => &.{},
|
||||
};
|
||||
}
|
||||
|
||||
fn getCondition(mcv: MCValue) ?Condition {
|
||||
return switch (mcv) {
|
||||
.eflags => |cc| cc,
|
||||
@@ -272,6 +284,7 @@ pub const MCValue = union(enum) {
|
||||
.immediate,
|
||||
.eflags,
|
||||
.register,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.lea_direct,
|
||||
@@ -300,6 +313,7 @@ pub const MCValue = union(enum) {
|
||||
.dead,
|
||||
.undef,
|
||||
.eflags,
|
||||
.register_pair,
|
||||
.register_overflow,
|
||||
.memory,
|
||||
.indirect,
|
||||
@@ -326,7 +340,11 @@ pub const MCValue = union(enum) {
|
||||
.unreach,
|
||||
.dead,
|
||||
.undef,
|
||||
.reserved_frame,
|
||||
.air_ref,
|
||||
=> unreachable, // not valid
|
||||
.eflags,
|
||||
.register_pair,
|
||||
.register_overflow,
|
||||
.memory,
|
||||
.indirect,
|
||||
@@ -337,9 +355,10 @@ pub const MCValue = union(enum) {
|
||||
.load_tlv,
|
||||
.lea_tlv,
|
||||
.load_frame,
|
||||
.reserved_frame,
|
||||
.air_ref,
|
||||
=> unreachable, // not offsettable
|
||||
=> switch (off) {
|
||||
0 => mcv,
|
||||
else => unreachable, // not offsettable
|
||||
},
|
||||
.immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) },
|
||||
.register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } },
|
||||
.register_offset => |reg_off| .{
|
||||
@@ -360,6 +379,7 @@ pub const MCValue = union(enum) {
|
||||
.immediate,
|
||||
.eflags,
|
||||
.register,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.load_direct,
|
||||
@@ -398,8 +418,11 @@ pub const MCValue = union(enum) {
|
||||
.immediate => |pl| try writer.print("0x{x}", .{pl}),
|
||||
.memory => |pl| try writer.print("[ds:0x{x}]", .{pl}),
|
||||
inline .eflags, .register => |pl| try writer.print("{s}", .{@tagName(pl)}),
|
||||
.register_pair => |pl| try writer.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }),
|
||||
.register_offset => |pl| try writer.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }),
|
||||
.register_overflow => |pl| try writer.print("{s}:{s}", .{ @tagName(pl.eflags), @tagName(pl.reg) }),
|
||||
.register_overflow => |pl| try writer.print("{s}:{s}", .{
|
||||
@tagName(pl.eflags), @tagName(pl.reg),
|
||||
}),
|
||||
.indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
|
||||
.load_direct => |pl| try writer.print("[direct:{d}]", .{pl}),
|
||||
.lea_direct => |pl| try writer.print("direct:{d}", .{pl}),
|
||||
@@ -443,6 +466,7 @@ const InstTracking = struct {
|
||||
=> unreachable,
|
||||
.eflags,
|
||||
.register,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.indirect,
|
||||
@@ -454,6 +478,10 @@ const InstTracking = struct {
|
||||
return self.short.getReg();
|
||||
}
|
||||
|
||||
fn getRegs(self: *const InstTracking) []const Register {
|
||||
return self.short.getRegs();
|
||||
}
|
||||
|
||||
fn getCondition(self: InstTracking) ?Condition {
|
||||
return self.short.getCondition();
|
||||
}
|
||||
@@ -494,6 +522,7 @@ const InstTracking = struct {
|
||||
.dead,
|
||||
.eflags,
|
||||
.register,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.indirect,
|
||||
@@ -536,6 +565,7 @@ const InstTracking = struct {
|
||||
.dead,
|
||||
.eflags,
|
||||
.register,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.indirect,
|
||||
@@ -777,7 +807,7 @@ pub fn generate(
|
||||
.lower = .{
|
||||
.allocator = bin_file.allocator,
|
||||
.mir = mir,
|
||||
.target = &bin_file.options.target,
|
||||
.cc = abi.resolveCallingConvention(fn_info.cc, function.target.*),
|
||||
.src_loc = src_loc,
|
||||
},
|
||||
.bin_file = bin_file,
|
||||
@@ -863,7 +893,7 @@ pub fn generateLazy(
|
||||
.lower = .{
|
||||
.allocator = bin_file.allocator,
|
||||
.mir = mir,
|
||||
.target = &bin_file.options.target,
|
||||
.cc = abi.resolveCallingConvention(.Unspecified, function.target.*),
|
||||
.src_loc = src_loc,
|
||||
},
|
||||
.bin_file = bin_file,
|
||||
@@ -950,6 +980,7 @@ fn formatWipMir(
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
const mod = data.self.bin_file.options.module.?;
|
||||
var lower = Lower{
|
||||
.allocator = data.self.gpa,
|
||||
.mir = .{
|
||||
@@ -957,7 +988,7 @@ fn formatWipMir(
|
||||
.extra = data.self.mir_extra.items,
|
||||
.frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(),
|
||||
},
|
||||
.target = data.self.target,
|
||||
.cc = mod.typeToFunc(data.self.fn_type).?.cc,
|
||||
.src_loc = data.self.src_loc,
|
||||
};
|
||||
for ((lower.lowerMir(data.inst) catch |err| switch (err) {
|
||||
@@ -1650,7 +1681,7 @@ fn gen(self: *Self) InnerError!void {
|
||||
try self.asmRegister(.{ ._, .pop }, .rbp);
|
||||
try self.asmOpOnly(.{ ._, .ret });
|
||||
|
||||
const frame_layout = try self.computeFrameLayout();
|
||||
const frame_layout = try self.computeFrameLayout(cc);
|
||||
const need_frame_align = frame_layout.stack_mask != math.maxInt(u32);
|
||||
const need_stack_adjust = frame_layout.stack_adjust > 0;
|
||||
const need_save_reg = frame_layout.save_reg_list.count() > 0;
|
||||
@@ -2028,7 +2059,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
while (it.next()) |index| {
|
||||
const tracked_inst = self.register_manager.registers[index];
|
||||
const tracking = self.getResolvedInstValue(tracked_inst);
|
||||
assert(RegisterManager.indexOfRegIntoTracked(tracking.getReg().?).? == index);
|
||||
for (tracking.getRegs()) |reg| {
|
||||
if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break;
|
||||
} else unreachable; // tracked register not in use
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2043,7 +2076,8 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
|
||||
const enum_ty = lazy_sym.ty;
|
||||
wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)});
|
||||
|
||||
const param_regs = abi.getCAbiIntParamRegs(self.target.*);
|
||||
const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*);
|
||||
const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
|
||||
const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*);
|
||||
defer for (param_locks) |lock| self.register_manager.unlockReg(lock);
|
||||
|
||||
@@ -2094,27 +2128,20 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
|
||||
}
|
||||
|
||||
fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void {
|
||||
const reg = value.getReg() orelse return;
|
||||
if (self.register_manager.isRegFree(reg)) {
|
||||
for (value.getRegs()) |reg| if (self.register_manager.isRegFree(reg))
|
||||
self.register_manager.getRegAssumeFree(reg, inst);
|
||||
}
|
||||
}
|
||||
|
||||
fn freeValue(self: *Self, value: MCValue) void {
|
||||
switch (value) {
|
||||
.register => |reg| {
|
||||
self.register_manager.freeReg(reg);
|
||||
},
|
||||
.register_offset => |reg_off| {
|
||||
self.register_manager.freeReg(reg_off.reg);
|
||||
},
|
||||
.register => |reg| self.register_manager.freeReg(reg),
|
||||
.register_pair => |regs| for (regs) |reg| self.register_manager.freeReg(reg),
|
||||
.register_offset => |reg_off| self.register_manager.freeReg(reg_off.reg),
|
||||
.register_overflow => |reg_ov| {
|
||||
self.register_manager.freeReg(reg_ov.reg);
|
||||
self.eflags_inst = null;
|
||||
},
|
||||
.eflags => {
|
||||
self.eflags_inst = null;
|
||||
},
|
||||
.eflags => self.eflags_inst = null,
|
||||
else => {}, // TODO process stack allocation death
|
||||
}
|
||||
}
|
||||
@@ -2183,7 +2210,7 @@ fn setFrameLoc(
|
||||
offset.* += self.frame_allocs.items(.abi_size)[frame_i];
|
||||
}
|
||||
|
||||
fn computeFrameLayout(self: *Self) !FrameLayout {
|
||||
fn computeFrameLayout(self: *Self, cc: std.builtin.CallingConvention) !FrameLayout {
|
||||
const frame_allocs_len = self.frame_allocs.len;
|
||||
try self.frame_locs.resize(self.gpa, frame_allocs_len);
|
||||
const stack_frame_order = try self.gpa.alloc(FrameIndex, frame_allocs_len - FrameIndex.named_count);
|
||||
@@ -2215,7 +2242,8 @@ fn computeFrameLayout(self: *Self) !FrameLayout {
|
||||
// Create list of registers to save in the prologue.
|
||||
// TODO handle register classes
|
||||
var save_reg_list = Mir.RegisterList{};
|
||||
const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*);
|
||||
const callee_preserved_regs =
|
||||
abi.getCalleePreservedRegs(abi.resolveCallingConvention(cc, self.target.*));
|
||||
for (callee_preserved_regs) |reg| {
|
||||
if (self.register_manager.isRegAllocated(reg)) {
|
||||
save_reg_list.push(callee_preserved_regs, reg);
|
||||
@@ -2449,7 +2477,9 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt
|
||||
|
||||
pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
|
||||
const tracking = self.inst_tracking.getPtr(inst) orelse return;
|
||||
assert(tracking.getReg().?.id() == reg.id());
|
||||
for (tracking.getRegs()) |tracked_reg| {
|
||||
if (tracked_reg.id() == reg.id()) break;
|
||||
} else unreachable; // spilled reg not tracked with spilled instruciton
|
||||
try tracking.spill(self, inst);
|
||||
tracking.trackSpill(self, inst);
|
||||
}
|
||||
@@ -2465,9 +2495,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {
|
||||
}
|
||||
|
||||
pub fn spillRegisters(self: *Self, registers: []const Register) !void {
|
||||
for (registers) |reg| {
|
||||
try self.register_manager.getReg(reg, null);
|
||||
}
|
||||
for (registers) |reg| try self.register_manager.getReg(reg, null);
|
||||
}
|
||||
|
||||
/// Copies a value to a register without tracking the register. The register is not considered
|
||||
@@ -5457,8 +5485,8 @@ fn reuseOperandAdvanced(
|
||||
return false;
|
||||
|
||||
switch (mcv) {
|
||||
.register => |reg| {
|
||||
// If it's in the registers table, need to associate the register with the
|
||||
.register, .register_pair => for (mcv.getRegs()) |reg| {
|
||||
// If it's in the registers table, need to associate the register(s) with the
|
||||
// new instruction.
|
||||
if (!self.register_manager.isRegFree(reg)) {
|
||||
if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
|
||||
@@ -5561,6 +5589,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
|
||||
.dead,
|
||||
.undef,
|
||||
.eflags,
|
||||
.register_pair,
|
||||
.register_overflow,
|
||||
.reserved_frame,
|
||||
=> unreachable, // not a valid pointer
|
||||
@@ -5585,7 +5614,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
|
||||
|
||||
try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } });
|
||||
},
|
||||
.air_ref => |ref| try self.load(dst_mcv, ptr_ty, try self.resolveInst(ref)),
|
||||
.air_ref => |ptr_ref| try self.load(dst_mcv, ptr_ty, try self.resolveInst(ptr_ref)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5707,6 +5736,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
|
||||
.dead,
|
||||
.undef,
|
||||
.eflags,
|
||||
.register_pair,
|
||||
.register_overflow,
|
||||
.reserved_frame,
|
||||
=> unreachable, // not a valid pointer
|
||||
@@ -5731,7 +5761,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
|
||||
|
||||
try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv);
|
||||
},
|
||||
.air_ref => |ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ref)),
|
||||
.air_ref => |ptr_ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ptr_ref)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6077,6 +6107,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC
|
||||
.air_ref,
|
||||
=> unreachable, // unmodifiable destination
|
||||
.register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),
|
||||
.register_pair => unreachable, // unimplemented
|
||||
.memory, .load_got, .load_direct, .load_tlv => {
|
||||
const addr_reg = try self.register_manager.allocReg(null, gp);
|
||||
const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
|
||||
@@ -6764,7 +6795,7 @@ fn genBinOp(
|
||||
=> {
|
||||
const resolved_src_mcv = switch (src_mcv) {
|
||||
else => src_mcv,
|
||||
.air_ref => |ref| try self.resolveInst(ref),
|
||||
.air_ref => |src_ref| try self.resolveInst(src_ref),
|
||||
};
|
||||
const mat_src_mcv: MCValue = if (switch (resolved_src_mcv) {
|
||||
.immediate,
|
||||
@@ -6780,6 +6811,9 @@ fn genBinOp(
|
||||
=> true,
|
||||
.memory => |addr| math.cast(i32, @as(i64, @bitCast(addr))) == null,
|
||||
else => false,
|
||||
.register_pair,
|
||||
.register_overflow,
|
||||
=> unreachable,
|
||||
})
|
||||
.{ .register = try self.copyToTmpRegister(rhs_ty, resolved_src_mcv) }
|
||||
else
|
||||
@@ -6820,6 +6854,7 @@ fn genBinOp(
|
||||
.undef,
|
||||
.immediate,
|
||||
.eflags,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.load_direct,
|
||||
@@ -7748,121 +7783,154 @@ fn genBinOpMir(
|
||||
.reserved_frame,
|
||||
.air_ref,
|
||||
=> unreachable, // unmodifiable destination
|
||||
.register, .register_offset => {
|
||||
assert(dst_mcv.isRegister());
|
||||
const dst_reg = dst_mcv.getReg().?;
|
||||
const dst_alias = registerAlias(dst_reg, abi_size);
|
||||
switch (src_mcv) {
|
||||
.none,
|
||||
.unreach,
|
||||
.dead,
|
||||
.undef,
|
||||
.register_overflow,
|
||||
.reserved_frame,
|
||||
=> unreachable,
|
||||
.register => |src_reg| try self.asmRegisterRegister(
|
||||
mir_tag,
|
||||
dst_alias,
|
||||
registerAlias(src_reg, abi_size),
|
||||
),
|
||||
.immediate => |imm| switch (self.regBitSize(ty)) {
|
||||
8 => try self.asmRegisterImmediate(
|
||||
mir_tag,
|
||||
dst_alias,
|
||||
if (math.cast(i8, @as(i64, @bitCast(imm)))) |small|
|
||||
Immediate.s(small)
|
||||
else
|
||||
Immediate.u(@as(u8, @intCast(imm))),
|
||||
),
|
||||
16 => try self.asmRegisterImmediate(
|
||||
mir_tag,
|
||||
dst_alias,
|
||||
if (math.cast(i16, @as(i64, @bitCast(imm)))) |small|
|
||||
Immediate.s(small)
|
||||
else
|
||||
Immediate.u(@as(u16, @intCast(imm))),
|
||||
),
|
||||
32 => try self.asmRegisterImmediate(
|
||||
mir_tag,
|
||||
dst_alias,
|
||||
if (math.cast(i32, @as(i64, @bitCast(imm)))) |small|
|
||||
Immediate.s(small)
|
||||
else
|
||||
Immediate.u(@as(u32, @intCast(imm))),
|
||||
),
|
||||
64 => if (math.cast(i32, @as(i64, @bitCast(imm)))) |small|
|
||||
try self.asmRegisterImmediate(mir_tag, dst_alias, Immediate.s(small))
|
||||
else
|
||||
try self.asmRegisterRegister(mir_tag, dst_alias, registerAlias(
|
||||
try self.copyToTmpRegister(ty, src_mcv),
|
||||
abi_size,
|
||||
)),
|
||||
.register, .register_pair, .register_offset => {
|
||||
switch (dst_mcv) {
|
||||
.register, .register_pair => {},
|
||||
.register_offset => |ro| assert(ro.off == 0),
|
||||
else => unreachable,
|
||||
}
|
||||
for (dst_mcv.getRegs(), 0..) |dst_reg, dst_reg_i| {
|
||||
const dst_reg_lock = self.register_manager.lockReg(dst_reg);
|
||||
defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
|
||||
const mir_limb_tag: Mir.Inst.FixedTag = switch (dst_reg_i) {
|
||||
0 => mir_tag,
|
||||
1 => switch (mir_tag[1]) {
|
||||
.add => .{ ._, .adc },
|
||||
.sub, .cmp => .{ ._, .sbb },
|
||||
.@"or", .@"and", .xor => mir_tag,
|
||||
else => return self.fail("TODO genBinOpMir implement large ABI for {s}", .{
|
||||
@tagName(mir_tag[1]),
|
||||
}),
|
||||
},
|
||||
else => unreachable,
|
||||
},
|
||||
.eflags,
|
||||
.register_offset,
|
||||
.memory,
|
||||
.indirect,
|
||||
.load_direct,
|
||||
.lea_direct,
|
||||
.load_got,
|
||||
.lea_got,
|
||||
.load_tlv,
|
||||
.lea_tlv,
|
||||
.load_frame,
|
||||
.lea_frame,
|
||||
=> {
|
||||
blk: {
|
||||
return self.asmRegisterMemory(
|
||||
mir_tag,
|
||||
registerAlias(dst_reg, abi_size),
|
||||
Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) {
|
||||
.memory => |addr| .{
|
||||
.base = .{ .reg = .ds },
|
||||
.disp = math.cast(i32, addr) orelse break :blk,
|
||||
},
|
||||
.indirect => |reg_off| .{
|
||||
.base = .{ .reg = reg_off.reg },
|
||||
.disp = reg_off.off,
|
||||
},
|
||||
.load_frame => |frame_addr| .{
|
||||
.base = .{ .frame = frame_addr.index },
|
||||
.disp = frame_addr.off,
|
||||
},
|
||||
else => break :blk,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
const off: u4 = @intCast(dst_reg_i * 8);
|
||||
const limb_abi_size = @min(abi_size - off, 8);
|
||||
const dst_alias = registerAlias(dst_reg, limb_abi_size);
|
||||
switch (src_mcv) {
|
||||
.none,
|
||||
.unreach,
|
||||
.dead,
|
||||
.undef,
|
||||
.register_overflow,
|
||||
.reserved_frame,
|
||||
=> unreachable,
|
||||
.register, .register_pair => try self.asmRegisterRegister(
|
||||
mir_limb_tag,
|
||||
dst_alias,
|
||||
registerAlias(src_mcv.getRegs()[dst_reg_i], limb_abi_size),
|
||||
),
|
||||
.immediate => |imm| {
|
||||
assert(off == 0);
|
||||
switch (self.regBitSize(ty)) {
|
||||
8 => try self.asmRegisterImmediate(
|
||||
mir_limb_tag,
|
||||
dst_alias,
|
||||
if (math.cast(i8, @as(i64, @bitCast(imm)))) |small|
|
||||
Immediate.s(small)
|
||||
else
|
||||
Immediate.u(@as(u8, @intCast(imm))),
|
||||
),
|
||||
16 => try self.asmRegisterImmediate(
|
||||
mir_limb_tag,
|
||||
dst_alias,
|
||||
if (math.cast(i16, @as(i64, @bitCast(imm)))) |small|
|
||||
Immediate.s(small)
|
||||
else
|
||||
Immediate.u(@as(u16, @intCast(imm))),
|
||||
),
|
||||
32 => try self.asmRegisterImmediate(
|
||||
mir_limb_tag,
|
||||
dst_alias,
|
||||
if (math.cast(i32, @as(i64, @bitCast(imm)))) |small|
|
||||
Immediate.s(small)
|
||||
else
|
||||
Immediate.u(@as(u32, @intCast(imm))),
|
||||
),
|
||||
64 => if (math.cast(i32, @as(i64, @bitCast(imm)))) |small|
|
||||
try self.asmRegisterImmediate(mir_limb_tag, dst_alias, Immediate.s(small))
|
||||
else
|
||||
try self.asmRegisterRegister(mir_limb_tag, dst_alias, registerAlias(
|
||||
try self.copyToTmpRegister(ty, src_mcv),
|
||||
limb_abi_size,
|
||||
)),
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.eflags,
|
||||
.register_offset,
|
||||
.memory,
|
||||
.indirect,
|
||||
.load_direct,
|
||||
.lea_direct,
|
||||
.load_got,
|
||||
.lea_got,
|
||||
.load_tlv,
|
||||
.lea_tlv,
|
||||
.load_frame,
|
||||
.lea_frame,
|
||||
=> {
|
||||
blk: {
|
||||
return self.asmRegisterMemory(
|
||||
mir_limb_tag,
|
||||
dst_alias,
|
||||
Memory.sib(Memory.PtrSize.fromSize(limb_abi_size), switch (src_mcv) {
|
||||
.memory => |addr| .{
|
||||
.base = .{ .reg = .ds },
|
||||
.disp = math.cast(i32, addr + off) orelse break :blk,
|
||||
},
|
||||
.indirect => |reg_off| .{
|
||||
.base = .{ .reg = reg_off.reg },
|
||||
.disp = reg_off.off + off,
|
||||
},
|
||||
.load_frame => |frame_addr| .{
|
||||
.base = .{ .frame = frame_addr.index },
|
||||
.disp = frame_addr.off + off,
|
||||
},
|
||||
else => break :blk,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const dst_reg_lock = self.register_manager.lockReg(dst_reg);
|
||||
defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
|
||||
switch (src_mcv) {
|
||||
.eflags,
|
||||
.register_offset,
|
||||
.lea_direct,
|
||||
.lea_got,
|
||||
.lea_tlv,
|
||||
.lea_frame,
|
||||
=> {
|
||||
const reg = try self.copyToTmpRegister(ty, src_mcv);
|
||||
return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg });
|
||||
},
|
||||
.memory,
|
||||
.load_direct,
|
||||
.load_got,
|
||||
.load_tlv,
|
||||
=> {
|
||||
const ptr_ty = try mod.singleConstPtrType(ty);
|
||||
const addr_reg = try self.copyToTmpRegister(ptr_ty, src_mcv.address());
|
||||
return self.genBinOpMir(mir_tag, ty, dst_mcv, .{
|
||||
.indirect = .{ .reg = addr_reg },
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.air_ref => |ref| try self.genBinOpMir(mir_tag, ty, dst_mcv, try self.resolveInst(ref)),
|
||||
switch (src_mcv) {
|
||||
.eflags,
|
||||
.register_offset,
|
||||
.lea_direct,
|
||||
.lea_got,
|
||||
.lea_tlv,
|
||||
.lea_frame,
|
||||
=> {
|
||||
assert(off == 0);
|
||||
const reg = try self.copyToTmpRegister(ty, src_mcv);
|
||||
return self.genBinOpMir(
|
||||
mir_limb_tag,
|
||||
ty,
|
||||
dst_mcv,
|
||||
.{ .register = reg },
|
||||
);
|
||||
},
|
||||
.memory,
|
||||
.load_direct,
|
||||
.load_got,
|
||||
.load_tlv,
|
||||
=> {
|
||||
const ptr_ty = try mod.singleConstPtrType(ty);
|
||||
const addr_reg = try self.copyToTmpRegister(ptr_ty, src_mcv.address());
|
||||
return self.genBinOpMir(mir_limb_tag, ty, dst_mcv, .{
|
||||
.indirect = .{ .reg = addr_reg, .off = off },
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.air_ref => |src_ref| return self.genBinOpMir(
|
||||
mir_tag,
|
||||
ty,
|
||||
dst_mcv,
|
||||
try self.resolveInst(src_ref),
|
||||
),
|
||||
}
|
||||
}
|
||||
},
|
||||
.memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => {
|
||||
@@ -7877,10 +7945,7 @@ fn genBinOpMir(
|
||||
errdefer self.register_manager.unlockReg(dst_addr_lock);
|
||||
|
||||
try self.genSetReg(dst_addr_reg, Type.usize, dst_mcv.address());
|
||||
break :dst .{
|
||||
.addr_reg = dst_addr_reg,
|
||||
.addr_lock = dst_addr_lock,
|
||||
};
|
||||
break :dst .{ .addr_reg = dst_addr_reg, .addr_lock = dst_addr_lock };
|
||||
},
|
||||
.load_frame => null,
|
||||
};
|
||||
@@ -7888,7 +7953,7 @@ fn genBinOpMir(
|
||||
|
||||
const resolved_src_mcv = switch (src_mcv) {
|
||||
else => src_mcv,
|
||||
.air_ref => |ref| try self.resolveInst(ref),
|
||||
.air_ref => |src_ref| try self.resolveInst(src_ref),
|
||||
};
|
||||
const src_info: OpInfo = switch (resolved_src_mcv) {
|
||||
.none,
|
||||
@@ -7900,9 +7965,10 @@ fn genBinOpMir(
|
||||
.air_ref,
|
||||
=> unreachable,
|
||||
.immediate,
|
||||
.register,
|
||||
.register_offset,
|
||||
.eflags,
|
||||
.register,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.indirect,
|
||||
.lea_direct,
|
||||
.lea_got,
|
||||
@@ -7924,10 +7990,7 @@ fn genBinOpMir(
|
||||
errdefer self.register_manager.unlockReg(src_addr_lock);
|
||||
|
||||
try self.genSetReg(src_addr_reg, Type.usize, resolved_src_mcv.address());
|
||||
break :src .{
|
||||
.addr_reg = src_addr_reg,
|
||||
.addr_lock = src_addr_lock,
|
||||
};
|
||||
break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock };
|
||||
},
|
||||
};
|
||||
defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock);
|
||||
@@ -7938,9 +8001,13 @@ fn genBinOpMir(
|
||||
.signed => Type.usize,
|
||||
.unsigned => Type.isize,
|
||||
};
|
||||
var limb_i: usize = 0;
|
||||
var off: i32 = 0;
|
||||
while (off < abi_size) : (off += 8) {
|
||||
const mir_limb_tag: Mir.Inst.FixedTag = switch (off) {
|
||||
while (off < abi_size) : ({
|
||||
limb_i += 1;
|
||||
off += 8;
|
||||
}) {
|
||||
const mir_limb_tag: Mir.Inst.FixedTag = switch (limb_i) {
|
||||
0 => mir_tag,
|
||||
else => switch (mir_tag[1]) {
|
||||
.add => .{ ._, .adc },
|
||||
@@ -7979,16 +8046,8 @@ fn genBinOpMir(
|
||||
.reserved_frame,
|
||||
.air_ref,
|
||||
=> unreachable,
|
||||
.register => |src_reg| switch (off) {
|
||||
0 => try self.asmMemoryRegister(
|
||||
mir_limb_tag,
|
||||
dst_limb_mem,
|
||||
registerAlias(src_reg, limb_abi_size),
|
||||
),
|
||||
else => unreachable,
|
||||
},
|
||||
.immediate => |src_imm| {
|
||||
const imm: u64 = switch (off) {
|
||||
const imm: u64 = switch (limb_i) {
|
||||
0 => src_imm,
|
||||
else => switch (ty_signedness) {
|
||||
.signed => @bitCast(@as(i64, @bitCast(src_imm)) >> 63),
|
||||
@@ -8038,6 +8097,8 @@ fn genBinOpMir(
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.register,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.eflags,
|
||||
.memory,
|
||||
@@ -8051,16 +8112,19 @@ fn genBinOpMir(
|
||||
.load_frame,
|
||||
.lea_frame,
|
||||
=> {
|
||||
const src_limb_reg = try self.copyToTmpRegister(limb_ty, if (src_info) |info| .{
|
||||
const src_limb_mcv: MCValue = if (src_info) |info| .{
|
||||
.indirect = .{ .reg = info.addr_reg, .off = off },
|
||||
} else switch (resolved_src_mcv) {
|
||||
.register, .register_pair => .{
|
||||
.register = resolved_src_mcv.getRegs()[limb_i],
|
||||
},
|
||||
.eflags,
|
||||
.register_offset,
|
||||
.lea_direct,
|
||||
.lea_got,
|
||||
.lea_tlv,
|
||||
.lea_frame,
|
||||
=> switch (off) {
|
||||
=> switch (limb_i) {
|
||||
0 => resolved_src_mcv,
|
||||
else => .{ .immediate = 0 },
|
||||
},
|
||||
@@ -8074,7 +8138,11 @@ fn genBinOpMir(
|
||||
.off = frame_addr.off + off,
|
||||
} },
|
||||
else => unreachable,
|
||||
});
|
||||
};
|
||||
const src_limb_reg = if (src_limb_mcv.isRegister())
|
||||
src_limb_mcv.getReg().?
|
||||
else
|
||||
try self.copyToTmpRegister(limb_ty, src_limb_mcv);
|
||||
try self.asmMemoryRegister(
|
||||
mir_limb_tag,
|
||||
dst_limb_mem,
|
||||
@@ -8098,8 +8166,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
|
||||
.dead,
|
||||
.undef,
|
||||
.immediate,
|
||||
.register_offset,
|
||||
.eflags,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.lea_direct,
|
||||
.lea_got,
|
||||
@@ -8115,13 +8183,14 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
|
||||
|
||||
const resolved_src_mcv = switch (src_mcv) {
|
||||
else => src_mcv,
|
||||
.air_ref => |ref| try self.resolveInst(ref),
|
||||
.air_ref => |src_ref| try self.resolveInst(src_ref),
|
||||
};
|
||||
switch (resolved_src_mcv) {
|
||||
.none,
|
||||
.unreach,
|
||||
.dead,
|
||||
.undef,
|
||||
.register_pair,
|
||||
.register_overflow,
|
||||
.reserved_frame,
|
||||
.air_ref,
|
||||
@@ -8187,6 +8256,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
|
||||
),
|
||||
}
|
||||
},
|
||||
.register_pair => unreachable, // unimplemented
|
||||
.memory, .indirect, .load_direct, .load_got, .load_tlv, .load_frame => {
|
||||
const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
|
||||
const tmp_mcv = MCValue{ .register = tmp_reg };
|
||||
@@ -8209,8 +8279,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
|
||||
const dst_mcv = self.args[arg_index];
|
||||
switch (dst_mcv) {
|
||||
.register => |reg| self.register_manager.getRegAssumeFree(reg, inst),
|
||||
.load_frame => {},
|
||||
.register, .register_pair, .load_frame => for (dst_mcv.getRegs()) |reg|
|
||||
self.register_manager.getRegAssumeFree(reg, inst),
|
||||
else => return self.fail("TODO implement arg for {}", .{dst_mcv}),
|
||||
}
|
||||
|
||||
@@ -8230,6 +8300,9 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
|
||||
.dwarf => |dw| {
|
||||
const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) {
|
||||
.register => |reg| .{ .register = reg.dwarfNum() },
|
||||
.register_pair => |regs| .{ .register_pair = .{
|
||||
regs[0].dwarfNum(), regs[1].dwarfNum(),
|
||||
} },
|
||||
// TODO use a frame index
|
||||
.load_frame => return,
|
||||
//.stack_offset => |off| .{
|
||||
@@ -8390,7 +8463,10 @@ fn genCall(self: *Self, info: union(enum) {
|
||||
};
|
||||
const fn_info = mod.typeToFunc(fn_ty).?;
|
||||
|
||||
const ExpectedContents = [16]Type;
|
||||
const ExpectedContents = extern struct {
|
||||
var_args: [16][@sizeOf(Type)]u8 align(@alignOf(Type)),
|
||||
arg_regs: [16][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)),
|
||||
};
|
||||
var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) =
|
||||
std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
|
||||
const allocator = stack.get();
|
||||
@@ -8399,8 +8475,12 @@ fn genCall(self: *Self, info: union(enum) {
|
||||
defer allocator.free(var_args);
|
||||
for (var_args, arg_types[fn_info.param_types.len..]) |*var_arg, arg_ty| var_arg.* = arg_ty;
|
||||
|
||||
var call_info =
|
||||
try self.resolveCallingConventionValues(fn_info, var_args, .call_frame);
|
||||
var arg_locks = std.ArrayList(?RegisterLock).init(allocator);
|
||||
defer arg_locks.deinit();
|
||||
try arg_locks.ensureTotalCapacity(16);
|
||||
defer for (arg_locks.items) |arg_lock| if (arg_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
|
||||
var call_info = try self.resolveCallingConventionValues(fn_info, var_args, .call_frame);
|
||||
defer call_info.deinit(self);
|
||||
|
||||
// We need a properly aligned and sized call frame to be able to call this function.
|
||||
@@ -8419,7 +8499,9 @@ fn genCall(self: *Self, info: union(enum) {
|
||||
}
|
||||
|
||||
try self.spillEflagsIfOccupied();
|
||||
try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*));
|
||||
try self.spillRegisters(abi.getCallerPreservedRegs(
|
||||
abi.resolveCallingConvention(fn_info.cc, self.target.*),
|
||||
));
|
||||
|
||||
// set stack arguments first because this can clobber registers
|
||||
// also clobber spill arguments as we go
|
||||
@@ -8430,7 +8512,14 @@ fn genCall(self: *Self, info: union(enum) {
|
||||
}
|
||||
for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) {
|
||||
.none => {},
|
||||
.register => |reg| try self.spillRegisters(&.{reg}),
|
||||
.register => |reg| {
|
||||
try self.spillRegisters(&.{reg});
|
||||
try arg_locks.append(self.register_manager.lockReg(reg));
|
||||
},
|
||||
.register_pair => |regs| {
|
||||
try self.spillRegisters(®s);
|
||||
try arg_locks.appendSlice(&self.register_manager.lockRegs(2, regs));
|
||||
},
|
||||
.load_frame => try self.genCopy(arg_ty, dst_arg, src_arg),
|
||||
else => unreachable,
|
||||
};
|
||||
@@ -8451,13 +8540,11 @@ fn genCall(self: *Self, info: union(enum) {
|
||||
};
|
||||
defer if (ret_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
|
||||
for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| {
|
||||
switch (dst_arg) {
|
||||
.none, .load_frame => {},
|
||||
.register => try self.genCopy(arg_ty, dst_arg, src_arg),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) {
|
||||
.none, .load_frame => {},
|
||||
.register, .register_pair => try self.genCopy(arg_ty, dst_arg, src_arg),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
// Due to incremental compilation, how function calls are generated depends
|
||||
// on linking.
|
||||
@@ -8527,7 +8614,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ret_ty = self.fn_type.fnReturnType(mod);
|
||||
switch (self.ret_mcv.short) {
|
||||
.none => {},
|
||||
.register => try self.genCopy(ret_ty, self.ret_mcv.short, operand),
|
||||
.register, .register_pair => try self.genCopy(ret_ty, self.ret_mcv.short, operand),
|
||||
.indirect => |reg_off| {
|
||||
try self.register_manager.getReg(reg_off.reg, null);
|
||||
const lock = self.register_manager.lockRegAssumeUnused(reg_off.reg);
|
||||
@@ -8612,6 +8699,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
|
||||
const lhs_mcv = try self.resolveInst(bin_op.lhs);
|
||||
const lhs_lock = switch (lhs_mcv) {
|
||||
.register => |reg| self.register_manager.lockRegAssumeUnused(reg),
|
||||
.register_offset => |ro| self.register_manager.lockRegAssumeUnused(ro.reg),
|
||||
else => null,
|
||||
};
|
||||
defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
@@ -8619,6 +8707,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
|
||||
const rhs_mcv = try self.resolveInst(bin_op.rhs);
|
||||
const rhs_lock = switch (rhs_mcv) {
|
||||
.register => |reg| self.register_manager.lockReg(reg),
|
||||
.register_offset => |ro| self.register_manager.lockReg(ro.reg),
|
||||
else => null,
|
||||
};
|
||||
defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
@@ -8665,33 +8754,122 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
|
||||
.lt, .gte => {},
|
||||
.lte, .gt => unreachable,
|
||||
.eq, .neq => {
|
||||
const dst_addr_mcv: MCValue = switch (dst_mcv) {
|
||||
.memory, .indirect, .load_frame => dst_mcv.address(),
|
||||
else => .{ .register = try self.copyToTmpRegister(
|
||||
Type.usize,
|
||||
dst_mcv.address(),
|
||||
) },
|
||||
};
|
||||
const dst_addr_lock = if (dst_addr_mcv.getReg()) |reg|
|
||||
self.register_manager.lockReg(reg)
|
||||
else
|
||||
null;
|
||||
defer if (dst_addr_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock };
|
||||
|
||||
const src_addr_mcv: MCValue = switch (src_mcv) {
|
||||
.memory, .indirect, .load_frame => src_mcv.address(),
|
||||
else => .{ .register = try self.copyToTmpRegister(
|
||||
Type.usize,
|
||||
src_mcv.address(),
|
||||
) },
|
||||
const resolved_dst_mcv = switch (dst_mcv) {
|
||||
else => dst_mcv,
|
||||
.air_ref => |dst_ref| try self.resolveInst(dst_ref),
|
||||
};
|
||||
const src_addr_lock = if (src_addr_mcv.getReg()) |reg|
|
||||
self.register_manager.lockReg(reg)
|
||||
else
|
||||
null;
|
||||
defer if (src_addr_lock) |lock| self.register_manager.unlockReg(lock);
|
||||
const dst_info: OpInfo = switch (resolved_dst_mcv) {
|
||||
.none,
|
||||
.unreach,
|
||||
.dead,
|
||||
.undef,
|
||||
.immediate,
|
||||
.eflags,
|
||||
.register,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.indirect,
|
||||
.lea_direct,
|
||||
.lea_got,
|
||||
.lea_tlv,
|
||||
.lea_frame,
|
||||
.reserved_frame,
|
||||
.air_ref,
|
||||
=> unreachable,
|
||||
.register_pair, .load_frame => null,
|
||||
.memory, .load_got, .load_direct, .load_tlv => dst: {
|
||||
switch (resolved_dst_mcv) {
|
||||
.memory => |addr| if (math.cast(
|
||||
i32,
|
||||
@as(i64, @bitCast(addr)),
|
||||
) != null and math.cast(
|
||||
i32,
|
||||
@as(i64, @bitCast(addr)) + abi_size - 8,
|
||||
) != null) break :dst null,
|
||||
.load_got, .load_direct, .load_tlv => {},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
|
||||
const dst_addr_reg =
|
||||
(try self.register_manager.allocReg(null, gp)).to64();
|
||||
const dst_addr_lock =
|
||||
self.register_manager.lockRegAssumeUnused(dst_addr_reg);
|
||||
errdefer self.register_manager.unlockReg(dst_addr_lock);
|
||||
|
||||
try self.genSetReg(
|
||||
dst_addr_reg,
|
||||
Type.usize,
|
||||
resolved_dst_mcv.address(),
|
||||
);
|
||||
break :dst .{
|
||||
.addr_reg = dst_addr_reg,
|
||||
.addr_lock = dst_addr_lock,
|
||||
};
|
||||
},
|
||||
};
|
||||
defer if (dst_info) |info|
|
||||
self.register_manager.unlockReg(info.addr_lock);
|
||||
|
||||
const resolved_src_mcv = switch (src_mcv) {
|
||||
else => src_mcv,
|
||||
.air_ref => |src_ref| try self.resolveInst(src_ref),
|
||||
};
|
||||
const src_info: OpInfo = switch (resolved_src_mcv) {
|
||||
.none,
|
||||
.unreach,
|
||||
.dead,
|
||||
.undef,
|
||||
.immediate,
|
||||
.eflags,
|
||||
.register,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.indirect,
|
||||
.lea_direct,
|
||||
.lea_got,
|
||||
.lea_tlv,
|
||||
.lea_frame,
|
||||
.reserved_frame,
|
||||
.air_ref,
|
||||
=> unreachable,
|
||||
.register_pair, .load_frame => null,
|
||||
.memory, .load_got, .load_direct, .load_tlv => src: {
|
||||
switch (resolved_src_mcv) {
|
||||
.memory => |addr| if (math.cast(
|
||||
i32,
|
||||
@as(i64, @bitCast(addr)),
|
||||
) != null and math.cast(
|
||||
i32,
|
||||
@as(i64, @bitCast(addr)) + abi_size - 8,
|
||||
) != null) break :src null,
|
||||
.load_got, .load_direct, .load_tlv => {},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const src_addr_reg =
|
||||
(try self.register_manager.allocReg(null, gp)).to64();
|
||||
const src_addr_lock =
|
||||
self.register_manager.lockRegAssumeUnused(src_addr_reg);
|
||||
errdefer self.register_manager.unlockReg(src_addr_lock);
|
||||
|
||||
try self.genSetReg(
|
||||
src_addr_reg,
|
||||
Type.usize,
|
||||
resolved_src_mcv.address(),
|
||||
);
|
||||
break :src .{
|
||||
.addr_reg = src_addr_reg,
|
||||
.addr_lock = src_addr_lock,
|
||||
};
|
||||
},
|
||||
};
|
||||
defer if (src_info) |info|
|
||||
self.register_manager.unlockReg(info.addr_lock);
|
||||
|
||||
const regs =
|
||||
try self.register_manager.allocRegs(2, .{ null, null }, gp);
|
||||
const acc_reg = regs[0].to64();
|
||||
const locks = self.register_manager.lockRegsAssumeUnused(2, regs);
|
||||
defer for (locks) |lock| self.register_manager.unlockReg(lock);
|
||||
@@ -8699,18 +8877,52 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
|
||||
const limbs_len = std.math.divCeil(u16, abi_size, 8) catch unreachable;
|
||||
var limb_i: u16 = 0;
|
||||
while (limb_i < limbs_len) : (limb_i += 1) {
|
||||
const off = limb_i * 8;
|
||||
const tmp_reg = regs[@min(limb_i, 1)].to64();
|
||||
try self.genSetReg(
|
||||
tmp_reg,
|
||||
Type.usize,
|
||||
dst_addr_mcv.offset(limb_i * 8).deref(),
|
||||
);
|
||||
|
||||
try self.genSetReg(tmp_reg, Type.usize, if (dst_info) |info| .{
|
||||
.indirect = .{ .reg = info.addr_reg, .off = off },
|
||||
} else switch (resolved_dst_mcv) {
|
||||
.register_pair => |dst_regs| .{ .register = dst_regs[limb_i] },
|
||||
.memory => |dst_addr| .{
|
||||
.memory = @bitCast(@as(i64, @bitCast(dst_addr)) + off),
|
||||
},
|
||||
.indirect => |reg_off| .{ .indirect = .{
|
||||
.reg = reg_off.reg,
|
||||
.off = reg_off.off + off,
|
||||
} },
|
||||
.load_frame => |frame_addr| .{ .load_frame = .{
|
||||
.index = frame_addr.index,
|
||||
.off = frame_addr.off + off,
|
||||
} },
|
||||
else => unreachable,
|
||||
});
|
||||
|
||||
try self.genBinOpMir(
|
||||
.{ ._, .xor },
|
||||
Type.usize,
|
||||
.{ .register = tmp_reg },
|
||||
src_addr_mcv.offset(limb_i * 8).deref(),
|
||||
if (src_info) |info| .{
|
||||
.indirect = .{ .reg = info.addr_reg, .off = off },
|
||||
} else switch (resolved_src_mcv) {
|
||||
.register_pair => |src_regs| .{
|
||||
.register = src_regs[limb_i],
|
||||
},
|
||||
.memory => |src_addr| .{
|
||||
.memory = @bitCast(@as(i64, @bitCast(src_addr)) + off),
|
||||
},
|
||||
.indirect => |reg_off| .{ .indirect = .{
|
||||
.reg = reg_off.reg,
|
||||
.off = reg_off.off + off,
|
||||
} },
|
||||
.load_frame => |frame_addr| .{ .load_frame = .{
|
||||
.index = frame_addr.index,
|
||||
.off = frame_addr.off + off,
|
||||
} },
|
||||
else => unreachable,
|
||||
},
|
||||
);
|
||||
|
||||
if (limb_i > 0) try self.asmRegisterRegister(
|
||||
.{ ._, .@"or" },
|
||||
acc_reg,
|
||||
@@ -9039,6 +9251,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
|
||||
.undef,
|
||||
.immediate,
|
||||
.eflags,
|
||||
.register_pair,
|
||||
.register_offset,
|
||||
.register_overflow,
|
||||
.lea_direct,
|
||||
@@ -9528,13 +9741,16 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
extra_i += inputs.len;
|
||||
|
||||
var result: MCValue = .none;
|
||||
var args = std.StringArrayHashMap(MCValue).init(self.gpa);
|
||||
var args = std.ArrayList(MCValue).init(self.gpa);
|
||||
try args.ensureTotalCapacity(outputs.len + inputs.len);
|
||||
defer {
|
||||
for (args.values()) |arg| if (arg.getReg()) |reg|
|
||||
for (args.items) |arg| if (arg.getReg()) |reg|
|
||||
self.register_manager.unlockReg(.{ .register = reg });
|
||||
args.deinit();
|
||||
}
|
||||
var arg_map = std.StringHashMap(u8).init(self.gpa);
|
||||
try arg_map.ensureTotalCapacity(@intCast(outputs.len + inputs.len));
|
||||
defer arg_map.deinit();
|
||||
|
||||
var outputs_extra_i = extra_i;
|
||||
for (outputs) |output| {
|
||||
@@ -9585,7 +9801,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
|
||||
_ = self.register_manager.lockRegAssumeUnused(reg);
|
||||
};
|
||||
args.putAssumeCapacity(name, arg_mcv);
|
||||
if (!std.mem.eql(u8, name, "_"))
|
||||
arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len));
|
||||
args.appendAssumeCapacity(arg_mcv);
|
||||
if (output == .none) result = arg_mcv;
|
||||
}
|
||||
|
||||
@@ -9643,7 +9861,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
|
||||
_ = self.register_manager.lockReg(reg);
|
||||
};
|
||||
args.putAssumeCapacity(name, arg_mcv);
|
||||
if (!std.mem.eql(u8, name, "_"))
|
||||
arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len));
|
||||
args.appendAssumeCapacity(arg_mcv);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -9712,8 +9932,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
op_str[colon_pos + 1 .. op_str.len - "]".len]
|
||||
else
|
||||
"";
|
||||
op.* = switch (args.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse
|
||||
return self.fail("no matching constraint: '{s}'", .{op_str})) {
|
||||
op.* = switch (args.items[
|
||||
arg_map.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse
|
||||
return self.fail("no matching constraint: '{s}'", .{op_str})
|
||||
]) {
|
||||
.register => |reg| if (std.mem.eql(u8, modifier, ""))
|
||||
.{ .reg = reg }
|
||||
else
|
||||
@@ -9826,7 +10048,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
};
|
||||
}
|
||||
|
||||
for (outputs, args.values()[0..outputs.len]) |output, mcv| {
|
||||
for (outputs, args.items[0..outputs.len]) |output, mcv| {
|
||||
const extra_bytes = std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]);
|
||||
const constraint =
|
||||
std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0);
|
||||
@@ -10093,6 +10315,19 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
|
||||
.off = -dst_reg_off.off,
|
||||
} },
|
||||
}),
|
||||
.register_pair => |dst_regs| for (dst_regs, 0..) |dst_reg, dst_reg_i| switch (src_mcv) {
|
||||
.register_pair => |src_regs| try self.genSetReg(
|
||||
dst_reg,
|
||||
Type.usize,
|
||||
.{ .register = src_regs[dst_reg_i] },
|
||||
),
|
||||
else => try self.genSetReg(
|
||||
dst_reg,
|
||||
Type.usize,
|
||||
src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(),
|
||||
),
|
||||
.air_ref => |src_ref| return self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)),
|
||||
},
|
||||
.indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),
|
||||
.memory, .load_direct, .load_got, .load_tlv => {
|
||||
switch (dst_mcv) {
|
||||
@@ -10126,6 +10361,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
|
||||
.none,
|
||||
.unreach,
|
||||
.dead,
|
||||
.register_pair,
|
||||
.register_overflow,
|
||||
.reserved_frame,
|
||||
=> unreachable,
|
||||
@@ -10203,17 +10439,13 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
|
||||
.sse => try self.asmRegisterRegister(
|
||||
@as(?Mir.Inst.FixedTag, switch (ty.scalarType(mod).zigTypeTag(mod)) {
|
||||
else => switch (abi_size) {
|
||||
1...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov },
|
||||
5...8 => if (self.hasFeature(.avx)) .{ .v_q, .mov } else .{ ._q, .mov },
|
||||
9...16 => if (self.hasFeature(.avx)) .{ .v_, .movdqa } else .{ ._, .movdqa },
|
||||
1...16 => if (self.hasFeature(.avx)) .{ .v_, .movdqa } else .{ ._, .movdqa },
|
||||
17...32 => if (self.hasFeature(.avx)) .{ .v_, .movdqa } else null,
|
||||
else => null,
|
||||
},
|
||||
.Float => switch (ty.scalarType(mod).floatBits(self.target.*)) {
|
||||
16, 128 => switch (abi_size) {
|
||||
2...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov },
|
||||
5...8 => if (self.hasFeature(.avx)) .{ .v_q, .mov } else .{ ._q, .mov },
|
||||
9...16 => if (self.hasFeature(.avx))
|
||||
2...16 => if (self.hasFeature(.avx))
|
||||
.{ .v_, .movdqa }
|
||||
else
|
||||
.{ ._, .movdqa },
|
||||
@@ -10307,6 +10539,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
|
||||
Immediate.u(0),
|
||||
),
|
||||
}
|
||||
return;
|
||||
},
|
||||
.load_direct => |sym_index| switch (ty.zigTypeTag(mod)) {
|
||||
else => {
|
||||
@@ -10398,7 +10631,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
|
||||
@tagName(self.bin_file.tag),
|
||||
});
|
||||
},
|
||||
.air_ref => |ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(ref)),
|
||||
.air_ref => |src_ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(src_ref)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10475,6 +10708,32 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
|
||||
),
|
||||
}
|
||||
},
|
||||
.register_pair => |src_regs| for (src_regs, 0..) |src_reg, src_reg_i| {
|
||||
const part_size = @min(abi_size - src_reg_i * 8, 8);
|
||||
const dst_mem = Memory.sib(
|
||||
Memory.PtrSize.fromSize(part_size),
|
||||
.{ .base = base, .disp = disp + @as(i32, @intCast(src_reg_i * 8)) },
|
||||
);
|
||||
const src_alias = registerAlias(src_reg, part_size);
|
||||
switch (try self.moveStrategy(ty, switch (base) {
|
||||
.none => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
|
||||
.reg => |reg| switch (reg) {
|
||||
.es, .cs, .ss, .ds => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
|
||||
else => false,
|
||||
},
|
||||
.frame => |frame_index| self.getFrameAddrAlignment(
|
||||
.{ .index = frame_index, .off = disp },
|
||||
).compare(.gte, ty.abiAlignment(mod)),
|
||||
})) {
|
||||
.move => |tag| try self.asmMemoryRegister(tag, dst_mem, src_alias),
|
||||
.insert_extract, .vex_insert_extract => |ie| try self.asmMemoryRegisterImmediate(
|
||||
ie.extract,
|
||||
dst_mem,
|
||||
src_alias,
|
||||
Immediate.u(0),
|
||||
),
|
||||
}
|
||||
},
|
||||
.register_overflow => |ro| {
|
||||
try self.genSetMem(
|
||||
base,
|
||||
@@ -10511,7 +10770,7 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
|
||||
},
|
||||
else => try self.genInlineMemcpy(dst_ptr_mcv, src_mcv.address(), .{ .immediate = abi_size }),
|
||||
},
|
||||
.air_ref => |ref| try self.genSetMem(base, disp, ty, try self.resolveInst(ref)),
|
||||
.air_ref => |src_ref| try self.genSetMem(base, disp, ty, try self.resolveInst(src_ref)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11475,6 +11734,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||
const inst_ty = self.typeOfIndex(inst);
|
||||
const enum_ty = self.typeOf(un_op);
|
||||
const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*);
|
||||
|
||||
// We need a properly aligned and sized call frame to be able to call this function.
|
||||
{
|
||||
@@ -11492,9 +11752,9 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
|
||||
}
|
||||
|
||||
try self.spillEflagsIfOccupied();
|
||||
try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*));
|
||||
try self.spillRegisters(abi.getCallerPreservedRegs(resolved_cc));
|
||||
|
||||
const param_regs = abi.getCAbiIntParamRegs(self.target.*);
|
||||
const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
|
||||
|
||||
const dst_mcv = try self.allocRegOrMem(inst, false);
|
||||
try self.genSetReg(param_regs[0], Type.usize, dst_mcv.address());
|
||||
@@ -12271,23 +12531,27 @@ fn resolveCallingConventionValues(
|
||||
|
||||
const ret_ty = fn_info.return_type.toType();
|
||||
|
||||
const resolved_cc = abi.resolveCallingConvention(cc, self.target.*);
|
||||
switch (cc) {
|
||||
.Naked => {
|
||||
assert(result.args.len == 0);
|
||||
result.return_value = InstTracking.init(.unreach);
|
||||
result.stack_align = .@"8";
|
||||
},
|
||||
.C => {
|
||||
var param_reg_i: usize = 0;
|
||||
.C, .SysV, .Win64 => {
|
||||
var ret_int_reg_i: usize = 0;
|
||||
var ret_sse_reg_i: usize = 0;
|
||||
var param_int_reg_i: usize = 0;
|
||||
var param_sse_reg_i: usize = 0;
|
||||
result.stack_align = .@"16";
|
||||
|
||||
switch (self.target.os.tag) {
|
||||
.windows => {
|
||||
switch (resolved_cc) {
|
||||
.SysV => {},
|
||||
.Win64 => {
|
||||
// Align the stack to 16bytes before allocating shadow stack space (if any).
|
||||
result.stack_byte_count += @intCast(4 * Type.usize.abiSize(mod));
|
||||
},
|
||||
else => {},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
// Return values
|
||||
@@ -12297,71 +12561,133 @@ fn resolveCallingConventionValues(
|
||||
// TODO: is this even possible for C calling convention?
|
||||
result.return_value = InstTracking.init(.none);
|
||||
} else {
|
||||
const classes = switch (self.target.os.tag) {
|
||||
.windows => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},
|
||||
else => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),
|
||||
var ret_tracking: [2]InstTracking = undefined;
|
||||
var ret_tracking_i: usize = 0;
|
||||
|
||||
const classes = switch (resolved_cc) {
|
||||
.SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),
|
||||
.Win64 => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},
|
||||
else => unreachable,
|
||||
};
|
||||
if (classes.len > 1) {
|
||||
return self.fail("TODO handle multiple classes per type", .{});
|
||||
}
|
||||
const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0];
|
||||
result.return_value = switch (classes[0]) {
|
||||
.integer => InstTracking.init(.{ .register = registerAlias(
|
||||
ret_reg,
|
||||
@intCast(ret_ty.abiSize(mod)),
|
||||
) }),
|
||||
.float, .sse => InstTracking.init(.{ .register = .xmm0 }),
|
||||
.memory => ret: {
|
||||
const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];
|
||||
param_reg_i += 1;
|
||||
break :ret .{
|
||||
.short = .{ .indirect = .{ .reg = ret_reg } },
|
||||
for (classes) |class| switch (class) {
|
||||
.integer => {
|
||||
const ret_int_reg = registerAlias(
|
||||
abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i],
|
||||
@intCast(@min(ret_ty.abiSize(mod), 8)),
|
||||
);
|
||||
ret_int_reg_i += 1;
|
||||
|
||||
ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_int_reg });
|
||||
ret_tracking_i += 1;
|
||||
},
|
||||
.sse, .float, .float_combine, .win_i128 => {
|
||||
const ret_sse_reg = registerAlias(
|
||||
abi.getCAbiSseReturnRegs(resolved_cc)[ret_sse_reg_i],
|
||||
@intCast(ret_ty.abiSize(mod)),
|
||||
);
|
||||
ret_sse_reg_i += 1;
|
||||
|
||||
ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_sse_reg });
|
||||
ret_tracking_i += 1;
|
||||
},
|
||||
.sseup => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .sse),
|
||||
.x87 => {
|
||||
ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = .st0 });
|
||||
ret_tracking_i += 1;
|
||||
},
|
||||
.x87up => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .x87),
|
||||
.complex_x87 => {
|
||||
ret_tracking[ret_tracking_i] =
|
||||
InstTracking.init(.{ .register_pair = .{ .st0, .st1 } });
|
||||
ret_tracking_i += 1;
|
||||
},
|
||||
.memory => {
|
||||
const ret_int_reg = abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i].to64();
|
||||
ret_int_reg_i += 1;
|
||||
const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i];
|
||||
param_int_reg_i += 1;
|
||||
|
||||
ret_tracking[ret_tracking_i] = .{
|
||||
.short = .{ .indirect = .{ .reg = ret_int_reg } },
|
||||
.long = .{ .indirect = .{ .reg = ret_indirect_reg } },
|
||||
};
|
||||
ret_tracking_i += 1;
|
||||
},
|
||||
else => |class| return self.fail("TODO handle calling convention class {s}", .{
|
||||
@tagName(class),
|
||||
}),
|
||||
.none => unreachable,
|
||||
};
|
||||
result.return_value = switch (ret_tracking_i) {
|
||||
else => unreachable,
|
||||
1 => ret_tracking[0],
|
||||
2 => InstTracking.init(.{ .register_pair = .{
|
||||
ret_tracking[0].short.register, ret_tracking[1].short.register,
|
||||
} }),
|
||||
};
|
||||
}
|
||||
|
||||
// Input params
|
||||
for (param_types, result.args) |ty, *arg| {
|
||||
assert(ty.hasRuntimeBitsIgnoreComptime(mod));
|
||||
switch (resolved_cc) {
|
||||
.SysV => {},
|
||||
.Win64 => {
|
||||
param_int_reg_i = @max(param_int_reg_i, param_sse_reg_i);
|
||||
param_sse_reg_i = param_int_reg_i;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
var arg_mcv: [2]MCValue = undefined;
|
||||
var arg_mcv_i: usize = 0;
|
||||
|
||||
const classes = switch (self.target.os.tag) {
|
||||
.windows => &[1]abi.Class{abi.classifyWindows(ty, mod)},
|
||||
else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none),
|
||||
};
|
||||
if (classes.len > 1) {
|
||||
return self.fail("TODO handle multiple classes per type", .{});
|
||||
}
|
||||
switch (classes[0]) {
|
||||
.integer => if (param_reg_i < abi.getCAbiIntParamRegs(self.target.*).len) {
|
||||
arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i] };
|
||||
param_reg_i += 1;
|
||||
continue;
|
||||
for (classes) |class| switch (class) {
|
||||
.integer => {
|
||||
const param_int_regs = abi.getCAbiIntParamRegs(resolved_cc);
|
||||
if (param_int_reg_i >= param_int_regs.len) break;
|
||||
|
||||
const param_int_reg = registerAlias(
|
||||
abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i],
|
||||
@intCast(@min(ty.abiSize(mod), 8)),
|
||||
);
|
||||
param_int_reg_i += 1;
|
||||
|
||||
arg_mcv[arg_mcv_i] = .{ .register = param_int_reg };
|
||||
arg_mcv_i += 1;
|
||||
},
|
||||
.float, .sse => switch (self.target.os.tag) {
|
||||
.windows => if (param_reg_i < 4) {
|
||||
arg.* = .{
|
||||
.register = @enumFromInt(@intFromEnum(Register.xmm0) + param_reg_i),
|
||||
};
|
||||
param_reg_i += 1;
|
||||
continue;
|
||||
},
|
||||
else => if (param_sse_reg_i < 8) {
|
||||
arg.* = .{
|
||||
.register = @enumFromInt(@intFromEnum(Register.xmm0) + param_sse_reg_i),
|
||||
};
|
||||
param_sse_reg_i += 1;
|
||||
continue;
|
||||
},
|
||||
.sse, .float, .float_combine => {
|
||||
const param_sse_regs = abi.getCAbiSseParamRegs(resolved_cc);
|
||||
if (param_sse_reg_i >= param_sse_regs.len) break;
|
||||
|
||||
const param_sse_reg = registerAlias(
|
||||
abi.getCAbiSseParamRegs(resolved_cc)[param_sse_reg_i],
|
||||
@intCast(ty.abiSize(mod)),
|
||||
);
|
||||
param_sse_reg_i += 1;
|
||||
|
||||
arg_mcv[arg_mcv_i] = .{ .register = param_sse_reg };
|
||||
arg_mcv_i += 1;
|
||||
},
|
||||
.memory => {}, // fallthrough
|
||||
else => |class| return self.fail("TODO handle calling convention class {s}", .{
|
||||
@tagName(class),
|
||||
}),
|
||||
.sseup => assert(arg_mcv[arg_mcv_i - 1].register.class() == .sse),
|
||||
.x87, .x87up, .complex_x87, .memory => break,
|
||||
.none => unreachable,
|
||||
.win_i128 => {
|
||||
const param_int_reg =
|
||||
abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i].to64();
|
||||
param_int_reg_i += 1;
|
||||
|
||||
arg_mcv[arg_mcv_i] = .{ .indirect = .{ .reg = param_int_reg } };
|
||||
arg_mcv_i += 1;
|
||||
},
|
||||
} else {
|
||||
arg.* = switch (arg_mcv_i) {
|
||||
else => unreachable,
|
||||
1 => arg_mcv[0],
|
||||
2 => .{ .register_pair = .{ arg_mcv[0].register, arg_mcv[1].register } },
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
const param_size: u31 = @intCast(ty.abiSize(mod));
|
||||
@@ -12384,13 +12710,13 @@ fn resolveCallingConventionValues(
|
||||
} else if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {
|
||||
result.return_value = InstTracking.init(.none);
|
||||
} else {
|
||||
const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0];
|
||||
const ret_reg = abi.getCAbiIntReturnRegs(resolved_cc)[0];
|
||||
const ret_ty_size: u31 = @intCast(ret_ty.abiSize(mod));
|
||||
if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) {
|
||||
const aliased_reg = registerAlias(ret_reg, ret_ty_size);
|
||||
result.return_value = .{ .short = .{ .register = aliased_reg }, .long = .none };
|
||||
} else {
|
||||
const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[0];
|
||||
const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[0];
|
||||
result.return_value = .{
|
||||
.short = .{ .indirect = .{ .reg = ret_reg } },
|
||||
.long = .{ .indirect = .{ .reg = ret_indirect_reg } },
|
||||
@@ -12462,7 +12788,10 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {
|
||||
reg
|
||||
else
|
||||
unreachable,
|
||||
.x87 => unreachable,
|
||||
.x87 => if (size_bytes == 16)
|
||||
reg
|
||||
else
|
||||
unreachable,
|
||||
.mmx => if (size_bytes <= 8)
|
||||
reg
|
||||
else
|
||||
@@ -12597,9 +12926,7 @@ fn floatCompilerRtAbiType(self: *Self, ty: Type, other_ty: Type) Type {
|
||||
|
||||
fn floatLibcAbiPrefix(ty: Type) []const u8 {
|
||||
return switch (ty.toIntern()) {
|
||||
.f16_type,
|
||||
.f80_type,
|
||||
=> "__",
|
||||
.f16_type, .f80_type => "__",
|
||||
.f32_type, .f64_type, .f128_type, .c_longdouble_type => "",
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
+10
-12
@@ -242,16 +242,14 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
|
||||
},
|
||||
.plan9 => |dbg_out| {
|
||||
if (delta_pc <= 0) return; // only do this when the pc changes
|
||||
// we have already checked the target in the linker to make sure it is compatable
|
||||
const quant = @import("../../link/Plan9/aout.zig").getPCQuant(emit.lower.target.cpu.arch) catch unreachable;
|
||||
|
||||
// increasing the line number
|
||||
try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
|
||||
try link.File.Plan9.changeLine(&dbg_out.dbg_line, delta_line);
|
||||
// increasing the pc
|
||||
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - quant;
|
||||
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
|
||||
if (d_pc_p9 > 0) {
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one quanta
|
||||
var diff = @divExact(d_pc_p9, quant) - quant;
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one pc quanta
|
||||
var diff = @divExact(d_pc_p9, dbg_out.pc_quanta) - dbg_out.pc_quanta;
|
||||
while (diff > 0) {
|
||||
if (diff < 64) {
|
||||
try dbg_out.dbg_line.append(@as(u8, @intCast(diff + 128)));
|
||||
@@ -261,15 +259,15 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
|
||||
diff -= 64;
|
||||
}
|
||||
}
|
||||
if (dbg_out.pcop_change_index.*) |pci|
|
||||
if (dbg_out.pcop_change_index) |pci|
|
||||
dbg_out.dbg_line.items[pci] += 1;
|
||||
dbg_out.pcop_change_index.* = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
|
||||
dbg_out.pcop_change_index = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
|
||||
} else if (d_pc_p9 == 0) {
|
||||
// we don't need to do anything, because adding the quant does it for us
|
||||
// we don't need to do anything, because adding the pc quanta does it for us
|
||||
} else unreachable;
|
||||
if (dbg_out.start_line.* == null)
|
||||
dbg_out.start_line.* = emit.prev_di_line;
|
||||
dbg_out.end_line.* = line;
|
||||
if (dbg_out.start_line == null)
|
||||
dbg_out.start_line = emit.prev_di_line;
|
||||
dbg_out.end_line = line;
|
||||
// only do this if the pc changed
|
||||
emit.prev_di_line = line;
|
||||
emit.prev_di_column = column;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
allocator: Allocator,
|
||||
mir: Mir,
|
||||
target: *const std.Target,
|
||||
cc: std.builtin.CallingConvention,
|
||||
err_msg: ?*ErrorMsg = null,
|
||||
src_loc: Module.SrcLoc,
|
||||
result_insts_len: u8 = undefined,
|
||||
@@ -552,15 +552,13 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
}
|
||||
|
||||
fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Error!void {
|
||||
const callee_preserved_regs = abi.getCalleePreservedRegs(lower.target.*);
|
||||
const callee_preserved_regs = abi.getCalleePreservedRegs(lower.cc);
|
||||
var it = inst.data.reg_list.iterator(.{ .direction = switch (mnemonic) {
|
||||
.push => .reverse,
|
||||
.pop => .forward,
|
||||
else => unreachable,
|
||||
} });
|
||||
while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{
|
||||
.reg = callee_preserved_regs[i],
|
||||
}});
|
||||
while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{ .reg = callee_preserved_regs[i] }});
|
||||
}
|
||||
|
||||
const page_size: i32 = 1 << 12;
|
||||
|
||||
+60
-24
@@ -137,7 +137,7 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
|
||||
return result;
|
||||
},
|
||||
128 => {
|
||||
// "Arguments of types__float128, _Decimal128 and__m128 are
|
||||
// "Arguments of types __float128, _Decimal128 and __m128 are
|
||||
// split into two halves. The least significant ones belong
|
||||
// to class SSE, the most significant one to class SSEUP."
|
||||
if (ctx == .other) {
|
||||
@@ -213,9 +213,9 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
|
||||
const struct_type = mod.typeToStruct(ty).?;
|
||||
const ty_size = ty.abiSize(mod);
|
||||
if (struct_type.layout == .Packed) {
|
||||
assert(ty_size <= 128);
|
||||
assert(ty_size <= 16);
|
||||
result[0] = .integer;
|
||||
if (ty_size > 64) result[1] = .integer;
|
||||
if (ty_size > 8) result[1] = .integer;
|
||||
return result;
|
||||
}
|
||||
if (ty_size > 64)
|
||||
@@ -331,9 +331,9 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
|
||||
const union_obj = mod.typeToUnion(ty).?;
|
||||
const ty_size = mod.unionAbiSize(union_obj);
|
||||
if (union_obj.getLayout(ip) == .Packed) {
|
||||
assert(ty_size <= 128);
|
||||
assert(ty_size <= 16);
|
||||
result[0] = .integer;
|
||||
if (ty_size > 64) result[1] = .integer;
|
||||
if (ty_size > 8) result[1] = .integer;
|
||||
return result;
|
||||
}
|
||||
if (ty_size > 64)
|
||||
@@ -422,11 +422,11 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
|
||||
},
|
||||
.Array => {
|
||||
const ty_size = ty.abiSize(mod);
|
||||
if (ty_size <= 64) {
|
||||
if (ty_size <= 8) {
|
||||
result[0] = .integer;
|
||||
return result;
|
||||
}
|
||||
if (ty_size <= 128) {
|
||||
if (ty_size <= 16) {
|
||||
result[0] = .integer;
|
||||
result[1] = .integer;
|
||||
return result;
|
||||
@@ -447,7 +447,9 @@ pub const SysV = struct {
|
||||
pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;
|
||||
|
||||
pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
|
||||
pub const c_abi_sse_param_regs = sse_avx_regs[0..8].*;
|
||||
pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
|
||||
pub const c_abi_sse_return_regs = sse_avx_regs[0..2].*;
|
||||
};
|
||||
|
||||
pub const Win64 = struct {
|
||||
@@ -460,34 +462,69 @@ pub const Win64 = struct {
|
||||
pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;
|
||||
|
||||
pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };
|
||||
pub const c_abi_sse_param_regs = sse_avx_regs[0..4].*;
|
||||
pub const c_abi_int_return_regs = [_]Register{.rax};
|
||||
pub const c_abi_sse_return_regs = sse_avx_regs[0..1].*;
|
||||
};
|
||||
|
||||
pub fn getCalleePreservedRegs(target: Target) []const Register {
|
||||
return switch (target.os.tag) {
|
||||
.windows => &Win64.callee_preserved_regs,
|
||||
else => &SysV.callee_preserved_regs,
|
||||
pub fn resolveCallingConvention(
|
||||
cc: std.builtin.CallingConvention,
|
||||
target: std.Target,
|
||||
) std.builtin.CallingConvention {
|
||||
return switch (cc) {
|
||||
.Unspecified, .C => switch (target.os.tag) {
|
||||
else => .SysV,
|
||||
.windows => .Win64,
|
||||
},
|
||||
else => cc,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getCallerPreservedRegs(target: Target) []const Register {
|
||||
return switch (target.os.tag) {
|
||||
.windows => &Win64.caller_preserved_regs,
|
||||
else => &SysV.caller_preserved_regs,
|
||||
pub fn getCalleePreservedRegs(cc: std.builtin.CallingConvention) []const Register {
|
||||
return switch (cc) {
|
||||
.SysV => &SysV.callee_preserved_regs,
|
||||
.Win64 => &Win64.callee_preserved_regs,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getCAbiIntParamRegs(target: Target) []const Register {
|
||||
return switch (target.os.tag) {
|
||||
.windows => &Win64.c_abi_int_param_regs,
|
||||
else => &SysV.c_abi_int_param_regs,
|
||||
pub fn getCallerPreservedRegs(cc: std.builtin.CallingConvention) []const Register {
|
||||
return switch (cc) {
|
||||
.SysV => &SysV.caller_preserved_regs,
|
||||
.Win64 => &Win64.caller_preserved_regs,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getCAbiIntReturnRegs(target: Target) []const Register {
|
||||
return switch (target.os.tag) {
|
||||
.windows => &Win64.c_abi_int_return_regs,
|
||||
else => &SysV.c_abi_int_return_regs,
|
||||
pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register {
|
||||
return switch (cc) {
|
||||
.SysV => &SysV.c_abi_int_param_regs,
|
||||
.Win64 => &Win64.c_abi_int_param_regs,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getCAbiSseParamRegs(cc: std.builtin.CallingConvention) []const Register {
|
||||
return switch (cc) {
|
||||
.SysV => &SysV.c_abi_sse_param_regs,
|
||||
.Win64 => &Win64.c_abi_sse_param_regs,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register {
|
||||
return switch (cc) {
|
||||
.SysV => &SysV.c_abi_int_return_regs,
|
||||
.Win64 => &Win64.c_abi_int_return_regs,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention) []const Register {
|
||||
return switch (cc) {
|
||||
.SysV => &SysV.c_abi_sse_return_regs,
|
||||
.Win64 => &Win64.c_abi_sse_return_regs,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -524,7 +561,6 @@ pub const RegisterClass = struct {
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const Target = std.Target;
|
||||
const assert = std.debug.assert;
|
||||
const testing = std.testing;
|
||||
|
||||
|
||||
+1
-22
@@ -40,28 +40,7 @@ pub const CodeGenError = error{
|
||||
|
||||
pub const DebugInfoOutput = union(enum) {
|
||||
dwarf: *link.File.Dwarf.DeclState,
|
||||
/// the plan9 debuginfo output is a bytecode with 4 opcodes
|
||||
/// assume all numbers/variables are bytes
|
||||
/// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset
|
||||
/// x when x < 65 -> add x to line offset
|
||||
/// x when x < 129 -> subtract 64 from x and subtract it from the line offset
|
||||
/// x -> subtract 129 from x, multiply it by the quanta of the instruction size
|
||||
/// (1 on x86_64), and add it to the pc
|
||||
/// after every opcode, add the quanta of the instruction size to the pc
|
||||
plan9: struct {
|
||||
/// the actual opcodes
|
||||
dbg_line: *std.ArrayList(u8),
|
||||
/// what line the debuginfo starts on
|
||||
/// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
|
||||
start_line: *?u32,
|
||||
/// what the line count ends on after codegen
|
||||
/// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
|
||||
end_line: *u32,
|
||||
/// the last pc change op
|
||||
/// This is very useful for adding quanta
|
||||
/// to it if its not actually the last one.
|
||||
pcop_change_index: *?u32,
|
||||
},
|
||||
plan9: *link.File.Plan9.DebugInfoOutput,
|
||||
none,
|
||||
};
|
||||
|
||||
|
||||
+74
-1
@@ -566,6 +566,7 @@ pub const DeclState = struct {
|
||||
|
||||
pub const DbgInfoLoc = union(enum) {
|
||||
register: u8,
|
||||
register_pair: [2]u8,
|
||||
stack: struct {
|
||||
fp_register: u8,
|
||||
offset: i32,
|
||||
@@ -610,6 +611,42 @@ pub const DeclState = struct {
|
||||
leb128.writeULEB128(dbg_info.writer(), reg) catch unreachable;
|
||||
}
|
||||
},
|
||||
.register_pair => |regs| {
|
||||
const reg_bits = self.mod.getTarget().ptrBitWidth();
|
||||
const reg_bytes = @as(u8, @intCast(@divExact(reg_bits, 8)));
|
||||
const abi_size = ty.abiSize(self.mod);
|
||||
try dbg_info.ensureUnusedCapacity(10);
|
||||
dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevKind.parameter));
|
||||
// DW.AT.location, DW.FORM.exprloc
|
||||
var expr_len = std.io.countingWriter(std.io.null_writer);
|
||||
for (regs, 0..) |reg, reg_i| {
|
||||
if (reg < 32) {
|
||||
expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable;
|
||||
} else {
|
||||
expr_len.writer().writeByte(DW.OP.regx) catch unreachable;
|
||||
leb128.writeULEB128(expr_len.writer(), reg) catch unreachable;
|
||||
}
|
||||
expr_len.writer().writeByte(DW.OP.piece) catch unreachable;
|
||||
leb128.writeULEB128(
|
||||
expr_len.writer(),
|
||||
@min(abi_size - reg_i * reg_bytes, reg_bytes),
|
||||
) catch unreachable;
|
||||
}
|
||||
leb128.writeULEB128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
|
||||
for (regs, 0..) |reg, reg_i| {
|
||||
if (reg < 32) {
|
||||
dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg);
|
||||
} else {
|
||||
dbg_info.appendAssumeCapacity(DW.OP.regx);
|
||||
leb128.writeULEB128(dbg_info.writer(), reg) catch unreachable;
|
||||
}
|
||||
dbg_info.appendAssumeCapacity(DW.OP.piece);
|
||||
leb128.writeULEB128(
|
||||
dbg_info.writer(),
|
||||
@min(abi_size - reg_i * reg_bytes, reg_bytes),
|
||||
) catch unreachable;
|
||||
}
|
||||
},
|
||||
.stack => |info| {
|
||||
try dbg_info.ensureUnusedCapacity(9);
|
||||
dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevKind.parameter));
|
||||
@@ -676,7 +713,7 @@ pub const DeclState = struct {
|
||||
|
||||
switch (loc) {
|
||||
.register => |reg| {
|
||||
try dbg_info.ensureUnusedCapacity(4);
|
||||
try dbg_info.ensureUnusedCapacity(3);
|
||||
// DW.AT.location, DW.FORM.exprloc
|
||||
var expr_len = std.io.countingWriter(std.io.null_writer);
|
||||
if (reg < 32) {
|
||||
@@ -694,6 +731,42 @@ pub const DeclState = struct {
|
||||
}
|
||||
},
|
||||
|
||||
.register_pair => |regs| {
|
||||
const reg_bits = self.mod.getTarget().ptrBitWidth();
|
||||
const reg_bytes = @as(u8, @intCast(@divExact(reg_bits, 8)));
|
||||
const abi_size = child_ty.abiSize(self.mod);
|
||||
try dbg_info.ensureUnusedCapacity(9);
|
||||
// DW.AT.location, DW.FORM.exprloc
|
||||
var expr_len = std.io.countingWriter(std.io.null_writer);
|
||||
for (regs, 0..) |reg, reg_i| {
|
||||
if (reg < 32) {
|
||||
expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable;
|
||||
} else {
|
||||
expr_len.writer().writeByte(DW.OP.regx) catch unreachable;
|
||||
leb128.writeULEB128(expr_len.writer(), reg) catch unreachable;
|
||||
}
|
||||
expr_len.writer().writeByte(DW.OP.piece) catch unreachable;
|
||||
leb128.writeULEB128(
|
||||
expr_len.writer(),
|
||||
@min(abi_size - reg_i * reg_bytes, reg_bytes),
|
||||
) catch unreachable;
|
||||
}
|
||||
leb128.writeULEB128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
|
||||
for (regs, 0..) |reg, reg_i| {
|
||||
if (reg < 32) {
|
||||
dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg);
|
||||
} else {
|
||||
dbg_info.appendAssumeCapacity(DW.OP.regx);
|
||||
leb128.writeULEB128(dbg_info.writer(), reg) catch unreachable;
|
||||
}
|
||||
dbg_info.appendAssumeCapacity(DW.OP.piece);
|
||||
leb128.writeULEB128(
|
||||
dbg_info.writer(),
|
||||
@min(abi_size - reg_i * reg_bytes, reg_bytes),
|
||||
) catch unreachable;
|
||||
}
|
||||
},
|
||||
|
||||
.stack => |info| {
|
||||
try dbg_info.ensureUnusedCapacity(9);
|
||||
// DW.AT.location, DW.FORM.exprloc
|
||||
|
||||
+38
-16
@@ -211,6 +211,31 @@ pub const Atom = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// the plan9 debuginfo output is a bytecode with 4 opcodes
|
||||
/// assume all numbers/variables are bytes
|
||||
/// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset
|
||||
/// x when x < 65 -> add x to line offset
|
||||
/// x when x < 129 -> subtract 64 from x and subtract it from the line offset
|
||||
/// x -> subtract 129 from x, multiply it by the quanta of the instruction size
|
||||
/// (1 on x86_64), and add it to the pc
|
||||
/// after every opcode, add the quanta of the instruction size to the pc
|
||||
pub const DebugInfoOutput = struct {
|
||||
/// the actual opcodes
|
||||
dbg_line: std.ArrayList(u8),
|
||||
/// what line the debuginfo starts on
|
||||
/// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
|
||||
start_line: ?u32,
|
||||
/// what the line count ends on after codegen
|
||||
/// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
|
||||
end_line: u32,
|
||||
/// the last pc change op
|
||||
/// This is very useful for adding quanta
|
||||
/// to it if its not actually the last one.
|
||||
pcop_change_index: ?u32,
|
||||
/// cached pc quanta
|
||||
pc_quanta: u8,
|
||||
};
|
||||
|
||||
const DeclMetadata = struct {
|
||||
index: Atom.Index,
|
||||
exports: std.ArrayListUnmanaged(usize) = .{},
|
||||
@@ -376,11 +401,15 @@ pub fn updateFunc(self: *Plan9, mod: *Module, func_index: InternPool.Index, air:
|
||||
|
||||
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
|
||||
defer code_buffer.deinit();
|
||||
var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
|
||||
defer dbg_line_buffer.deinit();
|
||||
var start_line: ?u32 = null;
|
||||
var end_line: u32 = undefined;
|
||||
var pcop_change_index: ?u32 = null;
|
||||
var dbg_info_output: DebugInfoOutput = .{
|
||||
.dbg_line = std.ArrayList(u8).init(self.base.allocator),
|
||||
.start_line = null,
|
||||
.end_line = undefined,
|
||||
.pcop_change_index = null,
|
||||
// we have already checked the target in the linker to make sure it is compatable
|
||||
.pc_quanta = aout.getPCQuant(self.base.options.target.cpu.arch) catch unreachable,
|
||||
};
|
||||
defer dbg_info_output.dbg_line.deinit();
|
||||
|
||||
const res = try codegen.generateFunction(
|
||||
&self.base,
|
||||
@@ -389,14 +418,7 @@ pub fn updateFunc(self: *Plan9, mod: *Module, func_index: InternPool.Index, air:
|
||||
air,
|
||||
liveness,
|
||||
&code_buffer,
|
||||
.{
|
||||
.plan9 = .{
|
||||
.dbg_line = &dbg_line_buffer,
|
||||
.end_line = &end_line,
|
||||
.start_line = &start_line,
|
||||
.pcop_change_index = &pcop_change_index,
|
||||
},
|
||||
},
|
||||
.{ .plan9 = &dbg_info_output },
|
||||
);
|
||||
const code = switch (res) {
|
||||
.ok => try code_buffer.toOwnedSlice(),
|
||||
@@ -412,9 +434,9 @@ pub fn updateFunc(self: *Plan9, mod: *Module, func_index: InternPool.Index, air:
|
||||
};
|
||||
const out: FnDeclOutput = .{
|
||||
.code = code,
|
||||
.lineinfo = try dbg_line_buffer.toOwnedSlice(),
|
||||
.start_line = start_line.?,
|
||||
.end_line = end_line,
|
||||
.lineinfo = try dbg_info_output.dbg_line.toOwnedSlice(),
|
||||
.start_line = dbg_info_output.start_line.?,
|
||||
.end_line = dbg_info_output.end_line,
|
||||
};
|
||||
try self.putFn(decl_index, out);
|
||||
return self.updateFinish(decl_index);
|
||||
|
||||
@@ -97,7 +97,7 @@ test "@abs floats" {
|
||||
try comptime testAbsFloats(f80);
|
||||
if (builtin.zig_backend != .stage2_x86_64 and builtin.zig_backend != .stage2_wasm) try testAbsFloats(f80);
|
||||
try comptime testAbsFloats(f128);
|
||||
if (builtin.zig_backend != .stage2_x86_64 and builtin.zig_backend != .stage2_wasm) try testAbsFloats(f128);
|
||||
if (builtin.zig_backend != .stage2_wasm) try testAbsFloats(f128);
|
||||
}
|
||||
|
||||
fn testAbsFloats(comptime T: type) !void {
|
||||
|
||||
@@ -297,11 +297,11 @@ test "triple level result location with bitcast sandwich passed as tuple element
|
||||
test "@bitCast packed struct of floats" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
const Foo = packed struct {
|
||||
a: f16 = 0,
|
||||
@@ -375,11 +375,11 @@ test "comptime @bitCast packed struct to int and back" {
|
||||
|
||||
test "comptime bitcast with fields following f80" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
const FloatT = extern struct { f: f80, x: u128 align(16) };
|
||||
const x: FloatT = .{ .f = 0.5, .x = 123 };
|
||||
|
||||
@@ -18,7 +18,6 @@ const wuffs_base__slice_u8 = extern struct {
|
||||
};
|
||||
test {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
|
||||
@@ -1369,10 +1369,10 @@ fn boolToStr(b: bool) []const u8 {
|
||||
test "cast f16 to wider types" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
@@ -1389,9 +1389,9 @@ test "cast f16 to wider types" {
|
||||
test "cast f128 to narrower types" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
|
||||
@@ -130,7 +130,7 @@ test "cmp f128" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
try testCmp(f128);
|
||||
try comptime testCmp(f128);
|
||||
@@ -1379,7 +1379,7 @@ test "comptime fixed-width float zero divided by zero produces NaN" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
inline for (.{ f16, f32, f64, f80, f128 }) |F| {
|
||||
try expect(math.isNan(@as(F, 0) / @as(F, 0)));
|
||||
|
||||
@@ -637,11 +637,11 @@ fn testShrTrunc(x: u16) !void {
|
||||
}
|
||||
|
||||
test "f128" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
try test_f128();
|
||||
try comptime test_f128();
|
||||
@@ -1481,7 +1481,6 @@ test "@round f80" {
|
||||
test "@round f128" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
|
||||
@@ -1522,9 +1521,9 @@ test "vector integer addition" {
|
||||
test "NaN comparison" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
try testNanEqNan(f16);
|
||||
try testNanEqNan(f32);
|
||||
@@ -1539,9 +1538,9 @@ test "NaN comparison" {
|
||||
test "NaN comparison f80" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
try testNanEqNan(f80);
|
||||
try comptime testNanEqNan(f80);
|
||||
|
||||
@@ -10,7 +10,6 @@ test "@max" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
@@ -30,7 +29,8 @@ test "@max on vectors" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and
|
||||
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
@@ -78,7 +78,8 @@ test "@min for vectors" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and
|
||||
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
|
||||
@@ -258,7 +258,6 @@ test "nested packed struct unaligned" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (native_endian != .Little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet
|
||||
|
||||
const S1 = packed struct {
|
||||
|
||||
@@ -60,11 +60,11 @@ test "float widening" {
|
||||
}
|
||||
|
||||
test "float widening f16 to f128" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
|
||||
|
||||
var x: f16 = 12.34;
|
||||
var y: f128 = x;
|
||||
|
||||
Reference in New Issue
Block a user