diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-10-28 19:41:07 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-28 19:41:07 +0200 |
| commit | 13c7aa5fefef9b1338951cf0d01c04345201d996 (patch) | |
| tree | 4fd2509ed8a88870a02b655cc29dec77ec0a9f6c /src | |
| parent | d817a3c5173a4f9e41c5872e58733287c9686018 (diff) | |
| parent | 12b5c8156a2ce9c2ef1e5e71a19e86d4876fd457 (diff) | |
| download | zig-13c7aa5fefef9b1338951cf0d01c04345201d996.tar.gz zig-13c7aa5fefef9b1338951cf0d01c04345201d996.zip | |
Merge pull request #17727 from ziglang/elf-memory-deferred
x86_64+elf: do not pass hardcoded memory addresses in prep for build-obj
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 6 | ||||
| -rw-r--r-- | src/arch/arm/CodeGen.zig | 2 | ||||
| -rw-r--r-- | src/arch/riscv64/CodeGen.zig | 2 | ||||
| -rw-r--r-- | src/arch/sparc64/CodeGen.zig | 2 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 1174 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 52 | ||||
| -rw-r--r-- | src/arch/x86_64/Lower.zig | 78 | ||||
| -rw-r--r-- | src/arch/x86_64/Mir.zig | 9 | ||||
| -rw-r--r-- | src/codegen.zig | 23 | ||||
| -rw-r--r-- | src/link/Elf.zig | 9 | ||||
| -rw-r--r-- | src/link/Elf/Atom.zig | 3 |
11 files changed, 844 insertions, 516 deletions
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index d0e068da73..b3ca885ef8 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4012,7 +4012,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .got => .load_memory_ptr_got, .direct => .load_memory_ptr_direct, .import => unreachable, - .extern_got => unreachable, }; const atom_index = switch (self.bin_file.tag) { .macho => blk: { @@ -5532,7 +5531,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .got => .load_memory_ptr_got, .direct => .load_memory_ptr_direct, .import => unreachable, - .extern_got => unreachable, }; const atom_index = switch (self.bin_file.tag) { .macho => blk: { @@ -5654,7 +5652,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .got => .load_memory_got, .direct => .load_memory_direct, .import => .load_memory_import, - .extern_got => unreachable, }; const atom_index = switch (self.bin_file.tag) { .macho => blk: { @@ -5852,7 +5849,6 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I .got => .load_memory_ptr_got, .direct => .load_memory_ptr_direct, .import => unreachable, - .extern_got => unreachable, }; const atom_index = switch (self.bin_file.tag) { .macho => blk: { @@ -6180,7 +6176,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { .memory => |addr| .{ .memory = addr }, .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } }, .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, - .load_extern_got, .load_tlv => unreachable, // TODO + .load_symbol, .load_tlv => unreachable, // TODO }, .fail => |msg| { self.err_msg = msg; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index a7c437bb59..18121a04ac 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -6135,7 +6135,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, - .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO + .load_got, .load_symbol, .load_direct, .load_tlv => unreachable, // TODO .immediate => |imm| .{ .immediate = @as(u32, @truncate(imm)) }, .memory => |addr| .{ .memory = addr }, }, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index e4c4266531..f66be65d1c 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2591,7 +2591,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, - .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO + .load_got, .load_symbol, .load_direct, .load_tlv => unreachable, // TODO .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, }, diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index ae4fc29e8a..cc9630320f 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -4137,7 +4137,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, - .load_got, .load_extern_got, .load_direct, .load_tlv => unreachable, // TODO + .load_got, .load_symbol, .load_direct, .load_tlv => unreachable, // TODO .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, }, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c18aa94716..c6d9aebf1f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -193,6 +193,11 @@ pub const MCValue = union(enum) { /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is stored at this memory location. memory: u64, + /// The value is in memory at an address not-yet-allocated by the linker. + /// This traditionally corresponds to a relocation emitted in a relocatable object file. + load_symbol: u32, + /// The address of the memory location not-yet-allocated by the linker. + lea_symbol: u32, /// The value is in memory at a constant offset from the address in a register. indirect: RegisterOffset, /// The value is in memory. @@ -207,12 +212,6 @@ pub const MCValue = union(enum) { /// The value is a pointer to a value referenced indirectly via GOT. /// Payload is a symbol index. lea_got: u32, - /// The value is an extern variable referenced via GOT. - /// Payload is a symbol index. - load_extern_got: u32, - /// The value is a pointer to an extern variable referenced via GOT. - /// Payload is a symbol index. - lea_extern_got: u32, /// The value is a threadlocal variable. /// Payload is a symbol index. load_tlv: u32, @@ -299,9 +298,9 @@ pub const MCValue = union(enum) { .register_pair, .register_offset, .register_overflow, + .lea_symbol, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, .reserved_frame, @@ -315,8 +314,8 @@ pub const MCValue = union(enum) { .load_direct => |sym_index| .{ .lea_direct = sym_index }, .load_got => |sym_index| .{ .lea_got = sym_index }, .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, - .load_extern_got => |sym_index| .{ .lea_extern_got = sym_index }, .load_frame => |frame_addr| .{ .lea_frame = frame_addr }, + .load_symbol => |sym_index| .{ .lea_symbol = sym_index }, }; } @@ -333,9 +332,9 @@ pub const MCValue = union(enum) { .indirect, .load_direct, .load_got, - .load_extern_got, .load_tlv, .load_frame, + .load_symbol, .reserved_frame, .air_ref, => unreachable, // not dereferenceable @@ -344,9 +343,9 @@ pub const MCValue = union(enum) { .register_offset => |reg_off| .{ .indirect = reg_off }, .lea_direct => |sym_index| .{ .load_direct = sym_index }, .lea_got => |sym_index| .{ .load_got = sym_index }, - .lea_extern_got => |sym_index| .{ .load_extern_got = sym_index }, .lea_tlv => |sym_index| .{ .load_tlv = sym_index }, .lea_frame => |frame_addr| .{ .load_frame = frame_addr }, + .lea_symbol => |sym_index| .{ .load_symbol = sym_index }, }; } @@ -368,11 +367,11 @@ pub const MCValue = union(enum) { .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .load_frame, + .load_symbol, + .lea_symbol, => switch (off) { 0 => mcv, else => unreachable, // not offsettable @@ -404,13 +403,13 @@ pub const MCValue = union(enum) { .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .lea_frame, .reserved_frame, .air_ref, + .load_symbol, + .lea_symbol, => unreachable, .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| Memory.sib(ptr_size, .{ .base = .{ .reg = .ds }, .disp = small_addr }) @@ -448,14 +447,14 @@ pub const MCValue = union(enum) { .lea_direct => |pl| try writer.print("direct:{d}", .{pl}), .load_got => |pl| try writer.print("[got:{d}]", .{pl}), .lea_got => |pl| try writer.print("got:{d}", .{pl}), - .load_extern_got => |pl| try writer.print("[extern_got:{d}]", .{pl}), - .lea_extern_got => |pl| try writer.print("extern_got:{d}", .{pl}), .load_tlv => |pl| try writer.print("[tlv:{d}]", .{pl}), .lea_tlv => |pl| try writer.print("tlv:{d}", .{pl}), .load_frame => |pl| try writer.print("[{} + 0x{x}]", .{ pl.index, pl.off }), .lea_frame => |pl| try writer.print("{} + 0x{x}", .{ pl.index, pl.off }), .reserved_frame => |pl| try writer.print("(dead:{})", .{pl}), .air_ref => |pl| try writer.print("(air:0x{x})", .{@intFromEnum(pl)}), + .load_symbol => |pl| try writer.print("[symbol:{d}]", .{pl}), + .lea_symbol => |pl| try writer.print("symbol:{d}", .{pl}), } } }; @@ -477,12 +476,12 @@ const InstTracking = struct { .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .load_frame, .lea_frame, + .load_symbol, + .lea_symbol, => result, .dead, .reserved_frame, @@ -538,12 +537,12 @@ const InstTracking = struct { .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .load_frame, .lea_frame, + .load_symbol, + .lea_symbol, => self.long, .dead, .eflags, @@ -575,11 +574,11 @@ const InstTracking = struct { .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .lea_frame, + .load_symbol, + .lea_symbol, => assert(std.meta.eql(self.long, target.long)), .load_frame, .reserved_frame, @@ -883,6 +882,7 @@ pub fn generate( var emit = Emit{ .lower = .{ + .bin_file = bin_file, .allocator = bin_file.allocator, .mir = mir, .cc = cc, @@ -970,6 +970,7 @@ pub fn generateLazy( var emit = Emit{ .lower = .{ + .bin_file = bin_file, .allocator = bin_file.allocator, .mir = mir, .cc = abi.resolveCallingConvention(.Unspecified, function.target.*), @@ -1060,6 +1061,7 @@ fn formatWipMir( writer: anytype, ) @TypeOf(writer).Error!void { var lower = Lower{ + .bin_file = data.self.bin_file, .allocator = data.self.gpa, .mir = .{ .instructions = data.self.mir_instructions.slice(), @@ -3261,25 +3263,51 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(tmp_lock); const lhs_mcv = try self.resolveInst(bin_op.lhs); - if (lhs_mcv.isMemory()) try self.asmRegisterMemory( + const mat_lhs_mcv = switch (lhs_mcv) { + .load_symbol => mat_lhs_mcv: { + // TODO clean this up! + const addr_reg = try self.copyToTmpRegister(Type.usize, lhs_mcv.address()); + break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; + }, + else => lhs_mcv, + }; + const mat_lhs_lock = switch (mat_lhs_mcv) { + .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), + else => null, + }; + defer if (mat_lhs_lock) |lock| self.register_manager.unlockReg(lock); + if (mat_lhs_mcv.isMemory()) try self.asmRegisterMemory( .{ ._, .mov }, tmp_reg, - lhs_mcv.address().offset(8).deref().mem(.qword), + mat_lhs_mcv.address().offset(8).deref().mem(.qword), ) else try self.asmRegisterRegister( .{ ._, .mov }, tmp_reg, - lhs_mcv.register_pair[1], + mat_lhs_mcv.register_pair[1], ); const rhs_mcv = try self.resolveInst(bin_op.rhs); - if (rhs_mcv.isMemory()) try self.asmRegisterMemory( + const mat_rhs_mcv = switch (rhs_mcv) { + .load_symbol => mat_rhs_mcv: { + // TODO clean this up! + const addr_reg = try self.copyToTmpRegister(Type.usize, rhs_mcv.address()); + break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; + }, + else => rhs_mcv, + }; + const mat_rhs_lock = switch (mat_rhs_mcv) { + .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), + else => null, + }; + defer if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); + if (mat_rhs_mcv.isMemory()) try self.asmRegisterMemory( .{ ._, .xor }, tmp_reg, - rhs_mcv.address().offset(8).deref().mem(.qword), + mat_rhs_mcv.address().offset(8).deref().mem(.qword), ) else try self.asmRegisterRegister( .{ ._, .xor }, tmp_reg, - rhs_mcv.register_pair[1], + mat_rhs_mcv.register_pair[1], ); const reloc = try self.asmJccReloc(.ns, undefined); @@ -3353,20 +3381,37 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock); const rhs_mcv = try self.resolveInst(bin_op.rhs); + const mat_rhs_mcv = switch (rhs_mcv) { + .load_symbol => mat_rhs_mcv: { + // TODO clean this up! + const addr_reg = try self.copyToTmpRegister(Type.usize, rhs_mcv.address()); + break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; + }, + else => rhs_mcv, + }; + const mat_rhs_lock = switch (mat_rhs_mcv) { + .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), + else => null, + }; + defer if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); for (tmp_regs, dst_regs) |tmp_reg, dst_reg| try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, dst_reg); - if (rhs_mcv.isMemory()) { - try self.asmRegisterMemory(.{ ._, .add }, tmp_regs[0], rhs_mcv.mem(.qword)); + if (mat_rhs_mcv.isMemory()) { + try self.asmRegisterMemory( + .{ ._, .add }, + tmp_regs[0], + mat_rhs_mcv.mem(.qword), + ); try self.asmRegisterMemory( .{ ._, .adc }, tmp_regs[1], - rhs_mcv.address().offset(8).deref().mem(.qword), + mat_rhs_mcv.address().offset(8).deref().mem(.qword), ); } else for ( [_]Mir.Inst.Tag{ .add, .adc }, tmp_regs, - rhs_mcv.register_pair, + mat_rhs_mcv.register_pair, ) |op, tmp_reg, rhs_reg| try self.asmRegisterRegister(.{ ._, op }, tmp_reg, rhs_reg); try self.asmRegisterRegister(.{ ._, .@"test" }, dst_regs[1], dst_regs[1]); @@ -3871,39 +3916,65 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs_mcv = try self.resolveInst(bin_op.lhs); const rhs_mcv = try self.resolveInst(bin_op.rhs); + const mat_lhs_mcv = switch (lhs_mcv) { + .load_symbol => mat_lhs_mcv: { + // TODO clean this up! + const addr_reg = try self.copyToTmpRegister(Type.usize, lhs_mcv.address()); + break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; + }, + else => lhs_mcv, + }; + const mat_lhs_lock = switch (mat_lhs_mcv) { + .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), + else => null, + }; + defer if (mat_lhs_lock) |lock| self.register_manager.unlockReg(lock); + const mat_rhs_mcv = switch (rhs_mcv) { + .load_symbol => mat_rhs_mcv: { + // TODO clean this up! + const addr_reg = try self.copyToTmpRegister(Type.usize, rhs_mcv.address()); + break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; + }, + else => rhs_mcv, + }; + const mat_rhs_lock = switch (mat_rhs_mcv) { + .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), + else => null, + }; + defer if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); - if (lhs_mcv.isMemory()) - try self.asmRegisterMemory(.{ ._, .mov }, .rax, lhs_mcv.mem(.qword)) + if (mat_lhs_mcv.isMemory()) + try self.asmRegisterMemory(.{ ._, .mov }, .rax, mat_lhs_mcv.mem(.qword)) else - try self.asmRegisterRegister(.{ ._, .mov }, .rax, lhs_mcv.register_pair[0]); - if (rhs_mcv.isMemory()) try self.asmRegisterMemory( + try self.asmRegisterRegister(.{ ._, .mov }, .rax, mat_lhs_mcv.register_pair[0]); + if (mat_rhs_mcv.isMemory()) try self.asmRegisterMemory( .{ ._, .mov }, tmp_regs[0], - rhs_mcv.address().offset(8).deref().mem(.qword), + mat_rhs_mcv.address().offset(8).deref().mem(.qword), ) else try self.asmRegisterRegister( .{ ._, .mov }, tmp_regs[0], - rhs_mcv.register_pair[1], + mat_rhs_mcv.register_pair[1], ); try self.asmRegisterRegister(.{ ._, .@"test" }, tmp_regs[0], tmp_regs[0]); try self.asmSetccRegister(.nz, tmp_regs[1].to8()); try self.asmRegisterRegister(.{ .i_, .mul }, tmp_regs[0], .rax); try self.asmSetccRegister(.o, tmp_regs[2].to8()); - if (rhs_mcv.isMemory()) - try self.asmMemory(.{ ._, .mul }, rhs_mcv.mem(.qword)) + if (mat_rhs_mcv.isMemory()) + try self.asmMemory(.{ ._, .mul }, mat_rhs_mcv.mem(.qword)) else - try self.asmRegister(.{ ._, .mul }, rhs_mcv.register_pair[0]); + try self.asmRegister(.{ ._, .mul }, mat_rhs_mcv.register_pair[0]); try self.asmRegisterRegister(.{ ._, .add }, .rdx, tmp_regs[0]); try self.asmSetccRegister(.c, tmp_regs[3].to8()); try self.asmRegisterRegister(.{ ._, .@"or" }, tmp_regs[2].to8(), tmp_regs[3].to8()); - if (lhs_mcv.isMemory()) try self.asmRegisterMemory( + if (mat_lhs_mcv.isMemory()) try self.asmRegisterMemory( .{ ._, .mov }, tmp_regs[0], - lhs_mcv.address().offset(8).deref().mem(.qword), + mat_lhs_mcv.address().offset(8).deref().mem(.qword), ) else try self.asmRegisterRegister( .{ ._, .mov }, tmp_regs[0], - lhs_mcv.register_pair[1], + mat_lhs_mcv.register_pair[1], ); try self.asmRegisterRegister(.{ ._, .@"test" }, tmp_regs[0], tmp_regs[0]); try self.asmSetccRegister(.nz, tmp_regs[3].to8()); @@ -3913,13 +3984,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { tmp_regs[3].to8(), ); try self.asmRegisterRegister(.{ ._, .@"or" }, tmp_regs[1].to8(), tmp_regs[2].to8()); - if (rhs_mcv.isMemory()) - try self.asmRegisterMemory(.{ .i_, .mul }, tmp_regs[0], rhs_mcv.mem(.qword)) + if (mat_rhs_mcv.isMemory()) + try self.asmRegisterMemory(.{ .i_, .mul }, tmp_regs[0], mat_rhs_mcv.mem(.qword)) else try self.asmRegisterRegister( .{ .i_, .mul }, tmp_regs[0], - rhs_mcv.register_pair[0], + mat_rhs_mcv.register_pair[0], ); try self.asmSetccRegister(.o, tmp_regs[2].to8()); try self.asmRegisterRegister(.{ ._, .@"or" }, tmp_regs[1].to8(), tmp_regs[2].to8()); @@ -4729,12 +4800,12 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { Memory.sib(.qword, .{ .base = .{ .frame = frame_addr.index }, .disp = frame_addr.off }), ), .memory, + .load_symbol, .load_direct, .load_got, - .load_extern_got, .load_tlv, => try self.genSetReg(addr_reg, Type.usize, array.address()), - .lea_direct, .lea_tlv => unreachable, + .lea_symbol, .lea_direct, .lea_tlv => unreachable, else => return self.fail("TODO implement array_elem_val when array is {}", .{array}), } @@ -6327,17 +6398,17 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro .immediate, .register, .register_offset, + .lea_symbol, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()), .memory, .indirect, + .load_symbol, .load_direct, .load_got, - .load_extern_got, .load_tlv, .load_frame, => { @@ -6476,17 +6547,17 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr .immediate, .register, .register_offset, + .lea_symbol, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv), .memory, .indirect, + .load_symbol, .load_direct, .load_got, - .load_extern_got, .load_tlv, .load_frame, => { @@ -6587,12 +6658,15 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const src_reg_lock = self.register_manager.lockRegAssumeUnused(src_reg); defer self.register_manager.unlockReg(src_reg_lock); - const dst_reg = if (field_rc.supersetOf(container_rc) and - self.reuseOperand(inst, operand, 0, src_mcv)) + const src_in_field_rc = + field_rc.isSet(RegisterManager.indexOfRegIntoTracked(src_reg).?); + const dst_reg = if (src_in_field_rc and self.reuseOperand(inst, operand, 0, src_mcv)) src_reg + else if (field_off == 0) + (try self.copyToRegisterWithInstTracking(inst, field_ty, src_mcv)).register else try self.copyToTmpRegister(Type.usize, .{ .register = src_reg }); - const dst_mcv = MCValue{ .register = dst_reg }; + const dst_mcv: MCValue = .{ .register = dst_reg }; const dst_lock = self.register_manager.lockReg(dst_reg); defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); @@ -6602,9 +6676,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { dst_mcv, .{ .immediate = field_off }, ); - if (self.regExtraBits(field_ty) > 0) try self.truncateRegister(field_ty, dst_reg); + if (abi.RegisterClass.gp.isSet(RegisterManager.indexOfRegIntoTracked(dst_reg).?) and + container_ty.abiSize(mod) * 8 > field_ty.bitSize(mod)) + try self.truncateRegister(field_ty, dst_reg); - break :result if (field_rc.supersetOf(abi.RegisterClass.gp)) + break :result if (field_off == 0 or field_rc.supersetOf(abi.RegisterClass.gp)) dst_mcv else try self.copyToRegisterWithInstTracking(inst, field_ty, dst_mcv); @@ -6908,15 +6984,15 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC .register_overflow, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, .reserved_frame, .air_ref, + .lea_symbol, => unreachable, // unmodifiable destination .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), .register_pair => unreachable, // unimplemented - .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => { + .memory, .load_symbol, .load_got, .load_direct, .load_tlv => { const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_reg_lock); @@ -7352,34 +7428,61 @@ fn genMulDivBinOp( const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); + const mat_lhs_mcv = switch (lhs_mcv) { + .load_symbol => mat_lhs_mcv: { + // TODO clean this up! + const addr_reg = try self.copyToTmpRegister(Type.usize, lhs_mcv.address()); + break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; + }, + else => lhs_mcv, + }; + const mat_lhs_lock = switch (mat_lhs_mcv) { + .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), + else => null, + }; + defer if (mat_lhs_lock) |lock| self.register_manager.unlockReg(lock); + const mat_rhs_mcv = switch (rhs_mcv) { + .load_symbol => mat_rhs_mcv: { + // TODO clean this up! + const addr_reg = try self.copyToTmpRegister(Type.usize, rhs_mcv.address()); + break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; + }, + else => rhs_mcv, + }; + const mat_rhs_lock = switch (mat_rhs_mcv) { + .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), + else => null, + }; + defer if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); + const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_lock); - if (lhs_mcv.isMemory()) - try self.asmRegisterMemory(.{ ._, .mov }, .rax, lhs_mcv.mem(.qword)) + if (mat_lhs_mcv.isMemory()) + try self.asmRegisterMemory(.{ ._, .mov }, .rax, mat_lhs_mcv.mem(.qword)) else - try self.asmRegisterRegister(.{ ._, .mov }, .rax, lhs_mcv.register_pair[0]); - if (rhs_mcv.isMemory()) try self.asmRegisterMemory( + try self.asmRegisterRegister(.{ ._, .mov }, .rax, mat_lhs_mcv.register_pair[0]); + if (mat_rhs_mcv.isMemory()) try self.asmRegisterMemory( .{ ._, .mov }, tmp_reg, - rhs_mcv.address().offset(8).deref().mem(.qword), - ) else try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, rhs_mcv.register_pair[1]); + mat_rhs_mcv.address().offset(8).deref().mem(.qword), + ) else try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, mat_rhs_mcv.register_pair[1]); try self.asmRegisterRegister(.{ .i_, .mul }, tmp_reg, .rax); - if (rhs_mcv.isMemory()) - try self.asmMemory(.{ ._, .mul }, rhs_mcv.mem(.qword)) + if (mat_rhs_mcv.isMemory()) + try self.asmMemory(.{ ._, .mul }, mat_rhs_mcv.mem(.qword)) else - try self.asmRegister(.{ ._, .mul }, rhs_mcv.register_pair[0]); + try self.asmRegister(.{ ._, .mul }, mat_rhs_mcv.register_pair[0]); try self.asmRegisterRegister(.{ ._, .add }, .rdx, tmp_reg); - if (lhs_mcv.isMemory()) try self.asmRegisterMemory( + if (mat_lhs_mcv.isMemory()) try self.asmRegisterMemory( .{ ._, .mov }, tmp_reg, - lhs_mcv.address().offset(8).deref().mem(.qword), - ) else try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, lhs_mcv.register_pair[1]); - if (rhs_mcv.isMemory()) - try self.asmRegisterMemory(.{ .i_, .mul }, tmp_reg, rhs_mcv.mem(.qword)) + mat_lhs_mcv.address().offset(8).deref().mem(.qword), + ) else try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, mat_lhs_mcv.register_pair[1]); + if (mat_rhs_mcv.isMemory()) + try self.asmRegisterMemory(.{ .i_, .mul }, tmp_reg, mat_rhs_mcv.mem(.qword)) else - try self.asmRegisterRegister(.{ .i_, .mul }, tmp_reg, rhs_mcv.register_pair[0]); + try self.asmRegisterRegister(.{ .i_, .mul }, tmp_reg, mat_rhs_mcv.register_pair[0]); try self.asmRegisterRegister(.{ ._, .add }, .rdx, tmp_reg); return .{ .register_pair = .{ .rax, .rdx } }; } @@ -7968,12 +8071,12 @@ fn genBinOp( .immediate, .eflags, .register_offset, + .load_symbol, + .lea_symbol, .load_direct, .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .lea_frame, @@ -8026,12 +8129,12 @@ fn genBinOp( .register_pair, .register_offset, .register_overflow, + .load_symbol, + .lea_symbol, .load_direct, .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .lea_frame, @@ -9159,9 +9262,9 @@ fn genBinOpMir( .register_overflow, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, + .lea_symbol, .reserved_frame, .air_ref, => unreachable, // unmodifiable destination @@ -9244,12 +9347,12 @@ fn genBinOpMir( .register_offset, .memory, .indirect, + .load_symbol, + .lea_symbol, .load_direct, .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .load_frame, @@ -9280,9 +9383,9 @@ fn genBinOpMir( switch (src_mcv) { .eflags, .register_offset, + .lea_symbol, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, => { @@ -9296,9 +9399,9 @@ fn genBinOpMir( ); }, .memory, + .load_symbol, .load_direct, .load_got, - .load_extern_got, .load_tlv, => { const ptr_ty = try mod.singleConstPtrType(ty); @@ -9319,13 +9422,13 @@ fn genBinOpMir( } } }, - .memory, .indirect, .load_got, .load_extern_got, .load_direct, .load_tlv, .load_frame => { + .memory, .indirect, .load_symbol, .load_got, .load_direct, .load_tlv, .load_frame => { const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; const limb_abi_size: u32 = @min(abi_size, 8); const dst_info: OpInfo = switch (dst_mcv) { else => unreachable, - .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => dst: { + .memory, .load_symbol, .load_got, .load_direct, .load_tlv => dst: { const dst_addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); @@ -9359,17 +9462,17 @@ fn genBinOpMir( .indirect, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .load_frame, .lea_frame, + .lea_symbol, => null, - .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => src: { + .memory, .load_symbol, .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 - limb_abi_size) != null) break :src null, - .load_got, .load_extern_got, .load_direct, .load_tlv => {}, + .load_symbol, .load_got, .load_direct, .load_tlv => {}, else => unreachable, } @@ -9411,8 +9514,8 @@ fn genBinOpMir( Memory.PtrSize.fromSize(limb_abi_size), switch (dst_mcv) { .memory, + .load_symbol, .load_got, - .load_extern_got, .load_direct, .load_tlv, => .{ .base = .{ .reg = dst_info.?.addr_reg }, .disp = off }, @@ -9493,12 +9596,12 @@ fn genBinOpMir( .eflags, .memory, .indirect, + .load_symbol, + .lea_symbol, .load_direct, .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .load_frame, @@ -9512,9 +9615,9 @@ fn genBinOpMir( }, .eflags, .register_offset, + .lea_symbol, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, => switch (limb_i) { @@ -9563,9 +9666,9 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .eflags, .register_offset, .register_overflow, + .lea_symbol, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, .reserved_frame, @@ -9610,12 +9713,12 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M }, .register_offset, .eflags, + .load_symbol, + .lea_symbol, .load_direct, .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .lea_frame, @@ -9654,7 +9757,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_extern_got, .load_tlv, .load_frame => { + .memory, .indirect, .load_symbol, .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 }; const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); @@ -9674,17 +9777,25 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { self.arg_index = arg_index + 1; const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { - const dst_mcv = self.args[arg_index]; - switch (dst_mcv) { - .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}), - } + const arg_ty = self.typeOfIndex(inst); + const src_mcv = self.args[arg_index]; + const dst_mcv = switch (src_mcv) { + .register, .register_pair, .load_frame => dst: { + for (src_mcv.getRegs()) |reg| self.register_manager.getRegAssumeFree(reg, inst); + break :dst src_mcv; + }, + .indirect => |reg_off| dst: { + self.register_manager.getRegAssumeFree(reg_off.reg, inst); + const dst_mcv = try self.allocRegOrMem(inst, false); + try self.genCopy(arg_ty, dst_mcv, src_mcv); + break :dst dst_mcv; + }, + else => return self.fail("TODO implement arg for {}", .{src_mcv}), + }; - const ty = self.typeOfIndex(inst); const src_index = self.air.instructions.items(.data)[inst].arg.src_index; const name = mod.getParamName(self.owner.func_index, src_index); - try self.genArgDbgInfo(ty, name, dst_mcv); + try self.genArgDbgInfo(arg_ty, name, src_mcv); break :result dst_mcv; }; @@ -9746,8 +9857,8 @@ fn genVarDbgInfo( // .offset = -off, //} }, .memory => |address| .{ .memory = address }, + .load_symbol => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, // TODO .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } }, - .load_extern_got => |sym_index| .{ .linker_load = .{ .type = .extern_got, .sym_index = sym_index } }, .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, .immediate => |x| .{ .immediate = x }, .undef => .undef, @@ -9863,7 +9974,8 @@ fn genCall(self: *Self, info: union(enum) { const ExpectedContents = extern struct { var_args: [16][@sizeOf(Type)]u8 align(@alignOf(Type)), - arg_regs: [16][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)), + frame_indices: [16]FrameIndex, + reg_locks: [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); @@ -9873,10 +9985,13 @@ 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 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); + const frame_indices = try allocator.alloc(FrameIndex, args.len); + defer allocator.free(frame_indices); + + var reg_locks = std.ArrayList(?RegisterLock).init(allocator); + defer reg_locks.deinit(); + try reg_locks.ensureTotalCapacity(16); + defer for (reg_locks.items) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); var call_info = try self.resolveCallingConventionValues(fn_info, var_args, .call_frame); defer call_info.deinit(self); @@ -9908,44 +10023,69 @@ fn genCall(self: *Self, info: union(enum) { .indirect => |reg_off| try self.spillRegisters(&.{reg_off.reg}), else => unreachable, } - for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) { - .none => {}, - .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); - try self.freeValue(src_arg); - }, - else => unreachable, - }; + for (call_info.args, arg_types, args, frame_indices) |dst_arg, arg_ty, src_arg, *frame_index| + switch (dst_arg) { + .none => {}, + .register => |reg| { + try self.spillRegisters(&.{reg}); + try reg_locks.append(self.register_manager.lockReg(reg)); + }, + .register_pair => |regs| { + try self.spillRegisters(®s); + try reg_locks.appendSlice(&self.register_manager.lockRegs(2, regs)); + }, + .indirect => |reg_off| { + frame_index.* = try self.allocFrameIndex(FrameAlloc.initType(arg_ty, mod)); + try self.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg); + try self.spillRegisters(&.{reg_off.reg}); + try reg_locks.append(self.register_manager.lockReg(reg_off.reg)); + }, + .load_frame => { + try self.genCopy(arg_ty, dst_arg, src_arg); + try self.freeValue(src_arg); + }, + else => unreachable, + }; // now we are free to set register arguments - const ret_lock = switch (call_info.return_value.long) { - .none, .unreach => null, - .indirect => |reg_off| lock: { + switch (call_info.return_value.long) { + .none, .unreach => {}, + .indirect => |reg_off| { const ret_ty = fn_info.return_type.toType(); const frame_index = try self.allocFrameIndex(FrameAlloc.initType(ret_ty, mod)); try self.genSetReg(reg_off.reg, Type.usize, .{ .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, }); call_info.return_value.short = .{ .load_frame = .{ .index = frame_index } }; - break :lock self.register_manager.lockRegAssumeUnused(reg_off.reg); + try reg_locks.append(self.register_manager.lockReg(reg_off.reg)); }, else => unreachable, - }; - 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, .register_pair => try self.genCopy(arg_ty, dst_arg, src_arg), - else => unreachable, - }; + for (call_info.args, arg_types, args, frame_indices) |dst_arg, arg_ty, src_arg, frame_index| + switch (dst_arg) { + .none, .load_frame => {}, + .register => |dst_reg| switch (fn_info.cc) { + else => try self.genSetReg( + registerAlias(dst_reg, @intCast(arg_ty.abiSize(mod))), + arg_ty, + src_arg, + ), + .C, .SysV, .Win64 => { + const promoted_ty = self.promoteInt(arg_ty); + const promoted_abi_size: u32 = @intCast(promoted_ty.abiSize(mod)); + const dst_alias = registerAlias(dst_reg, promoted_abi_size); + try self.genSetReg(dst_alias, promoted_ty, src_arg); + if (promoted_ty.toIntern() != arg_ty.toIntern()) + try self.truncateRegister(arg_ty, dst_alias); + }, + }, + .register_pair => try self.genCopy(arg_ty, dst_arg, src_arg), + .indirect => |reg_off| try self.genSetReg(reg_off.reg, Type.usize, .{ + .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, + }), + else => unreachable, + }; if (fn_info.is_var_args) try self.asmRegisterImmediate(.{ ._, .mov }, .al, Immediate.u(call_info.fp_count)); @@ -9969,12 +10109,12 @@ fn genCall(self: *Self, info: union(enum) { const sym = elf_file.symbol(sym_index); _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); if (self.bin_file.options.pic) { - try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym.esym_index }); + try self.genSetReg(.rax, Type.usize, .{ .load_symbol = sym.esym_index }); try self.asmRegister(.{ ._, .call }, .rax); } else { _ = try self.addInst(.{ .tag = .call, - .ops = .direct_got_reloc, + .ops = .linker_reloc, .data = .{ .reloc = .{ .atom_index = try self.owner.getSymbolIndex(self), .sym_index = sym.esym_index, @@ -10054,7 +10194,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { const ptr_ty = self.typeOf(un_op); switch (self.ret_mcv.short) { .none => {}, - .register => try self.load(self.ret_mcv.short, ptr_ty, ptr), + .register, .register_pair => try self.load(self.ret_mcv.short, ptr_ty, ptr), .indirect => |reg_off| try self.genSetReg(reg_off.reg, ptr_ty, ptr), else => unreachable, } @@ -10115,20 +10255,28 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { try self.spillEflagsIfOccupied(); 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, + const lhs_locks: [2]?RegisterLock = switch (lhs_mcv) { + .register => |lhs_reg| .{ self.register_manager.lockRegAssumeUnused(lhs_reg), null }, + .register_pair => |lhs_regs| locks: { + const locks = self.register_manager.lockRegsAssumeUnused(2, lhs_regs); + break :locks .{ locks[0], locks[1] }; + }, + .register_offset => |lhs_ro| .{ + self.register_manager.lockRegAssumeUnused(lhs_ro.reg), + null, + }, + else => .{null} ** 2, }; - defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); + defer for (lhs_locks) |lhs_lock| if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 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, + const rhs_locks: [2]?RegisterLock = switch (rhs_mcv) { + .register => |rhs_reg| .{ self.register_manager.lockReg(rhs_reg), null }, + .register_pair => |rhs_regs| self.register_manager.lockRegs(2, rhs_regs), + .register_offset => |rhs_ro| .{ self.register_manager.lockReg(rhs_ro.reg), null }, + else => .{null} ** 2, }; - defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + defer for (rhs_locks) |rhs_lock| if (rhs_lock) |lock| self.register_manager.unlockReg(lock); switch (ty.zigTypeTag(mod)) { else => { @@ -10159,7 +10307,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { if (dst_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); - const src_mcv = if (flipped) lhs_mcv else rhs_mcv; + const src_mcv = try self.resolveInst(if (flipped) bin_op.lhs else bin_op.rhs); const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; defer if (src_lock) |lock| self.register_manager.unlockReg(lock); @@ -10191,14 +10339,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { .indirect, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, + .lea_symbol, .reserved_frame, .air_ref, => unreachable, .register_pair, .load_frame => null, - .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => dst: { + .memory, .load_symbol, .load_got, .load_direct, .load_tlv => dst: { switch (resolved_dst_mcv) { .memory => |addr| if (math.cast( i32, @@ -10207,7 +10355,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { i32, @as(i64, @bitCast(addr)) + abi_size - 8, ) != null) break :dst null, - .load_got, .load_extern_got, .load_direct, .load_tlv => {}, + .load_symbol, .load_got, .load_direct, .load_tlv => {}, else => unreachable, } @@ -10248,16 +10396,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { .register_offset, .register_overflow, .indirect, + .lea_symbol, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, .reserved_frame, .air_ref, => unreachable, .register_pair, .load_frame => null, - .memory, .load_got, .load_extern_got, .load_direct, .load_tlv => src: { + .memory, .load_symbol, .load_got, .load_direct, .load_tlv => src: { switch (resolved_src_mcv) { .memory => |addr| if (math.cast( i32, @@ -10266,7 +10414,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { i32, @as(i64, @bitCast(addr)) + abi_size - 8, ) != null) break :src null, - .load_got, .load_extern_got, .load_direct, .load_tlv => {}, + .load_symbol, .load_got, .load_direct, .load_tlv => {}, else => unreachable, } @@ -10350,13 +10498,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { }, ); - if (limb_i > 0) try self.asmRegisterRegister( - .{ ._, .@"or" }, - acc_reg, - tmp_reg, - ); + if (limb_i > 0) + try self.asmRegisterRegister(.{ ._, .@"or" }, acc_reg, tmp_reg); } - try self.asmRegisterRegister(.{ ._, .@"test" }, acc_reg, acc_reg); + assert(limbs_len >= 2); // use flags from or break :result_op flipped_op; }, }; @@ -10692,9 +10837,9 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC .register_overflow, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, + .lea_symbol, .reserved_frame, .air_ref, => unreachable, @@ -10718,8 +10863,8 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC }, .memory, + .load_symbol, .load_got, - .load_extern_got, .load_direct, .load_tlv, => { @@ -11316,7 +11461,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_| break :arg input_mcv, .indirect, .load_frame => break :arg input_mcv, - .load_direct, .load_got, .load_extern_got, .load_tlv => {}, + .load_symbol, .load_direct, .load_got, .load_tlv => {}, else => { const temp_mcv = try self.allocTempRegOrMem(ty, false); try self.genCopy(ty, temp_mcv, input_mcv); @@ -11578,6 +11723,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_got = sym_index }) } else return self.fail("invalid modifier: '{s}'", .{modifier}), + .load_symbol => |sym_index| if (mem.eql(u8, modifier, "P")) + .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .load_symbol = sym_index }) } + else + return self.fail("invalid modifier: '{s}'", .{modifier}), else => return self.fail("invalid constraint: '{s}'", .{op_str}), }; } else if (mem.startsWith(u8, op_str, "$")) { @@ -11798,6 +11947,7 @@ const MoveStrategy = union(enum) { .move => |tag| try self.asmRegisterMemory(tag, dst_reg, src_mem), .x87_load_store => { try self.asmMemory(.{ .f_, .ld }, src_mem); + assert(dst_reg != .st7); try self.asmRegister(.{ .f_p, .st }, @enumFromInt(@intFromEnum(dst_reg) + 1)); }, .insert_extract => |ie| try self.asmRegisterMemoryImmediate( @@ -11831,188 +11981,284 @@ const MoveStrategy = union(enum) { } } }; -fn moveStrategy(self: *Self, ty: Type, aligned: bool) !MoveStrategy { +fn moveStrategy(self: *Self, ty: Type, class: Register.Class, aligned: bool) !MoveStrategy { const mod = self.bin_file.options.module.?; - switch (ty.zigTypeTag(mod)) { - else => return .{ .move = .{ ._, .mov } }, - .Float => switch (ty.floatBits(self.target.*)) { - 16 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ - .insert = .{ .vp_w, .insr }, - .extract = .{ .vp_w, .extr }, - } } else .{ .insert_extract = .{ - .insert = .{ .p_w, .insr }, - .extract = .{ .p_w, .extr }, - } }, - 32 => return .{ .move = if (self.hasFeature(.avx)) .{ .v_ss, .mov } else .{ ._ss, .mov } }, - 64 => return .{ .move = if (self.hasFeature(.avx)) .{ .v_sd, .mov } else .{ ._sd, .mov } }, - 80 => return .x87_load_store, - 128 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } - else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, - else => {}, - }, - .Vector => switch (ty.childType(mod).zigTypeTag(mod)) { - .Bool => return .{ .move = .{ ._, .mov } }, - .Int => switch (ty.childType(mod).intInfo(mod).bits) { - 8 => switch (ty.vectorLen(mod)) { - 1 => if (self.hasFeature(.avx)) return .{ .vex_insert_extract = .{ - .insert = .{ .vp_b, .insr }, - .extract = .{ .vp_b, .extr }, - } } else if (self.hasFeature(.sse4_2)) return .{ .insert_extract = .{ - .insert = .{ .p_b, .insr }, - .extract = .{ .p_b, .extr }, - } }, - 2 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ - .insert = .{ .vp_w, .insr }, - .extract = .{ .vp_w, .extr }, - } } else .{ .insert_extract = .{ - .insert = .{ .p_w, .insr }, - .extract = .{ .p_w, .extr }, - } }, - 3...4 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_d, .mov } - else - .{ ._d, .mov } }, - 5...8 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_q, .mov } - else - .{ ._q, .mov } }, - 9...16 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } - else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, - 17...32 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, - else => {}, - }, - 16 => switch (ty.vectorLen(mod)) { - 1 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ - .insert = .{ .vp_w, .insr }, - .extract = .{ .vp_w, .extr }, - } } else .{ .insert_extract = .{ - .insert = .{ .p_w, .insr }, - .extract = .{ .p_w, .extr }, - } }, - 2 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_d, .mov } - else - .{ ._d, .mov } }, - 3...4 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_q, .mov } - else - .{ ._q, .mov } }, - 5...8 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } - else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, - 9...16 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, - else => {}, - }, - 32 => switch (ty.vectorLen(mod)) { - 1 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_d, .mov } - else - .{ ._d, .mov } }, - 2 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_q, .mov } - else - .{ ._q, .mov } }, - 3...4 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } - else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, - 5...8 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, - else => {}, - }, - 64 => switch (ty.vectorLen(mod)) { - 1 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_q, .mov } - else - .{ ._q, .mov } }, - 2 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } - else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, - 3...4 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, - else => {}, - }, - 128 => switch (ty.vectorLen(mod)) { - 1 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } - else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, - 2 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, - else => {}, - }, - 256 => switch (ty.vectorLen(mod)) { - 1 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, - else => {}, + switch (class) { + .general_purpose, .segment => return .{ .move = .{ ._, .mov } }, + .x87 => return .x87_load_store, + .mmx => {}, + .sse => { + switch (ty.zigTypeTag(mod)) { + else => { + const classes = mem.sliceTo(&abi.classifySystemV(ty, mod, .other), .none); + assert(std.mem.indexOfNone(abi.Class, classes, &.{ + .integer, .sse, .float, .float_combine, + }) == null); + const abi_size = ty.abiSize(mod); + if (abi_size < 4 or + std.mem.indexOfScalar(abi.Class, classes, .integer) != null) switch (abi_size) { + 1 => if (self.hasFeature(.avx)) return .{ .vex_insert_extract = .{ + .insert = .{ .vp_b, .insr }, + .extract = .{ .vp_b, .extr }, + } } else if (self.hasFeature(.sse4_2)) return .{ .insert_extract = .{ + .insert = .{ .p_b, .insr }, + .extract = .{ .p_b, .extr }, + } }, + 2 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ + .insert = .{ .vp_w, .insr }, + .extract = .{ .vp_w, .extr }, + } } else .{ .insert_extract = .{ + .insert = .{ .p_w, .insr }, + .extract = .{ .p_w, .extr }, + } }, + 3...4 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_d, .mov } + else + .{ ._d, .mov } }, + 5...8 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_q, .mov } + else + .{ ._q, .mov } }, + 9...16 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } + else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, + 17...32 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, + else => {}, + } else switch (abi_size) { + 4 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_ss, .mov } + else + .{ ._ss, .mov } }, + 5...8 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_sd, .mov } + else + .{ ._sd, .mov } }, + 9...16 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_pd, .mova } else .{ .v_pd, .movu } + else if (aligned) .{ ._pd, .mova } else .{ ._pd, .movu } }, + 17...32 => if (self.hasFeature(.avx)) return .{ .move = if (aligned) + .{ .v_pd, .mova } + else + .{ .v_pd, .movu } }, + else => {}, + } }, - else => {}, - }, - .Float => switch (ty.childType(mod).floatBits(self.target.*)) { - 16 => switch (ty.vectorLen(mod)) { - 1 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ + .Float => switch (ty.floatBits(self.target.*)) { + 16 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ .insert = .{ .vp_w, .insr }, .extract = .{ .vp_w, .extr }, } } else .{ .insert_extract = .{ .insert = .{ .p_w, .insr }, .extract = .{ .p_w, .extr }, } }, - 2 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_d, .mov } - else - .{ ._d, .mov } }, - 3...4 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_q, .mov } - else - .{ ._q, .mov } }, - 5...8 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } - else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, - 9...16 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, - else => {}, - }, - 32 => switch (ty.vectorLen(mod)) { - 1 => return .{ .move = if (self.hasFeature(.avx)) + 32 => return .{ .move = if (self.hasFeature(.avx)) .{ .v_ss, .mov } else .{ ._ss, .mov } }, - 2 => return .{ .move = if (self.hasFeature(.avx)) - .{ .v_sd, .mov } - else - .{ ._sd, .mov } }, - 3...4 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_ps, .mova } else .{ .v_ps, .movu } - else if (aligned) .{ ._ps, .mova } else .{ ._ps, .movu } }, - 5...8 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_ps, .mova } else .{ .v_ps, .movu } }, - else => {}, - }, - 64 => switch (ty.vectorLen(mod)) { - 1 => return .{ .move = if (self.hasFeature(.avx)) + 64 => return .{ .move = if (self.hasFeature(.avx)) .{ .v_sd, .mov } else .{ ._sd, .mov } }, - 2 => return .{ .move = if (self.hasFeature(.avx)) - if (aligned) .{ .v_pd, .mova } else .{ .v_pd, .movu } - else if (aligned) .{ ._pd, .mova } else .{ ._pd, .movu } }, - 3...4 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_pd, .mova } else .{ .v_pd, .movu } }, - else => {}, - }, - 128 => switch (ty.vectorLen(mod)) { - 1 => return .{ .move = if (self.hasFeature(.avx)) + 128 => return .{ .move = if (self.hasFeature(.avx)) if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, - 2 => if (self.hasFeature(.avx)) - return .{ .move = if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } }, else => {}, }, - else => {}, - }, - else => {}, + .Vector => switch (ty.childType(mod).zigTypeTag(mod)) { + .Bool => return .{ .move = .{ ._, .mov } }, + .Int => switch (ty.childType(mod).intInfo(mod).bits) { + 8 => switch (ty.vectorLen(mod)) { + 1 => if (self.hasFeature(.avx)) return .{ .vex_insert_extract = .{ + .insert = .{ .vp_b, .insr }, + .extract = .{ .vp_b, .extr }, + } } else if (self.hasFeature(.sse4_2)) return .{ .insert_extract = .{ + .insert = .{ .p_b, .insr }, + .extract = .{ .p_b, .extr }, + } }, + 2 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ + .insert = .{ .vp_w, .insr }, + .extract = .{ .vp_w, .extr }, + } } else .{ .insert_extract = .{ + .insert = .{ .p_w, .insr }, + .extract = .{ .p_w, .extr }, + } }, + 3...4 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_d, .mov } + else + .{ ._d, .mov } }, + 5...8 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_q, .mov } + else + .{ ._q, .mov } }, + 9...16 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } + else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, + 17...32 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_, .movdqa } + else + .{ .v_, .movdqu } }, + else => {}, + }, + 16 => switch (ty.vectorLen(mod)) { + 1 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ + .insert = .{ .vp_w, .insr }, + .extract = .{ .vp_w, .extr }, + } } else .{ .insert_extract = .{ + .insert = .{ .p_w, .insr }, + .extract = .{ .p_w, .extr }, + } }, + 2 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_d, .mov } + else + .{ ._d, .mov } }, + 3...4 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_q, .mov } + else + .{ ._q, .mov } }, + 5...8 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } + else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, + 9...16 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_, .movdqa } + else + .{ .v_, .movdqu } }, + else => {}, + }, + 32 => switch (ty.vectorLen(mod)) { + 1 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_d, .mov } + else + .{ ._d, .mov } }, + 2 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_q, .mov } + else + .{ ._q, .mov } }, + 3...4 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } + else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, + 5...8 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_, .movdqa } + else + .{ .v_, .movdqu } }, + else => {}, + }, + 64 => switch (ty.vectorLen(mod)) { + 1 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_q, .mov } + else + .{ ._q, .mov } }, + 2 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } + else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, + 3...4 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_, .movdqa } + else + .{ .v_, .movdqu } }, + else => {}, + }, + 128 => switch (ty.vectorLen(mod)) { + 1 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } + else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, + 2 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_, .movdqa } + else + .{ .v_, .movdqu } }, + else => {}, + }, + 256 => switch (ty.vectorLen(mod)) { + 1 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_, .movdqa } + else + .{ .v_, .movdqu } }, + else => {}, + }, + else => {}, + }, + .Float => switch (ty.childType(mod).floatBits(self.target.*)) { + 16 => switch (ty.vectorLen(mod)) { + 1 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ + .insert = .{ .vp_w, .insr }, + .extract = .{ .vp_w, .extr }, + } } else .{ .insert_extract = .{ + .insert = .{ .p_w, .insr }, + .extract = .{ .p_w, .extr }, + } }, + 2 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_d, .mov } + else + .{ ._d, .mov } }, + 3...4 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_q, .mov } + else + .{ ._q, .mov } }, + 5...8 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } + else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, + 9...16 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_, .movdqa } + else + .{ .v_, .movdqu } }, + else => {}, + }, + 32 => switch (ty.vectorLen(mod)) { + 1 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_ss, .mov } + else + .{ ._ss, .mov } }, + 2 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_sd, .mov } + else + .{ ._sd, .mov } }, + 3...4 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_ps, .mova } else .{ .v_ps, .movu } + else if (aligned) .{ ._ps, .mova } else .{ ._ps, .movu } }, + 5...8 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_ps, .mova } + else + .{ .v_ps, .movu } }, + else => {}, + }, + 64 => switch (ty.vectorLen(mod)) { + 1 => return .{ .move = if (self.hasFeature(.avx)) + .{ .v_sd, .mov } + else + .{ ._sd, .mov } }, + 2 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_pd, .mova } else .{ .v_pd, .movu } + else if (aligned) .{ ._pd, .mova } else .{ ._pd, .movu } }, + 3...4 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_pd, .mova } + else + .{ .v_pd, .movu } }, + else => {}, + }, + 128 => switch (ty.vectorLen(mod)) { + 1 => return .{ .move = if (self.hasFeature(.avx)) + if (aligned) .{ .v_, .movdqa } else .{ .v_, .movdqu } + else if (aligned) .{ ._, .movdqa } else .{ ._, .movdqu } }, + 2 => if (self.hasFeature(.avx)) + return .{ .move = if (aligned) + .{ .v_, .movdqa } + else + .{ .v_, .movdqu } }, + else => {}, + }, + else => {}, + }, + else => {}, + }, + } }, } return self.fail("TODO moveStrategy for {}", .{ty.fmt(mod)}); @@ -12034,9 +12280,9 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError .register_overflow, .lea_direct, .lea_got, - .lea_extern_got, .lea_tlv, .lea_frame, + .lea_symbol, .reserved_frame, .air_ref, => unreachable, // unmodifiable destination @@ -12060,37 +12306,58 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError } }, }), .register_pair => |dst_regs| { - switch (src_mcv) { + const src_info: ?struct { addr_reg: Register, addr_lock: RegisterLock } = switch (src_mcv) { + .register_pair, .memory, .indirect, .load_frame => null, + .load_symbol, .load_direct, .load_got, .load_tlv => src: { + const src_addr_reg = + (try self.register_manager.allocReg(null, abi.RegisterClass.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, src_mcv.address()); + break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock }; + }, .air_ref => |src_ref| return self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)), - else => {}, - } + else => return self.fail("TODO implement genCopy for {s} of {}", .{ + @tagName(src_mcv), ty.fmt(mod), + }), + }; + defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock); + const classes = mem.sliceTo(&abi.classifySystemV(ty, mod, .other), .none); for (dst_regs, classes, 0..) |dst_reg, class, dst_reg_i| { const class_ty = switch (class) { .integer => Type.usize, - .sse => Type.f64, + .sse, .float, .float_combine => Type.f64, else => unreachable, }; + const off: i32 = @intCast(dst_reg_i * 8); switch (src_mcv) { .register_pair => |src_regs| try self.genSetReg( dst_reg, class_ty, .{ .register = src_regs[dst_reg_i] }, ), - else => try self.genSetReg( + .memory, .indirect, .load_frame => try self.genSetReg( + dst_reg, + class_ty, + src_mcv.address().offset(off).deref(), + ), + .load_symbol, .load_direct, .load_got, .load_tlv => try self.genSetReg( dst_reg, class_ty, - src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(), + .{ .indirect = .{ .reg = src_info.?.addr_reg, .off = off } }, ), + else => unreachable, } } }, .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv), - .memory, .load_direct, .load_got, .load_extern_got, .load_tlv => { + .memory, .load_symbol, .load_direct, .load_got, .load_tlv => { switch (dst_mcv) { .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv), - .load_direct, .load_got, .load_extern_got, .load_tlv => {}, + .load_symbol, .load_direct, .load_got, .load_tlv => {}, else => unreachable, } @@ -12157,6 +12424,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr registerAlias(dst_reg, abi_size), src_reg, ), + .x87, .mmx => unreachable, .sse => try self.asmRegisterRegister( switch (abi_size) { 1...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov }, @@ -12166,17 +12434,30 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr registerAlias(dst_reg, @max(abi_size, 4)), src_reg.to128(), ), - .x87, .mmx => unreachable, }, .segment => try self.asmRegisterRegister( .{ ._, .mov }, dst_reg, switch (src_reg.class()) { .general_purpose, .segment => registerAlias(src_reg, abi_size), - .sse => try self.copyToTmpRegister(ty, src_mcv), .x87, .mmx => unreachable, + .sse => try self.copyToTmpRegister(ty, src_mcv), }, ), + .x87 => switch (src_reg.class()) { + .general_purpose, .segment => unreachable, + .x87 => switch (src_reg) { + .st0 => try self.asmRegister(.{ .f_, .st }, dst_reg), + .st1, .st2, .st3, .st4, .st5, .st6 => { + try self.asmRegister(.{ .f_, .ld }, src_reg); + assert(dst_reg != .st7); + try self.asmRegister(.{ .f_p, .st }, @enumFromInt(@intFromEnum(dst_reg) + 1)); + }, + else => unreachable, + }, + .mmx, .sse => unreachable, + }, + .mmx => unreachable, .sse => switch (src_reg.class()) { .general_purpose => try self.asmRegisterRegister( switch (abi_size) { @@ -12192,6 +12473,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr ty, .{ .register = try self.copyToTmpRegister(ty, src_mcv) }, ), + .x87, .mmx => unreachable, .sse => try self.asmRegisterRegister( @as(?Mir.Inst.FixedTag, switch (ty.scalarType(mod).zigTypeTag(mod)) { else => switch (abi_size) { @@ -12217,9 +12499,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr registerAlias(dst_reg, abi_size), registerAlias(src_reg, abi_size), ), - .x87, .mmx => unreachable, }, - .x87, .mmx => unreachable, }, .register_pair => |src_regs| try self.genSetReg(dst_reg, ty, .{ .register = src_regs[0] }), .register_offset, @@ -12231,9 +12511,10 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr 0 => return self.genSetReg(dst_reg, ty, .{ .register = reg_off.reg }), else => .{ .move = .{ ._, .lea } }, }, - .indirect => try self.moveStrategy(ty, false), + .indirect => try self.moveStrategy(ty, dst_reg.class(), false), .load_frame => |frame_addr| try self.moveStrategy( ty, + dst_reg.class(), self.getFrameAddrAlignment(frame_addr).compare(.gte, ty.abiAlignment(mod)), ), .lea_frame => .{ .move = .{ ._, .lea } }, @@ -12252,18 +12533,19 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr else => unreachable, }, )), - .memory, .load_direct, .load_got, .load_extern_got, .load_tlv => { + .memory, .load_symbol, .load_direct, .load_got, .load_tlv => { switch (src_mcv) { .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| return (try self.moveStrategy( ty, + dst_reg.class(), ty.abiAlignment(mod).check(@as(u32, @bitCast(small_addr))), )).read(self, registerAlias(dst_reg, abi_size), Memory.sib( self.memPtrSize(ty), .{ .base = .{ .reg = .ds }, .disp = small_addr }, )), - .load_direct => |sym_index| switch (ty.zigTypeTag(mod)) { - else => { + .load_direct => |sym_index| switch (dst_reg.class()) { + .general_purpose => { const atom_index = try self.owner.getSymbolIndex(self); _ = try self.addInst(.{ .tag = .mov, @@ -12278,9 +12560,10 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr }); return; }, - .Float, .Vector => {}, + .segment, .mmx => unreachable, + .x87, .sse => {}, }, - .load_got, .load_extern_got, .load_tlv => {}, + .load_symbol, .load_got, .load_tlv => {}, else => unreachable, } @@ -12288,34 +12571,47 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_lock); - try (try self.moveStrategy(ty, false)).read( + try (try self.moveStrategy(ty, dst_reg.class(), false)).read( self, registerAlias(dst_reg, abi_size), Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .{ .reg = addr_reg } }), ); }, - .lea_direct, .lea_got, .lea_extern_got => |sym_index| { + .lea_symbol, .lea_direct, .lea_got => |sym_index| { const atom_index = try self.owner.getSymbolIndex(self); - _ = try self.addInst(.{ - .tag = switch (src_mcv) { - .lea_direct => .lea, - .lea_got, .lea_extern_got => .mov, - else => unreachable, - }, - .ops = switch (src_mcv) { - .lea_direct => .direct_reloc, - .lea_got => .got_reloc, - .lea_extern_got => .extern_got_reloc, - else => unreachable, - }, - .data = .{ .rx = .{ - .r1 = dst_reg.to64(), - .payload = try self.addExtra(Mir.Reloc{ - .atom_index = atom_index, - .sym_index = sym_index, - }), - } }, - }); + if (self.bin_file.cast(link.File.Elf)) |_| { + _ = try self.addInst(.{ + .tag = .lea, + .ops = .linker_reloc, + .data = .{ .rx = .{ + .r1 = dst_reg.to64(), + .payload = try self.addExtra(Mir.Reloc{ + .atom_index = atom_index, + .sym_index = sym_index, + }), + } }, + }); + } else { + _ = try self.addInst(.{ + .tag = switch (src_mcv) { + .lea_direct => .lea, + .lea_got => .mov, + else => unreachable, + }, + .ops = switch (src_mcv) { + .lea_direct => .direct_reloc, + .lea_got => .got_reloc, + else => unreachable, + }, + .data = .{ .rx = .{ + .r1 = dst_reg.to64(), + .payload = try self.addExtra(Mir.Reloc{ + .atom_index = atom_index, + .sym_index = sym_index, + }), + } }, + }); + } }, .lea_tlv => |sym_index| { const atom_index = try self.owner.getSymbolIndex(self); @@ -12393,7 +12689,7 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal }, }, .eflags => |cc| try self.asmSetccMemory(cc, Memory.sib(.byte, .{ .base = base, .disp = disp })), - .register => |src_reg| try (try self.moveStrategy(ty, switch (base) { + .register => |src_reg| try (try self.moveStrategy(ty, src_reg.class(), 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))), @@ -12408,17 +12704,21 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal registerAlias(src_reg, abi_size), ), .register_pair => |src_regs| for (src_regs, 0..) |src_reg, src_reg_i| { - const part_size = @min(abi_size - src_reg_i * 8, 8); - try (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, + const part_size: u16 = @min(abi_size - src_reg_i * 8, 8); + try (try self.moveStrategy( + try mod.intType(.unsigned, part_size * 8), + src_reg.class(), + 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)), }, - .frame => |frame_index| self.getFrameAddrAlignment( - .{ .index = frame_index, .off = disp }, - ).compare(.gte, ty.abiAlignment(mod)), - })).write(self, Memory.sib( + )).write(self, Memory.sib( Memory.PtrSize.fromSize(part_size), .{ .base = base, .disp = disp + @as(i32, @intCast(src_reg_i * 8)) }, ), registerAlias(src_reg, part_size)); @@ -12460,12 +12760,12 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal .lea_direct, .load_got, .lea_got, - .load_extern_got, - .lea_extern_got, .load_tlv, .lea_tlv, .load_frame, .lea_frame, + .load_symbol, + .lea_symbol, => switch (abi_size) { 0 => {}, 1, 2, 4, 8 => { @@ -12482,9 +12782,9 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal } fn genInlineMemcpy(self: *Self, dst_ptr: MCValue, src_ptr: MCValue, len: MCValue) InnerError!void { - try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); - try self.genSetReg(.rdi, Type.usize, dst_ptr); + try self.spillRegisters(&.{ .rsi, .rdi, .rcx }); try self.genSetReg(.rsi, Type.usize, src_ptr); + try self.genSetReg(.rdi, Type.usize, dst_ptr); try self.genSetReg(.rcx, Type.usize, len); try self.asmOpOnly(.{ .@"rep _sb", .mov }); } @@ -12556,8 +12856,8 @@ fn genLazySymbolRef( if (self.bin_file.options.pic) { switch (tag) { - .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym.esym_index }), - .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym.esym_index }), + .lea, .call => try self.genSetReg(reg, Type.usize, .{ .load_symbol = sym.esym_index }), + .mov => try self.genSetReg(reg, Type.usize, .{ .load_symbol = sym.esym_index }), else => unreachable, } switch (tag) { @@ -12573,7 +12873,7 @@ fn genLazySymbolRef( switch (tag) { .lea, .mov => _ = try self.addInst(.{ .tag = .mov, - .ops = .direct_got_reloc, + .ops = .linker_reloc, .data = .{ .rx = .{ .r1 = reg.to64(), .payload = try self.addExtra(reloc), @@ -12581,20 +12881,11 @@ fn genLazySymbolRef( }), .call => _ = try self.addInst(.{ .tag = .call, - .ops = .direct_got_reloc, + .ops = .linker_reloc, .data = .{ .reloc = reloc }, }), else => unreachable, } - switch (tag) { - .lea, .call => {}, - .mov => try self.asmRegisterMemory( - .{ ._, tag }, - reg.to64(), - Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }), - ), - else => unreachable, - } } } else if (self.bin_file.cast(link.File.Plan9)) |p9_file| { const atom_index = p9_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| @@ -14481,10 +14772,11 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue { } else mcv: { const ip_index = Air.refToInterned(ref).?; const gop = try self.const_tracking.getOrPut(self.gpa, ip_index); - if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(try self.genTypedValue(.{ + const mcv = try self.genTypedValue(.{ .ty = ty, .val = ip_index.toValue(), - })); + }); + if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(mcv); break :mcv gop.value_ptr.short; }; @@ -14531,9 +14823,9 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { .undef => .undef, .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, + .load_symbol => |sym_index| .{ .load_symbol = sym_index }, .load_direct => |sym_index| .{ .load_direct = sym_index }, .load_got => |sym_index| .{ .lea_got = sym_index }, - .load_extern_got => |sym_index| .{ .lea_extern_got = sym_index }, .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, }, .fail => |msg| { @@ -14730,16 +15022,22 @@ fn resolveCallingConventionValues( arg_mcv_i += 1; }, .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; + .x87, .x87up, .complex_x87, .memory, .win_i128 => switch (resolved_cc) { + .SysV => switch (class) { + .x87, .x87up, .complex_x87, .memory => break, + else => unreachable, + }, + .Win64 => if (ty.abiSize(mod) > 8) { + 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 break, + else => unreachable, }, + .none => unreachable, } else { arg.* = switch (arg_mcv_i) { else => unreachable, @@ -15017,34 +15315,32 @@ fn floatLibcAbiSuffix(ty: Type) []const u8 { }; } -fn promoteVarArg(self: *Self, ty: Type) Type { +fn promoteInt(self: *Self, ty: Type) Type { const mod = self.bin_file.options.module.?; - switch (ty.zigTypeTag(mod)) { - .Bool => return Type.c_int, - else => { - const int_info = ty.intInfo(mod); - for ([_]Type{ - Type.c_int, Type.c_uint, - Type.c_long, Type.c_ulong, - Type.c_longlong, Type.c_ulonglong, - }) |promote_ty| { - const promote_info = promote_ty.intInfo(mod); - if (int_info.signedness == .signed and promote_info.signedness == .unsigned) continue; - if (int_info.bits + @intFromBool(int_info.signedness == .unsigned and - promote_info.signedness == .signed) <= promote_info.bits) return promote_ty; - } - unreachable; - }, - .Float => switch (ty.floatBits(self.target.*)) { - 32, 64 => return Type.f64, - else => |float_bits| { - assert(float_bits == self.target.c_type_bit_size(.longdouble)); - return Type.c_longdouble; - }, - }, - .Pointer => { - assert(!ty.isSlice(mod)); - return ty; + const int_info: InternPool.Key.IntType = switch (ty.toIntern()) { + .bool_type => .{ .signedness = .unsigned, .bits = 1 }, + else => if (ty.isAbiInt(mod)) ty.intInfo(mod) else return ty, + }; + for ([_]Type{ + Type.c_int, Type.c_uint, + Type.c_long, Type.c_ulong, + Type.c_longlong, Type.c_ulonglong, + }) |promote_ty| { + const promote_info = promote_ty.intInfo(mod); + if (int_info.signedness == .signed and promote_info.signedness == .unsigned) continue; + if (int_info.bits + @intFromBool(int_info.signedness == .unsigned and + promote_info.signedness == .signed) <= promote_info.bits) return promote_ty; + } + return ty; +} + +fn promoteVarArg(self: *Self, ty: Type) Type { + if (!ty.isRuntimeFloat()) return self.promoteInt(ty); + switch (ty.floatBits(self.target.*)) { + 32, 64 => return Type.f64, + else => |float_bits| { + assert(float_bits == self.target.c_type_bit_size(.longdouble)); + return Type.c_longdouble; }, } } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index ea00a0b627..e599e7c2a7 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -78,31 +78,41 @@ pub fn emitMir(emit: *Emit) Error!void { } else return emit.fail("TODO implement extern reloc for {s}", .{ @tagName(emit.bin_file.tag), }), + .linker_reloc => |data| if (emit.bin_file.cast(link.File.Elf)) |elf_file| { + const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; + const sym = elf_file.symbol(elf_file.zigModulePtr().symbol(data.sym_index)); + if (emit.bin_file.options.pic) { + const r_type: u32 = if (sym.flags.has_zig_got) + link.File.Elf.R_X86_64_ZIG_GOTPCREL + else if (sym.flags.needs_got) + std.elf.R_X86_64_GOTPCREL + else + std.elf.R_X86_64_PC32; + try atom.addReloc(elf_file, .{ + .r_offset = end_offset - 4, + .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, + .r_addend = -4, + }); + } else { + const r_type: u32 = if (sym.flags.has_zig_got) + link.File.Elf.R_X86_64_ZIG_GOT32 + else if (sym.flags.needs_got) + std.elf.R_X86_64_GOT32 + else + std.elf.R_X86_64_32; + try atom.addReloc(elf_file, .{ + .r_offset = end_offset - 4, + .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, + .r_addend = 0, + }); + } + } else unreachable, .linker_got, - .linker_extern_got, .linker_direct, - .linker_direct_got, .linker_import, .linker_tlv, - => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| { - const r_type: u32 = switch (lowered_relocs[0].target) { - .linker_direct_got => link.File.Elf.R_X86_64_ZIG_GOT32, - .linker_got => link.File.Elf.R_X86_64_ZIG_GOTPCREL, - .linker_extern_got => std.elf.R_X86_64_GOTPCREL, - .linker_direct => std.elf.R_X86_64_PC32, - else => unreachable, - }; - const r_addend: i64 = switch (lowered_relocs[0].target) { - .linker_direct_got => 0, - .linker_got, .linker_extern_got, .linker_direct => -4, - else => unreachable, - }; - const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; - try atom_ptr.addReloc(elf_file, .{ - .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type, - .r_addend = r_addend, - }); + => |symbol| if (emit.bin_file.cast(link.File.Elf)) |_| { + unreachable; } else if (emit.bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 5ac3c3a72c..7115c34eff 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -1,5 +1,6 @@ //! This file contains the functionality for lowering x86_64 MIR to Instructions +bin_file: *link.File, allocator: Allocator, mir: Mir, cc: std.builtin.CallingConvention, @@ -49,11 +50,10 @@ pub const Reloc = struct { const Target = union(enum) { inst: Mir.Inst.Index, + linker_reloc: Mir.Reloc, linker_extern_fn: Mir.Reloc, linker_got: Mir.Reloc, - linker_extern_got: Mir.Reloc, linker_direct: Mir.Reloc, - linker_direct_got: Mir.Reloc, linker_import: Mir.Reloc, linker_tlv: Mir.Reloc, }; @@ -408,12 +408,57 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .m_sib, .m_rip, .rax_moffs, .moffs_rax => inst.data.x.fixes, .extern_fn_reloc, .got_reloc, - .extern_got_reloc, .direct_reloc, - .direct_got_reloc, .import_reloc, .tlv_reloc, => ._, + .linker_reloc => { + if (lower.bin_file.options.pic) { + assert(inst.data.rx.fixes == ._); + const reg = inst.data.rx.r1; + const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data; + _ = lower.reloc(.{ .linker_reloc = extra }); + const mnemonic: Mnemonic = switch (inst.tag) { + .mov => .mov, + .lea => .lea, + else => unreachable, + }; + try lower.emit(.none, mnemonic, &.{ + .{ .reg = reg }, + .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, + }); + } else { + switch (inst.tag) { + .call => { + _ = lower.reloc(.{ .linker_reloc = inst.data.reloc }); + try lower.emit(.none, .call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) }, + }); + }, + .lea => { + assert(inst.data.rx.fixes == ._); + const reg = inst.data.rx.r1; + const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data; + try lower.emit(.none, .mov, &.{ + .{ .reg = reg }, + .{ .imm = lower.reloc(.{ .linker_reloc = extra }) }, + }); + }, + .mov => { + assert(inst.data.rx.fixes == ._); + const reg = inst.data.rx.r1; + const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data; + _ = lower.reloc(.{ .linker_reloc = extra }); + try lower.emit(.none, .mov, &.{ + .{ .reg = reg }, + .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) }, + }); + }, + else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), + } + } + return; + }, else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}), }; try lower.emit(switch (fixes) { @@ -545,32 +590,12 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .extern_fn_reloc => &.{ .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) }, }, - .direct_got_reloc => ops: { - switch (inst.tag) { - .call => { - _ = lower.reloc(.{ .linker_direct_got = inst.data.reloc }); - break :ops &.{ - .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) }, - }; - }, - .mov => { - const reg = inst.data.rx.r1; - const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data; - _ = lower.reloc(.{ .linker_direct_got = extra }); - break :ops &.{ - .{ .reg = reg }, - .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) }, - }; - }, - else => unreachable, - } - }, - .got_reloc, .extern_got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: { + .linker_reloc => unreachable, + .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: { const reg = inst.data.rx.r1; const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data; _ = lower.reloc(switch (inst.ops) { .got_reloc => .{ .linker_got = extra }, - .extern_got_reloc => .{ .linker_extern_got = extra }, .direct_reloc => .{ .linker_direct = extra }, .import_reloc => .{ .linker_import = extra }, .tlv_reloc => .{ .linker_tlv = extra }, @@ -601,6 +626,7 @@ const abi = @import("abi.zig"); const assert = std.debug.assert; const bits = @import("bits.zig"); const encoder = @import("encoder.zig"); +const link = @import("../../link.zig"); const std = @import("std"); const Air = @import("../../Air.zig"); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 8a83510fb8..5cc1be8b62 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -854,9 +854,6 @@ pub const Inst = struct { /// Linker relocation - GOT indirection. /// Uses `rx` payload with extra data of type `Reloc`. got_reloc, - /// Linker relocation - reference to an extern variable via GOT. - /// Uses `rx` payload with extra data of type `Reloc`. - extern_got_reloc, /// Linker relocation - direct reference. /// Uses `rx` payload with extra data of type `Reloc`. direct_reloc, @@ -866,9 +863,9 @@ pub const Inst = struct { /// Linker relocation - threadlocal variable via GOT indirection. /// Uses `rx` payload with extra data of type `Reloc`. tlv_reloc, - /// Linker relocation - non-PIC direct reference to GOT cell. - /// Uses `reloc` payload if tag is `call`, `rx` otherwise. - direct_got_reloc, + /// Linker relocation. + /// Uses `rx` payload with extra data of type `Reloc`. + linker_reloc, // Pseudo instructions: diff --git a/src/codegen.zig b/src/codegen.zig index 051c2b33b3..4db32df16f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -791,13 +791,11 @@ fn lowerDeclRef( /// Helper struct to denote that the value is in memory but requires a linker relocation fixup: /// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc) -/// * extern_got - pointer to extern variable referenced via GOT /// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc) /// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc) pub const LinkerLoad = struct { type: enum { got, - extern_got, direct, import, }, @@ -827,8 +825,9 @@ pub const GenResult = union(enum) { load_got: u32, /// Direct by-address reference to memory location. memory: u64, - /// Pointer to extern variable via GOT. - load_extern_got: u32, + /// Reference to memory location but deferred until linker allocated the Decl in memory. + /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. + load_symbol: u32, }; fn mcv(val: MCValue) GenResult { @@ -903,16 +902,14 @@ fn genDeclRef( mod.intern_pool.stringToSliceUnwrap(ov.lib_name) else null; - return GenResult.mcv(.{ .load_extern_got = try elf_file.getGlobalSymbol(name, lib_name) }); + const sym_index = try elf_file.getGlobalSymbol(name, lib_name); + elf_file.symbol(elf_file.zigModulePtr().symbol(sym_index)).flags.needs_got = true; + return GenResult.mcv(.{ .load_symbol = sym_index }); } const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index); const sym = elf_file.symbol(sym_index); _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); - if (bin_file.options.pic) { - return GenResult.mcv(.{ .load_got = sym.esym_index }); - } else { - return GenResult.mcv(.{ .memory = sym.zigGotAddress(elf_file) }); - } + return GenResult.mcv(.{ .load_symbol = sym.esym_index }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; @@ -948,11 +945,7 @@ fn genUnnamedConst( }; if (bin_file.cast(link.File.Elf)) |elf_file| { const local = elf_file.symbol(local_sym_index); - if (bin_file.options.pic) { - return GenResult.mcv(.{ .load_direct = local.esym_index }); - } else { - return GenResult.mcv(.{ .memory = local.value }); - } + return GenResult.mcv(.{ .load_symbol = local.esym_index }); } else if (bin_file.cast(link.File.MachO)) |_| { return GenResult.mcv(.{ .load_direct = local_sym_index }); } else if (bin_file.cast(link.File.Coff)) |_| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 42bd23a2c8..f5d6a78298 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3333,7 +3333,8 @@ pub fn updateDecl( const variable = decl.getOwnedVariable(mod).?; const name = mod.intern_pool.stringToSlice(decl.name); const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); - _ = try self.getGlobalSymbol(name, lib_name); + const esym_index = try self.getGlobalSymbol(name, lib_name); + self.symbol(self.zigModulePtr().symbol(esym_index)).flags.needs_got = true; return; } @@ -5955,6 +5956,12 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 return lookup_gop.value_ptr.*; } +pub fn zigModulePtr(self: *Elf) *ZigModule { + assert(self.zig_module_index != null); + const file_ptr = self.file(self.zig_module_index.?).?; + return file_ptr.zig_module; +} + const GetOrCreateComdatGroupOwnerResult = struct { found_existing: bool, index: ComdatGroupOwner.Index, diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index c4dc8fd65b..abfaf3b337 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -370,6 +370,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); }, + elf.R_X86_64_GOT32, elf.R_X86_64_GOTPC32, elf.R_X86_64_GOTPC64, elf.R_X86_64_GOTPCREL, @@ -879,6 +880,8 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { } }, + elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A))), + // Zig custom relocations Elf.R_X86_64_ZIG_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(ZIG_GOT + A))), Elf.R_X86_64_ZIG_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(ZIG_GOT + A - P))), |
