Skip to content

Commit bbf8abf

Browse files
committed
x86_64: support rip-relative addressing to labels in inline asm
1 parent f4a31be commit bbf8abf

File tree

6 files changed

+61
-16
lines changed

6 files changed

+61
-16
lines changed

src/arch/x86_64/CodeGen.zig

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103178,6 +103178,19 @@ fn performReloc(self: *CodeGen, reloc: Mir.Inst.Index) void {
103178103178
.pseudo_j_z_and_np_inst, .pseudo_j_nz_or_p_inst => {},
103179103179
else => unreachable,
103180103180
},
103181+
.lea => switch (self.mir_instructions.items(.ops)[reloc]) {
103182+
.rm => {
103183+
const rx = self.mir_instructions.items(.data)[reloc].rx;
103184+
assert(rx.fixes == ._);
103185+
const mem_info: Mir.Memory.Info = @bitCast(
103186+
self.mir_extra.items[rx.payload + std.meta.fieldIndex(Mir.Memory, "info").?],
103187+
);
103188+
assert(mem_info.base == .rip_inst);
103189+
self.mir_extra.items[rx.payload + std.meta.fieldIndex(Mir.Memory, "base").?] = next_inst;
103190+
return;
103191+
},
103192+
else => unreachable,
103193+
},
103181103194
else => unreachable,
103182103195
}
103183103196
self.mir_instructions.items(.data)[reloc].inst.inst = next_inst;
@@ -103461,7 +103474,10 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
103461103474
// for the string, we still use the next u32 for the null terminator.
103462103475
extra_i += clobber.len / 4 + 1;
103463103476

103464-
if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory")) {
103477+
if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory") or
103478+
std.mem.eql(u8, clobber, "fpsr") or std.mem.eql(u8, clobber, "fpcr") or
103479+
std.mem.eql(u8, clobber, "mxcsr") or std.mem.eql(u8, clobber, "dirflag"))
103480+
{
103465103481
// ok, sure
103466103482
} else if (std.mem.eql(u8, clobber, "cc") or
103467103483
std.mem.eql(u8, clobber, "flags") or
@@ -103726,7 +103742,24 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
103726103742
else
103727103743
.@"1";
103728103744
if (sib_it.next()) |_| return self.fail("invalid memory operand: '{s}'", .{op_str});
103729-
op.* = if (std.mem.eql(u8, base_str, "%%dx") and index_str.len == 0) .{ .reg = .dx } else .{ .mem = .{
103745+
op.* = if (std.mem.eql(u8, base_str, "%%dx") and index_str.len == 0)
103746+
.{ .reg = .dx }
103747+
else if (std.mem.eql(u8, base_str, "%%rip") and index_str.len == 0 and
103748+
Label.isValid(.reference, op_str[0..open]))
103749+
op: {
103750+
const anon = std.ascii.isDigit(op_str[0]);
103751+
const label_gop = try labels.getOrPut(self.gpa, op_str[0..if (anon) 1 else open]);
103752+
if (anon and (op_str[1] == 'b' or op_str[1] == 'B') and !label_gop.found_existing)
103753+
return self.fail("undefined label: '{s}'", .{op_str});
103754+
if (!label_gop.found_existing) label_gop.value_ptr.* = .{};
103755+
const pending_relocs = &label_gop.value_ptr.pending_relocs;
103756+
if (if (anon)
103757+
op_str[1] == 'f' or op_str[1] == 'F'
103758+
else
103759+
!label_gop.found_existing or pending_relocs.items.len > 0)
103760+
try pending_relocs.append(self.gpa, @intCast(self.mir_instructions.len));
103761+
break :op .{ .mem = .{ .base = .{ .rip_inst = label_gop.value_ptr.target } } };
103762+
} else .{ .mem = .{
103730103763
.base = if (base_str.len > 0)
103731103764
.{ .reg = parseRegName(base_str["%%".len..]) orelse
103732103765
return self.fail("invalid base register: '{s}'", .{base_str}) }
@@ -103770,9 +103803,9 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
103770103803
} else if (Label.isValid(.reference, op_str)) {
103771103804
const anon = std.ascii.isDigit(op_str[0]);
103772103805
const label_gop = try labels.getOrPut(self.gpa, op_str[0..if (anon) 1 else op_str.len]);
103773-
if (!label_gop.found_existing) label_gop.value_ptr.* = .{};
103774103806
if (anon and (op_str[1] == 'b' or op_str[1] == 'B') and !label_gop.found_existing)
103775103807
return self.fail("undefined label: '{s}'", .{op_str});
103808+
if (!label_gop.found_existing) label_gop.value_ptr.* = .{};
103776103809
const pending_relocs = &label_gop.value_ptr.pending_relocs;
103777103810
if (if (anon)
103778103811
op_str[1] == 'f' or op_str[1] == 'F'
@@ -105008,7 +105041,7 @@ fn genSetMem(
105008105041
.none => .{ .immediate = @bitCast(@as(i64, disp)) },
105009105042
.reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } },
105010105043
.frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } },
105011-
.table => unreachable,
105044+
.table, .rip_inst => unreachable,
105012105045
.reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } },
105013105046
};
105014105047
switch (src_mcv) {
@@ -105118,7 +105151,7 @@ fn genSetMem(
105118105151
.index = frame_index,
105119105152
.off = disp,
105120105153
}).compare(.gte, src_align),
105121-
.table => unreachable,
105154+
.table, .rip_inst => unreachable,
105122105155
.reloc => false,
105123105156
})).write(
105124105157
self,
@@ -105767,7 +105800,7 @@ fn airCmpxchg(self: *CodeGen, inst: Air.Inst.Index) !void {
105767105800
const ptr_lock = switch (ptr_mem.base) {
105768105801
.none, .frame, .reloc => null,
105769105802
.reg => |reg| self.register_manager.lockReg(reg),
105770-
.table => unreachable,
105803+
.table, .rip_inst => unreachable,
105771105804
};
105772105805
defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
105773105806

@@ -105850,7 +105883,7 @@ fn atomicOp(
105850105883
const mem_lock = switch (ptr_mem.base) {
105851105884
.none, .frame, .reloc => null,
105852105885
.reg => |reg| self.register_manager.lockReg(reg),
105853-
.table => unreachable,
105886+
.table, .rip_inst => unreachable,
105854105887
};
105855105888
defer if (mem_lock) |lock| self.register_manager.unlockReg(lock);
105856105889

src/arch/x86_64/Emit.zig

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,14 @@ pub fn emitMir(emit: *Emit) Error!void {
9797
op_index -= 1;
9898
const op = lowered_inst.encoding.data.ops[op_index];
9999
if (op == .none) continue;
100-
const enc_length: u4 = @intCast(
101-
std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable,
102-
);
100+
const is_mem = op.isMemory();
101+
const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) {
102+
.rip_inst => 4,
103+
else => unreachable,
104+
} else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable);
103105
reloc_offset -= enc_length;
104-
if (op_index == lowered_relocs[0].op_index)
105-
break :reloc_offset_length .{ reloc_offset, enc_length };
106+
if (op_index == lowered_relocs[0].op_index) break :reloc_offset_length .{ reloc_offset, enc_length };
107+
std.debug.assert(!is_mem);
106108
}
107109
};
108110
try relocs.append(emit.lower.allocator, .{
@@ -434,7 +436,7 @@ pub fn emitMir(emit: *Emit) Error!void {
434436
loc_buf[0] = switch (mem.base()) {
435437
.none => .{ .constu = 0 },
436438
.reg => |reg| .{ .breg = reg.dwarfNum() },
437-
.frame, .table => unreachable,
439+
.frame, .table, .rip_inst => unreachable,
438440
.reloc => |sym_index| .{ .addr = .{ .sym = sym_index } },
439441
};
440442
break :base &loc_buf[0];

src/arch/x86_64/Lower.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ pub fn mem(lower: *Lower, op_index: InstOpIndex, payload: u32) Memory {
395395
.sib => |*sib| switch (sib.base) {
396396
else => {},
397397
.table => sib.disp = lower.reloc(op_index, .table, sib.disp).signed,
398+
.rip_inst => |inst_index| sib.disp = lower.reloc(op_index, .{ .inst = inst_index }, sib.disp).signed,
398399
},
399400
else => {},
400401
}

src/arch/x86_64/Mir.zig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,7 @@ pub const Memory = struct {
17421742
.reg => |reg| @intFromEnum(reg),
17431743
.frame => |frame_index| @intFromEnum(frame_index),
17441744
.reloc => |sym_index| sym_index,
1745+
.rip_inst => |inst_index| inst_index,
17451746
},
17461747
.off = switch (mem.mod) {
17471748
.rm => |rm| @bitCast(rm.disp),
@@ -1769,6 +1770,7 @@ pub const Memory = struct {
17691770
.frame => .{ .frame = @enumFromInt(mem.base) },
17701771
.table => .table,
17711772
.reloc => .{ .reloc = mem.base },
1773+
.rip_inst => .{ .rip_inst = mem.base },
17721774
},
17731775
.scale_index = switch (mem.info.index) {
17741776
.none => null,
@@ -1832,7 +1834,7 @@ pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffse
18321834

18331835
pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
18341836
return switch (mem.info.base) {
1835-
.none, .reg, .table, .reloc => mem,
1837+
.none, .reg, .table, .reloc, .rip_inst => mem,
18361838
.frame => if (mir.frame_locs.len > 0) .{
18371839
.info = .{
18381840
.base = .reg,

src/arch/x86_64/bits.zig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const expect = std.testing.expect;
44

55
const Allocator = std.mem.Allocator;
66
const ArrayList = std.ArrayList;
7+
const Mir = @import("Mir.zig");
78

89
/// EFLAGS condition codes
910
pub const Condition = enum(u5) {
@@ -678,12 +679,13 @@ pub const Memory = struct {
678679
frame: FrameIndex,
679680
table,
680681
reloc: u32,
682+
rip_inst: Mir.Inst.Index,
681683

682684
pub const Tag = @typeInfo(Base).@"union".tag_type.?;
683685

684686
pub fn isExtended(self: Base) bool {
685687
return switch (self) {
686-
.none, .frame, .table, .reloc => false, // rsp, rbp, and rip are not extended
688+
.none, .frame, .table, .reloc, .rip_inst => false, // rsp, rbp, and rip are not extended
687689
.reg => |reg| reg.isExtended(),
688690
};
689691
}

src/arch/x86_64/encoder.zig

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ pub const Instruction = struct {
138138
.moffs => true,
139139
.rip => false,
140140
.sib => |s| switch (s.base) {
141-
.none, .frame, .table, .reloc => false,
141+
.none, .frame, .table, .reloc, .rip_inst => false,
142142
.reg => |reg| reg.class() == .segment,
143143
},
144144
};
@@ -279,6 +279,7 @@ pub const Instruction = struct {
279279
.frame => |frame_index| try writer.print("{}", .{frame_index}),
280280
.table => try writer.print("Table", .{}),
281281
.reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}),
282+
.rip_inst => |inst_index| try writer.print("RipInst({d})", .{inst_index}),
282283
}
283284
if (mem.scaleIndex()) |si| {
284285
if (any) try writer.writeAll(" + ");
@@ -705,6 +706,10 @@ pub const Instruction = struct {
705706
try encoder.modRm_indirectDisp32(operand_enc, 0);
706707
try encoder.disp32(undefined);
707708
} else return error.CannotEncode,
709+
.rip_inst => {
710+
try encoder.modRm_RIPDisp32(operand_enc);
711+
try encoder.disp32(sib.disp);
712+
},
708713
},
709714
.rip => |rip| {
710715
try encoder.modRm_RIPDisp32(operand_enc);

0 commit comments

Comments
 (0)