diff options
| author | Jacob Young <jacobly0@users.noreply.github.com> | 2023-10-05 14:47:15 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-05 14:47:15 -0400 |
| commit | e6590fea19e3eab94b35bfd3c36e29b53cefcaaf (patch) | |
| tree | 11fb8283d25a1175895233332720cf68504f2544 /src | |
| parent | 8b4e3b6aee61a26e7229ea30728e6c5e31c7cd0b (diff) | |
| parent | 54b2d6f072bde7f85c44bbcc4795bd980d79d45e (diff) | |
| download | zig-e6590fea19e3eab94b35bfd3c36e29b53cefcaaf.tar.gz zig-e6590fea19e3eab94b35bfd3c36e29b53cefcaaf.zip | |
Merge pull request #17359 from jacobly0/x86_64
x86_64: improve C abi support
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/aarch64/Emit.zig | 22 | ||||
| -rw-r--r-- | src/arch/arm/Emit.zig | 22 | ||||
| -rw-r--r-- | src/arch/riscv64/Emit.zig | 22 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 907 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 22 | ||||
| -rw-r--r-- | src/arch/x86_64/Lower.zig | 8 | ||||
| -rw-r--r-- | src/arch/x86_64/abi.zig | 84 | ||||
| -rw-r--r-- | src/codegen.zig | 23 | ||||
| -rw-r--r-- | src/link/Dwarf.zig | 75 | ||||
| -rw-r--r-- | src/link/Plan9.zig | 54 |
10 files changed, 833 insertions, 406 deletions
diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 68c28ed8aa..9ba722f393 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -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; diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 54062d00a7..45c3392918 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -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; diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 20f2c40ba4..9d82cc38cc 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -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; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4d15255567..868bf4d337 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -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, + .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, + }; + 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, - if (math.cast(i16, @as(i64, @bitCast(imm)))) |small| - Immediate.s(small) - else - Immediate.u(@as(u16, @intCast(imm))), + registerAlias(src_mcv.getRegs()[dst_reg_i], limb_abi_size), ), - 32 => try self.asmRegisterImmediate( + .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, + }), + ); + } + + 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, - dst_alias, - if (math.cast(i32, @as(i64, @bitCast(imm)))) |small| - Immediate.s(small) - else - Immediate.u(@as(u32, @intCast(imm))), + ty, + dst_mcv, + try self.resolveInst(src_ref), ), - 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, - )), - 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 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)), + } } }, .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, + .eflags, .register, + .register_pair, .register_offset, - .eflags, .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 OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; + + const resolved_dst_mcv = switch (dst_mcv) { + else => dst_mcv, + .air_ref => |dst_ref| try self.resolveInst(dst_ref), }; - 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 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 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 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_src_mcv = switch (src_mcv) { + else => src_mcv, + .air_ref => |src_ref| try self.resolveInst(src_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 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 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, }; diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index f17eda57eb..d2a199da42 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -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; diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index b557520673..71f411bdaf 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -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; diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 1794abeb0b..682c2644e1 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -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 getCalleePreservedRegs(cc: std.builtin.CallingConvention) []const Register { + return switch (cc) { + .SysV => &SysV.callee_preserved_regs, + .Win64 => &Win64.callee_preserved_regs, + else => unreachable, + }; +} + +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 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 getCallerPreservedRegs(target: Target) []const Register { - return switch (target.os.tag) { - .windows => &Win64.caller_preserved_regs, - else => &SysV.caller_preserved_regs, +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 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 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 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 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; diff --git a/src/codegen.zig b/src/codegen.zig index dd1b6e0cbf..2c5fd7b630 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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, }; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 2beef48595..cbf5f350e8 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -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 diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index a7a571e9ff..b519fdda00 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -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); |
