x86_64: remove linker references from codegen

This commit is contained in:
Jacob Young 2025-06-07 23:30:17 -04:00 committed by mlugg
parent c95b1bf2d3
commit ba53b14028
No known key found for this signature in database
GPG Key ID: 3F5B7DCCBF4AF02E
16 changed files with 2685 additions and 2602 deletions

View File

@ -212,8 +212,8 @@ pub fn DebugAllocator(comptime config: Config) type {
DummyMutex{};
const DummyMutex = struct {
inline fn lock(_: *DummyMutex) void {}
inline fn unlock(_: *DummyMutex) void {}
inline fn lock(_: DummyMutex) void {}
inline fn unlock(_: DummyMutex) void {}
};
const stack_n = config.stack_trace_frames;

View File

@ -3603,9 +3603,7 @@ fn airRuntimeNavPtr(func: *Func, inst: Air.Inst.Index) !void {
const tlv_sym_index = if (func.bin_file.cast(.elf)) |elf_file| sym: {
const zo = elf_file.zigObjectPtr().?;
if (nav.getExtern(ip)) |e| {
const sym = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip));
zo.symbol(sym).flags.is_extern_ptr = true;
break :sym sym;
break :sym try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip));
}
break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav);
} else return func.fail("TODO runtime_nav_ptr on {}", .{func.bin_file.tag});

View File

@ -50,8 +50,8 @@ pub fn emitMir(emit: *Emit) Error!void {
const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
const sym = zo.symbol(symbol.sym_index);
if (sym.flags.is_extern_ptr and emit.lower.pic) {
return emit.fail("emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)});
if (emit.lower.pic) {
return emit.fail("know when to emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)});
}
const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,9 @@
//! This file contains the functionality for emitting x86_64 MIR as machine code
lower: Lower,
bin_file: *link.File,
pt: Zcu.PerThread,
pic: bool,
atom_index: u32,
debug_output: link.File.DebugInfoOutput,
code: *std.ArrayListUnmanaged(u8),
@ -9,28 +12,28 @@ prev_di_loc: Loc,
/// Relative to the beginning of `code`.
prev_di_pc: usize,
code_offset_mapping: std.ArrayListUnmanaged(u32),
relocs: std.ArrayListUnmanaged(Reloc),
table_relocs: std.ArrayListUnmanaged(TableReloc),
pub const Error = Lower.Error || error{
EmitFail,
} || link.File.UpdateDebugInfoError;
pub fn emitMir(emit: *Emit) Error!void {
const gpa = emit.lower.bin_file.comp.gpa;
const code_offset_mapping = try emit.lower.allocator.alloc(u32, emit.lower.mir.instructions.len);
defer emit.lower.allocator.free(code_offset_mapping);
var relocs: std.ArrayListUnmanaged(Reloc) = .empty;
defer relocs.deinit(emit.lower.allocator);
var table_relocs: std.ArrayListUnmanaged(TableReloc) = .empty;
defer table_relocs.deinit(emit.lower.allocator);
var local_name_index: usize = 0;
const gpa = emit.bin_file.comp.gpa;
try emit.code_offset_mapping.resize(gpa, emit.lower.mir.instructions.len);
emit.relocs.clearRetainingCapacity();
emit.table_relocs.clearRetainingCapacity();
var local_index: usize = 0;
for (0..emit.lower.mir.instructions.len) |mir_i| {
const mir_index: Mir.Inst.Index = @intCast(mir_i);
code_offset_mapping[mir_index] = @intCast(emit.code.items.len);
emit.code_offset_mapping.items[mir_index] = @intCast(emit.code.items.len);
const lowered = try emit.lower.lowerMir(mir_index);
var lowered_relocs = lowered.relocs;
for (lowered.insts, 0..) |lowered_inst, lowered_index| {
const start_offset: u32 = @intCast(emit.code.items.len);
lowered_inst: for (lowered.insts, 0..) |lowered_inst, lowered_index| {
if (lowered_inst.prefix == .directive) {
const start_offset: u32 = @intCast(emit.code.items.len);
switch (emit.debug_output) {
.dwarf => |dwarf| switch (lowered_inst.encoding.mnemonic) {
.@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{
@ -83,204 +86,305 @@ pub fn emitMir(emit: *Emit) Error!void {
}
continue;
}
try lowered_inst.encode(emit.code.writer(gpa), .{});
const end_offset: u32 = @intCast(emit.code.items.len);
var reloc_info_buf: [2]RelocInfo = undefined;
var reloc_info_index: usize = 0;
while (lowered_relocs.len > 0 and
lowered_relocs[0].lowered_inst_index == lowered_index) : ({
lowered_relocs = lowered_relocs[1..];
}) switch (lowered_relocs[0].target) {
.inst => |target| {
const inst_length: u4 = @intCast(end_offset - start_offset);
const reloc_offset, const reloc_length = reloc_offset_length: {
var reloc_offset = inst_length;
var op_index: usize = lowered_inst.ops.len;
while (true) {
op_index -= 1;
const op = lowered_inst.encoding.data.ops[op_index];
if (op == .none) continue;
const is_mem = op.isMemory();
const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) {
.rip_inst => 4,
else => unreachable,
} else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable);
reloc_offset -= enc_length;
if (op_index == lowered_relocs[0].op_index) break :reloc_offset_length .{ reloc_offset, enc_length };
std.debug.assert(!is_mem);
}
};
try relocs.append(emit.lower.allocator, .{
.inst_offset = start_offset,
.inst_length = inst_length,
.source_offset = reloc_offset,
.source_length = reloc_length,
.target = target,
.target_offset = lowered_relocs[0].off,
});
},
.table => try table_relocs.append(emit.lower.allocator, .{
.source_offset = end_offset - 4,
.target_offset = lowered_relocs[0].off,
}),
.linker_extern_fn => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
// Add relocation to the decl.
const zo = elf_file.zigObjectPtr().?;
const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?;
const r_type = @intFromEnum(std.elf.R_X86_64.PLT32);
try atom_ptr.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off - 4,
}, zo);
} else if (emit.lower.bin_file.cast(.macho)) |macho_file| {
// Add relocation to the decl.
const zo = macho_file.getZigObject().?;
const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = end_offset - 4,
.target = sym_index,
.addend = lowered_relocs[0].off,
.type = .branch,
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
.symbolnum = @intCast(sym_index),
reloc_info_index += 1;
}) reloc_info_buf[reloc_info_index] = .{
.op_index = lowered_relocs[0].op_index,
.off = lowered_relocs[0].off,
.target = target: switch (lowered_relocs[0].target) {
.inst => |inst| .{ .index = inst, .is_extern = false, .type = .inst },
.table => .{ .index = undefined, .is_extern = false, .type = .table },
.nav => |nav| {
const ip = &emit.pt.zcu.intern_pool;
const sym_index = switch (try codegen.genNavRef(
emit.bin_file,
emit.pt,
emit.lower.src_loc,
.fromInterned(ip.getNav(nav).typeOf(ip)),
nav,
emit.lower.target.*,
)) {
.mcv => |mcv| switch (mcv) {
else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }),
.lea_symbol => |sym_index| sym_index,
},
.fail => |em| {
assert(emit.lower.err_msg == null);
emit.lower.err_msg = em;
return error.EmitFail;
},
};
break :target switch (ip.getNav(nav).status) {
.unresolved => unreachable,
.type_resolved => |type_resolved| .{
.index = sym_index,
.is_extern = false,
.type = if (type_resolved.is_threadlocal) .tlv else .symbol,
},
.fully_resolved => |fully_resolved| switch (ip.indexToKey(fully_resolved.val)) {
.@"extern" => |@"extern"| .{
.index = sym_index,
.is_extern = switch (@"extern".visibility) {
.default => true,
.hidden, .protected => false,
},
.type = if (@"extern".is_threadlocal) .tlv else .symbol,
.force_pcrel_direct = switch (@"extern".relocation) {
.any => false,
.pcrel => true,
},
},
.variable => |variable| .{
.index = sym_index,
.is_extern = false,
.type = if (variable.is_threadlocal) .tlv else .symbol,
},
else => .{ .index = sym_index, .is_extern = false, .type = .symbol },
},
};
},
.uav => |uav| .{
.index = switch (try emit.bin_file.lowerUav(
emit.pt,
uav.val,
Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu),
emit.lower.src_loc,
)) {
.mcv => |mcv| switch (mcv) {
else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }),
.load_direct, .load_symbol => |sym_index| sym_index,
},
.fail => |em| {
assert(emit.lower.err_msg == null);
emit.lower.err_msg = em;
return error.EmitFail;
},
},
});
} else if (emit.lower.bin_file.cast(.coff)) |coff_file| {
// Add relocation to the decl.
const atom_index = coff_file.getAtomIndexForSymbol(
.{ .sym_index = emit.atom_index, .file = null },
).?;
const target = if (link.File.Coff.global_symbol_bit & sym_index != 0)
coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & sym_index)
else
link.File.Coff.SymbolWithLoc{ .sym_index = sym_index, .file = null };
try coff_file.addRelocation(atom_index, .{
.type = .direct,
.target = target,
.offset = end_offset - 4,
.addend = @intCast(lowered_relocs[0].off),
.pcrel = true,
.length = 2,
});
} else return emit.fail("TODO implement extern reloc for {s}", .{
@tagName(emit.lower.bin_file.tag),
}),
.linker_tlsld => |sym_index| {
const elf_file = emit.lower.bin_file.cast(.elf).?;
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD);
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off - 4,
}, zo);
},
.linker_dtpoff => |sym_index| {
const elf_file = emit.lower.bin_file.cast(.elf).?;
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32);
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off,
}, zo);
},
.linker_reloc, .linker_pcrel => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const sym = zo.symbol(sym_index);
if (emit.lower.pic) {
const r_type: u32 = if (sym.flags.is_extern_ptr and lowered_relocs[0].target != .linker_pcrel)
@intFromEnum(std.elf.R_X86_64.GOTPCREL)
.is_extern = false,
.type = .symbol,
},
.lazy_sym => |lazy_sym| .{
.index = if (emit.bin_file.cast(.elf)) |elf_file|
elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, emit.pt, lazy_sym) catch |err|
return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
else if (emit.bin_file.cast(.macho)) |macho_file|
macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
else if (emit.bin_file.cast(.coff)) |coff_file| sym_index: {
const atom = coff_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err|
return emit.fail("{s} creating lazy symbol", .{@errorName(err)});
break :sym_index coff_file.getAtom(atom).getSymbolIndex().?;
} else if (emit.bin_file.cast(.plan9)) |p9_file|
p9_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err|
return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
else
@intFromEnum(std.elf.R_X86_64.PC32);
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off - 4,
}, zo);
} else {
const r_type: u32 = if (sym.flags.is_tls)
@intFromEnum(std.elf.R_X86_64.TPOFF32)
return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
.is_extern = false,
.type = .symbol,
},
.extern_func => |extern_func| .{
.index = if (emit.bin_file.cast(.elf)) |elf_file|
try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
else if (emit.bin_file.cast(.macho)) |macho_file|
try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
else if (emit.bin_file.cast(.coff)) |coff_file|
link.File.Coff.global_symbol_bit | try coff_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
else
@intFromEnum(std.elf.R_X86_64.@"32");
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off,
}, zo);
}
} else if (emit.lower.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
const sym = &zo.symbols.items[sym_index];
const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr and lowered_relocs[0].target != .linker_pcrel)
.got_load
else if (sym.flags.tlv)
.tlv
else
.signed;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = @intCast(end_offset - 4),
.target = sym_index,
.addend = lowered_relocs[0].off,
.type = @"type",
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
.symbolnum = @intCast(sym_index),
},
});
} else unreachable,
.linker_got,
.linker_direct,
.linker_import,
=> |sym_index| if (emit.lower.bin_file.cast(.elf)) |_| {
unreachable;
} else if (emit.lower.bin_file.cast(.macho)) |_| {
unreachable;
} else if (emit.lower.bin_file.cast(.coff)) |coff_file| {
const atom_index = coff_file.getAtomIndexForSymbol(.{
.sym_index = emit.atom_index,
.file = null,
}).?;
const target = if (link.File.Coff.global_symbol_bit & sym_index != 0)
coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & sym_index)
else
link.File.Coff.SymbolWithLoc{ .sym_index = sym_index, .file = null };
try coff_file.addRelocation(atom_index, .{
.type = switch (lowered_relocs[0].target) {
.linker_got => .got,
.linker_direct => .direct,
.linker_import => .import,
else => unreachable,
},
.target = target,
.offset = @intCast(end_offset - 4),
.addend = @intCast(lowered_relocs[0].off),
.pcrel = true,
.length = 2,
});
} else if (emit.lower.bin_file.cast(.plan9)) |p9_file| {
try p9_file.addReloc(emit.atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct
.target = sym_index, // we set sym_index to just be the atom index
.offset = @intCast(end_offset - 4),
.addend = @intCast(lowered_relocs[0].off),
.type = .pcrel,
});
} else return emit.fail("TODO implement linker reloc for {s}", .{
@tagName(emit.lower.bin_file.tag),
}),
return emit.fail("external symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
.is_extern = true,
.type = .symbol,
},
},
};
const reloc_info = reloc_info_buf[0..reloc_info_index];
for (reloc_info) |*reloc| switch (reloc.target.type) {
.inst, .table => {},
.symbol => {
switch (lowered_inst.encoding.mnemonic) {
.call => {
reloc.target.type = .branch;
try emit.encodeInst(lowered_inst, reloc_info);
continue :lowered_inst;
},
else => {},
}
if (emit.bin_file.cast(.elf)) |_| {
if (!emit.pic) switch (lowered_inst.encoding.mnemonic) {
.lea => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .imm = .s(0) },
}, emit.lower.target), reloc_info),
.mov => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{
.base = .{ .reg = .ds },
}) },
}, emit.lower.target), reloc_info),
else => unreachable,
} else if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) {
.lea => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(.ptr, 0) },
}, emit.lower.target), reloc_info),
.mov => {
try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(.ptr, 0) },
}, emit.lower.target), reloc_info);
try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{
.reg = lowered_inst.ops[0].reg.to64(),
} }) },
}, emit.lower.target), &.{});
},
else => unreachable,
} else switch (lowered_inst.encoding.mnemonic) {
.lea => try emit.encodeInst(try .new(.none, .lea, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(.none, 0) },
}, emit.lower.target), reloc_info),
.mov => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) },
}, emit.lower.target), reloc_info),
else => unreachable,
}
} else if (emit.bin_file.cast(.macho)) |_| {
if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) {
.lea => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(.ptr, 0) },
}, emit.lower.target), reloc_info),
.mov => {
try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(.ptr, 0) },
}, emit.lower.target), reloc_info);
try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{
.reg = lowered_inst.ops[0].reg.to64(),
} }) },
}, emit.lower.target), &.{});
},
else => unreachable,
} else switch (lowered_inst.encoding.mnemonic) {
.lea => try emit.encodeInst(try .new(.none, .lea, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(.none, 0) },
}, emit.lower.target), reloc_info),
.mov => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) },
}, emit.lower.target), reloc_info),
else => unreachable,
}
} else return emit.fail("TODO implement relocs for {s}", .{
@tagName(emit.bin_file.tag),
});
continue :lowered_inst;
},
.branch, .tls => unreachable,
.tlv => {
if (emit.bin_file.cast(.elf)) |elf_file| {
if (reloc.target.is_extern) {
// TODO handle extern TLS vars, i.e., emit GD model
return emit.fail("TODO implement extern {s} reloc for {s}", .{
@tagName(reloc.target.type), @tagName(emit.bin_file.tag),
});
} else if (emit.pic) switch (lowered_inst.encoding.mnemonic) {
.lea, .mov => {
// Here, we currently assume local dynamic TLS vars, and so
// we emit LD model.
try emit.encodeInst(try .new(.none, .lea, &.{
.{ .reg = .rdi },
.{ .mem = .initRip(.none, 0) },
}, emit.lower.target), &.{.{
.op_index = 1,
.target = .{
.index = reloc.target.index,
.is_extern = false,
.type = .tls,
},
}});
try emit.encodeInst(try .new(.none, .call, &.{
.{ .imm = .s(0) },
}, emit.lower.target), &.{.{
.op_index = 0,
.target = .{
.index = try elf_file.getGlobalSymbol("__tls_get_addr", null),
.is_extern = true,
.type = .branch,
},
}});
try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
lowered_inst.ops[0],
.{ .mem = .initSib(.none, .{
.base = .{ .reg = .rax },
.disp = std.math.minInt(i32),
}) },
}, emit.lower.target), reloc_info);
},
else => unreachable,
} else switch (lowered_inst.encoding.mnemonic) {
.lea, .mov => {
// Since we are linking statically, we emit LE model directly.
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .rax },
.{ .mem = .initSib(.qword, .{ .base = .{ .reg = .fs } }) },
}, emit.lower.target), &.{});
try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
lowered_inst.ops[0],
.{ .mem = .initSib(.none, .{
.base = .{ .reg = .rax },
.disp = std.math.minInt(i32),
}) },
}, emit.lower.target), reloc_info);
},
else => unreachable,
}
} else if (emit.bin_file.cast(.macho)) |_| switch (lowered_inst.encoding.mnemonic) {
.lea => {
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .rdi },
.{ .mem = .initRip(.ptr, 0) },
}, emit.lower.target), reloc_info);
try emit.encodeInst(try .new(.none, .call, &.{
.{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rdi } }) },
}, emit.lower.target), &.{});
try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .reg = .rax },
}, emit.lower.target), &.{});
},
.mov => {
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .rdi },
.{ .mem = .initRip(.ptr, 0) },
}, emit.lower.target), reloc_info);
try emit.encodeInst(try .new(.none, .call, &.{
.{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rdi } }) },
}, emit.lower.target), &.{});
try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rax } }) },
}, emit.lower.target), &.{});
},
else => unreachable,
} else return emit.fail("TODO implement relocs for {s}", .{
@tagName(emit.bin_file.tag),
});
continue :lowered_inst;
},
};
try emit.encodeInst(lowered_inst, reloc_info);
}
std.debug.assert(lowered_relocs.len == 0);
assert(lowered_relocs.len == 0);
if (lowered.insts.len == 0) {
const mir_inst = emit.lower.mir.instructions.get(mir_index);
@ -358,7 +462,6 @@ pub fn emitMir(emit: *Emit) Error!void {
.pseudo_dbg_arg_i_s,
.pseudo_dbg_arg_i_u,
.pseudo_dbg_arg_i_64,
.pseudo_dbg_arg_reloc,
.pseudo_dbg_arg_ro,
.pseudo_dbg_arg_fa,
.pseudo_dbg_arg_m,
@ -366,7 +469,6 @@ pub fn emitMir(emit: *Emit) Error!void {
.pseudo_dbg_var_i_s,
.pseudo_dbg_var_i_u,
.pseudo_dbg_var_i_64,
.pseudo_dbg_var_reloc,
.pseudo_dbg_var_ro,
.pseudo_dbg_var_fa,
.pseudo_dbg_var_m,
@ -391,16 +493,6 @@ pub fn emitMir(emit: *Emit) Error!void {
loc_buf[0] = .{ .constu = mir_inst.data.i64 };
break :stack_value &loc_buf[0];
} },
.pseudo_dbg_arg_reloc, .pseudo_dbg_var_reloc => .{ .plus = .{
sym: {
loc_buf[0] = .{ .addr_reloc = mir_inst.data.reloc.sym_index };
break :sym &loc_buf[0];
},
off: {
loc_buf[1] = .{ .consts = mir_inst.data.reloc.off };
break :off &loc_buf[1];
},
} },
.pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => {
const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa);
break :loc .{ .plus = .{
@ -415,15 +507,53 @@ pub fn emitMir(emit: *Emit) Error!void {
} };
},
.pseudo_dbg_arg_m, .pseudo_dbg_var_m => {
const mem = emit.lower.mem(undefined, mir_inst.data.x.payload);
const ip = &emit.pt.zcu.intern_pool;
const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode();
break :loc .{ .plus = .{
base: {
loc_buf[0] = switch (mem.base()) {
.none => .{ .constu = 0 },
.reg => |reg| .{ .breg = reg.dwarfNum() },
.frame, .table, .rip_inst => unreachable,
.reloc => |sym_index| .{ .addr_reloc = sym_index },
.pcrel => unreachable,
.nav => |nav| .{ .addr_reloc = switch (codegen.genNavRef(
emit.bin_file,
emit.pt,
emit.lower.src_loc,
.fromInterned(ip.getNav(nav).typeOf(ip)),
nav,
emit.lower.target.*,
) catch |err| switch (err) {
error.CodegenFail,
=> return emit.fail("unable to codegen: {s}", .{@errorName(err)}),
else => |e| return e,
}) {
.mcv => |mcv| switch (mcv) {
else => unreachable,
.load_direct, .load_symbol => |sym_index| sym_index,
},
.fail => |em| {
assert(emit.lower.err_msg == null);
emit.lower.err_msg = em;
return error.EmitFail;
},
} },
.uav => |uav| .{ .addr_reloc = switch (try emit.bin_file.lowerUav(
emit.pt,
uav.val,
Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu),
emit.lower.src_loc,
)) {
.mcv => |mcv| switch (mcv) {
else => unreachable,
.load_direct, .load_symbol => |sym_index| sym_index,
},
.fail => |em| {
assert(emit.lower.err_msg == null);
emit.lower.err_msg = em;
return error.EmitFail;
},
} },
.lazy_sym, .extern_func => unreachable,
};
break :base &loc_buf[0];
},
@ -438,13 +568,8 @@ pub fn emitMir(emit: *Emit) Error!void {
},
};
const local_name_bytes = emit.lower.mir.local_name_bytes[local_name_index..];
const local_name = local_name_bytes[0..std.mem.indexOfScalar(u8, local_name_bytes, 0).? :0];
local_name_index += local_name.len + 1;
const local_type = emit.lower.mir.local_types[local_index];
const local = &emit.lower.mir.locals[local_index];
local_index += 1;
try dwarf.genLocalVarDebugInfo(
switch (mir_inst.ops) {
else => unreachable,
@ -452,7 +577,6 @@ pub fn emitMir(emit: *Emit) Error!void {
.pseudo_dbg_arg_i_s,
.pseudo_dbg_arg_i_u,
.pseudo_dbg_arg_i_64,
.pseudo_dbg_arg_reloc,
.pseudo_dbg_arg_ro,
.pseudo_dbg_arg_fa,
.pseudo_dbg_arg_m,
@ -462,27 +586,23 @@ pub fn emitMir(emit: *Emit) Error!void {
.pseudo_dbg_var_i_s,
.pseudo_dbg_var_i_u,
.pseudo_dbg_var_i_64,
.pseudo_dbg_var_reloc,
.pseudo_dbg_var_ro,
.pseudo_dbg_var_fa,
.pseudo_dbg_var_m,
.pseudo_dbg_var_val,
=> .local_var,
},
local_name,
.fromInterned(local_type),
local.name.toSlice(&emit.lower.mir),
.fromInterned(local.type),
loc,
);
},
.plan9 => {},
.none => {},
.plan9, .none => local_index += 1,
},
.pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) {
.dwarf => |dwarf| {
const local_name_bytes = emit.lower.mir.local_name_bytes[local_name_index..];
const local_name = local_name_bytes[0..std.mem.indexOfScalar(u8, local_name_bytes, 0).? :0];
local_name_index += local_name.len + 1;
const local = &emit.lower.mir.locals[local_index];
local_index += 1;
try dwarf.genLocalConstDebugInfo(
emit.lower.src_loc,
switch (mir_inst.ops) {
@ -490,12 +610,11 @@ pub fn emitMir(emit: *Emit) Error!void {
.pseudo_dbg_arg_val => .comptime_arg,
.pseudo_dbg_var_val => .local_const,
},
local_name,
local.name.toSlice(&emit.lower.mir),
.fromInterned(mir_inst.data.ip_index),
);
},
.plan9 => {},
.none => {},
.plan9, .none => local_index += 1,
},
.pseudo_dbg_var_args_none => switch (emit.debug_output) {
.dwarf => |dwarf| try dwarf.genVarArgsDebugInfo(),
@ -507,8 +626,8 @@ pub fn emitMir(emit: *Emit) Error!void {
}
}
}
for (relocs.items) |reloc| {
const target = code_offset_mapping[reloc.target];
for (emit.relocs.items) |reloc| {
const target = emit.code_offset_mapping.items[reloc.target];
const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.inst_offset + reloc.inst_length)) + reloc.target_offset;
const inst_bytes = emit.code.items[reloc.inst_offset..][0..reloc.inst_length];
switch (reloc.source_length) {
@ -522,13 +641,13 @@ pub fn emitMir(emit: *Emit) Error!void {
}
}
if (emit.lower.mir.table.len > 0) {
if (emit.lower.bin_file.cast(.elf)) |elf_file| {
if (emit.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8);
var table_offset = std.mem.alignForward(u32, @intCast(emit.code.items.len), ptr_size);
for (table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{
for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{
.r_offset = table_reloc.source_offset,
.r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32"),
.r_addend = @as(i64, table_offset) + table_reloc.target_offset,
@ -537,7 +656,7 @@ pub fn emitMir(emit: *Emit) Error!void {
try atom.addReloc(gpa, .{
.r_offset = table_offset,
.r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"64"),
.r_addend = code_offset_mapping[entry],
.r_addend = emit.code_offset_mapping.items[entry],
}, zo);
table_offset += ptr_size;
}
@ -546,6 +665,192 @@ pub fn emitMir(emit: *Emit) Error!void {
}
}
pub fn deinit(emit: *Emit) void {
const gpa = emit.bin_file.comp.gpa;
emit.code_offset_mapping.deinit(gpa);
emit.relocs.deinit(gpa);
emit.table_relocs.deinit(gpa);
emit.* = undefined;
}
const RelocInfo = struct {
op_index: Lower.InstOpIndex,
off: i32 = 0,
target: Target,
const Target = struct {
index: u32,
is_extern: bool,
type: Target.Type,
force_pcrel_direct: bool = false,
const Type = enum { inst, table, symbol, branch, tls, tlv };
};
};
fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocInfo) Error!void {
const comp = emit.bin_file.comp;
const gpa = comp.gpa;
const start_offset: u32 = @intCast(emit.code.items.len);
try lowered_inst.encode(emit.code.writer(gpa), .{});
const end_offset: u32 = @intCast(emit.code.items.len);
for (reloc_info) |reloc| switch (reloc.target.type) {
.inst => {
const inst_length: u4 = @intCast(end_offset - start_offset);
const reloc_offset, const reloc_length = reloc_offset_length: {
var reloc_offset = inst_length;
var op_index: usize = lowered_inst.ops.len;
while (true) {
op_index -= 1;
const op = lowered_inst.encoding.data.ops[op_index];
if (op == .none) continue;
const is_mem = op.isMemory();
const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) {
.rip_inst => 4,
else => unreachable,
} else @intCast(std.math.divCeil(u7, @intCast(op.immBitSize()), 8) catch unreachable);
reloc_offset -= enc_length;
if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length };
assert(!is_mem);
}
};
try emit.relocs.append(emit.lower.allocator, .{
.inst_offset = start_offset,
.inst_length = inst_length,
.source_offset = reloc_offset,
.source_length = reloc_length,
.target = reloc.target.index,
.target_offset = reloc.off,
});
},
.table => try emit.table_relocs.append(emit.lower.allocator, .{
.source_offset = end_offset - 4,
.target_offset = reloc.off,
}),
.symbol => if (emit.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = if (!emit.pic)
.@"32"
else if (reloc.target.is_extern and !reloc.target.force_pcrel_direct)
.GOTPCREL
else
.PC32;
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
.r_addend = if (emit.pic) reloc.off - 4 else reloc.off,
}, zo);
} else if (emit.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = end_offset - 4,
.target = reloc.target.index,
.addend = reloc.off,
.type = if (reloc.target.is_extern and !reloc.target.force_pcrel_direct) .got_load else .signed,
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
.symbolnum = @intCast(reloc.target.index),
},
});
} else unreachable,
.branch => if (emit.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = .PLT32;
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
.r_addend = reloc.off - 4,
}, zo);
} else if (emit.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = end_offset - 4,
.target = reloc.target.index,
.addend = reloc.off,
.type = .branch,
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
.symbolnum = @intCast(reloc.target.index),
},
});
} else if (emit.bin_file.cast(.coff)) |coff_file| {
const atom_index = coff_file.getAtomIndexForSymbol(
.{ .sym_index = emit.atom_index, .file = null },
).?;
const target: link.File.Coff.SymbolWithLoc = if (link.File.Coff.global_symbol_bit & reloc.target.index != 0)
coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & reloc.target.index)
else
.{ .sym_index = reloc.target.index, .file = null };
try coff_file.addRelocation(atom_index, .{
.type = .direct,
.target = target,
.offset = end_offset - 4,
.addend = @intCast(reloc.off),
.pcrel = true,
.length = 2,
});
} else return emit.fail("TODO implement {s} reloc for {s}", .{
@tagName(reloc.target.type), @tagName(emit.bin_file.tag),
}),
.tls => if (emit.bin_file.cast(.elf)) |elf_file| {
if (reloc.target.is_extern) return emit.fail("TODO implement extern {s} reloc for {s}", .{
@tagName(reloc.target.type), @tagName(emit.bin_file.tag),
});
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = if (emit.pic) .TLSLD else unreachable;
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
.r_addend = reloc.off - 4,
}, zo);
} else return emit.fail("TODO implement {s} reloc for {s}", .{
@tagName(reloc.target.type), @tagName(emit.bin_file.tag),
}),
.tlv => if (emit.bin_file.cast(.elf)) |elf_file| {
if (reloc.target.is_extern) return emit.fail("TODO implement extern {s} reloc for {s}", .{
@tagName(reloc.target.type), @tagName(emit.bin_file.tag),
});
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32;
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
.r_addend = reloc.off,
}, zo);
} else if (emit.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = end_offset - 4,
.target = reloc.target.index,
.addend = reloc.off,
.type = .tlv,
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
.symbolnum = @intCast(reloc.target.index),
},
});
} else return emit.fail("TODO implement {s} reloc for {s}", .{
@tagName(reloc.target.type), @tagName(emit.bin_file.tag),
}),
};
}
fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
return switch (emit.lower.fail(format, args)) {
error.LowerFail => error.EmitFail,
@ -629,11 +934,17 @@ fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
}
}
const assert = std.debug.assert;
const bits = @import("bits.zig");
const codegen = @import("../../codegen.zig");
const Emit = @This();
const encoder = @import("encoder.zig");
const Instruction = encoder.Instruction;
const InternPool = @import("../../InternPool.zig");
const link = @import("../../link.zig");
const log = std.log.scoped(.emit);
const Lower = @import("Lower.zig");
const Mir = @import("Mir.zig");
const std = @import("std");
const Type = @import("../../Type.zig");
const Zcu = @import("../../Zcu.zig");

View File

@ -1,10 +1,6 @@
//! This file contains the functionality for lowering x86_64 MIR to Instructions
bin_file: *link.File,
target: *const std.Target,
output_mode: std.builtin.OutputMode,
link_mode: std.builtin.LinkMode,
pic: bool,
allocator: std.mem.Allocator,
mir: Mir,
cc: std.builtin.CallingConvention,
@ -17,7 +13,6 @@ result_relocs: [max_result_relocs]Reloc = undefined,
const max_result_insts = @max(
1, // non-pseudo instructions
3, // (ELF only) TLS local dynamic (LD) sequence in PIC mode
2, // cmovcc: cmovcc \ cmovcc
3, // setcc: setcc \ setcc \ logicop
2, // jcc: jcc \ jcc
@ -25,6 +20,7 @@ const max_result_insts = @max(
pseudo_probe_adjust_unrolled_max_insts,
pseudo_probe_adjust_setup_insts,
pseudo_probe_adjust_loop_insts,
abi.zigcc.callee_preserved_regs.len * 2, // push_regs/pop_regs
abi.Win64.callee_preserved_regs.len * 2, // push_regs/pop_regs
abi.SysV.callee_preserved_regs.len * 2, // push_regs/pop_regs
);
@ -33,14 +29,13 @@ const max_result_relocs = @max(
2, // jcc: jcc \ jcc
2, // test \ jcc \ probe \ sub \ jmp
1, // probe \ sub \ jcc
3, // (ELF only) TLS local dynamic (LD) sequence in PIC mode
);
const ResultInstIndex = std.math.IntFittingRange(0, max_result_insts - 1);
const ResultRelocIndex = std.math.IntFittingRange(0, max_result_relocs - 1);
const InstOpIndex = std.math.IntFittingRange(
const ResultInstIndex = std.math.IntFittingRange(0, max_result_insts);
const ResultRelocIndex = std.math.IntFittingRange(0, max_result_relocs);
pub const InstOpIndex = std.math.IntFittingRange(
0,
@typeInfo(@FieldType(Instruction, "ops")).array.len - 1,
@typeInfo(@FieldType(Instruction, "ops")).array.len,
);
pub const pseudo_probe_align_insts = 5; // test \ jcc \ probe \ sub \ jmp
@ -54,7 +49,8 @@ pub const Error = error{
LowerFail,
InvalidInstruction,
CannotEncode,
};
CodegenFail,
} || codegen.GenerateSymbolError;
pub const Reloc = struct {
lowered_inst_index: ResultInstIndex,
@ -65,14 +61,10 @@ pub const Reloc = struct {
const Target = union(enum) {
inst: Mir.Inst.Index,
table,
linker_reloc: u32,
linker_pcrel: u32,
linker_tlsld: u32,
linker_dtpoff: u32,
linker_extern_fn: u32,
linker_got: u32,
linker_direct: u32,
linker_import: u32,
nav: InternPool.Nav.Index,
uav: InternPool.Key.Ptr.BaseAddr.Uav,
lazy_sym: link.File.LazySymbol,
extern_func: Mir.NullTerminatedString,
};
};
@ -80,7 +72,7 @@ const Options = struct { allow_frame_locs: bool };
/// The returned slice is overwritten by the next call to lowerMir.
pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
insts: []const Instruction,
insts: []Instruction,
relocs: []const Reloc,
} {
lower.result_insts = undefined;
@ -98,130 +90,130 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.pseudo => switch (inst.ops) {
.pseudo_cmov_z_and_np_rr => {
assert(inst.data.rr.fixes == ._);
try lower.emit(.none, .cmovnz, &.{
try lower.encode(.none, .cmovnz, &.{
.{ .reg = inst.data.rr.r2 },
.{ .reg = inst.data.rr.r1 },
});
try lower.emit(.none, .cmovnp, &.{
try lower.encode(.none, .cmovnp, &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_cmov_nz_or_p_rr => {
assert(inst.data.rr.fixes == ._);
try lower.emit(.none, .cmovnz, &.{
try lower.encode(.none, .cmovnz, &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
});
try lower.emit(.none, .cmovp, &.{
try lower.encode(.none, .cmovp, &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_cmov_nz_or_p_rm => {
assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .cmovnz, &.{
try lower.encode(.none, .cmovnz, &.{
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(1, inst.data.rx.payload) },
});
try lower.emit(.none, .cmovp, &.{
try lower.encode(.none, .cmovp, &.{
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(1, inst.data.rx.payload) },
});
},
.pseudo_set_z_and_np_r => {
assert(inst.data.rr.fixes == ._);
try lower.emit(.none, .setz, &.{
try lower.encode(.none, .setz, &.{
.{ .reg = inst.data.rr.r1 },
});
try lower.emit(.none, .setnp, &.{
try lower.encode(.none, .setnp, &.{
.{ .reg = inst.data.rr.r2 },
});
try lower.emit(.none, .@"and", &.{
try lower.encode(.none, .@"and", &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_set_z_and_np_m => {
assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .setz, &.{
try lower.encode(.none, .setz, &.{
.{ .mem = lower.mem(0, inst.data.rx.payload) },
});
try lower.emit(.none, .setnp, &.{
try lower.encode(.none, .setnp, &.{
.{ .reg = inst.data.rx.r1 },
});
try lower.emit(.none, .@"and", &.{
try lower.encode(.none, .@"and", &.{
.{ .mem = lower.mem(0, inst.data.rx.payload) },
.{ .reg = inst.data.rx.r1 },
});
},
.pseudo_set_nz_or_p_r => {
assert(inst.data.rr.fixes == ._);
try lower.emit(.none, .setnz, &.{
try lower.encode(.none, .setnz, &.{
.{ .reg = inst.data.rr.r1 },
});
try lower.emit(.none, .setp, &.{
try lower.encode(.none, .setp, &.{
.{ .reg = inst.data.rr.r2 },
});
try lower.emit(.none, .@"or", &.{
try lower.encode(.none, .@"or", &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_set_nz_or_p_m => {
assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .setnz, &.{
try lower.encode(.none, .setnz, &.{
.{ .mem = lower.mem(0, inst.data.rx.payload) },
});
try lower.emit(.none, .setp, &.{
try lower.encode(.none, .setp, &.{
.{ .reg = inst.data.rx.r1 },
});
try lower.emit(.none, .@"or", &.{
try lower.encode(.none, .@"or", &.{
.{ .mem = lower.mem(0, inst.data.rx.payload) },
.{ .reg = inst.data.rx.r1 },
});
},
.pseudo_j_z_and_np_inst => {
assert(inst.data.inst.fixes == ._);
try lower.emit(.none, .jnz, &.{
try lower.encode(.none, .jnz, &.{
.{ .imm = lower.reloc(0, .{ .inst = index + 1 }, 0) },
});
try lower.emit(.none, .jnp, &.{
try lower.encode(.none, .jnp, &.{
.{ .imm = lower.reloc(0, .{ .inst = inst.data.inst.inst }, 0) },
});
},
.pseudo_j_nz_or_p_inst => {
assert(inst.data.inst.fixes == ._);
try lower.emit(.none, .jnz, &.{
try lower.encode(.none, .jnz, &.{
.{ .imm = lower.reloc(0, .{ .inst = inst.data.inst.inst }, 0) },
});
try lower.emit(.none, .jp, &.{
try lower.encode(.none, .jp, &.{
.{ .imm = lower.reloc(0, .{ .inst = inst.data.inst.inst }, 0) },
});
},
.pseudo_probe_align_ri_s => {
try lower.emit(.none, .@"test", &.{
try lower.encode(.none, .@"test", &.{
.{ .reg = inst.data.ri.r1 },
.{ .imm = .s(@bitCast(inst.data.ri.i)) },
});
try lower.emit(.none, .jz, &.{
try lower.encode(.none, .jz, &.{
.{ .imm = lower.reloc(0, .{ .inst = index + 1 }, 0) },
});
try lower.emit(.none, .lea, &.{
try lower.encode(.none, .lea, &.{
.{ .reg = inst.data.ri.r1 },
.{ .mem = Memory.initSib(.qword, .{
.base = .{ .reg = inst.data.ri.r1 },
.disp = -page_size,
}) },
});
try lower.emit(.none, .@"test", &.{
try lower.encode(.none, .@"test", &.{
.{ .mem = Memory.initSib(.dword, .{
.base = .{ .reg = inst.data.ri.r1 },
}) },
.{ .reg = inst.data.ri.r1.to32() },
});
try lower.emit(.none, .jmp, &.{
try lower.encode(.none, .jmp, &.{
.{ .imm = lower.reloc(0, .{ .inst = index }, 0) },
});
assert(lower.result_insts_len == pseudo_probe_align_insts);
@ -229,7 +221,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.pseudo_probe_adjust_unrolled_ri_s => {
var offset = page_size;
while (offset < @as(i32, @bitCast(inst.data.ri.i))) : (offset += page_size) {
try lower.emit(.none, .@"test", &.{
try lower.encode(.none, .@"test", &.{
.{ .mem = Memory.initSib(.dword, .{
.base = .{ .reg = inst.data.ri.r1 },
.disp = -offset,
@ -237,25 +229,25 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.{ .reg = inst.data.ri.r1.to32() },
});
}
try lower.emit(.none, .sub, &.{
try lower.encode(.none, .sub, &.{
.{ .reg = inst.data.ri.r1 },
.{ .imm = .s(@bitCast(inst.data.ri.i)) },
});
assert(lower.result_insts_len <= pseudo_probe_adjust_unrolled_max_insts);
},
.pseudo_probe_adjust_setup_rri_s => {
try lower.emit(.none, .mov, &.{
try lower.encode(.none, .mov, &.{
.{ .reg = inst.data.rri.r2.to32() },
.{ .imm = .s(@bitCast(inst.data.rri.i)) },
});
try lower.emit(.none, .sub, &.{
try lower.encode(.none, .sub, &.{
.{ .reg = inst.data.rri.r1 },
.{ .reg = inst.data.rri.r2 },
});
assert(lower.result_insts_len == pseudo_probe_adjust_setup_insts);
},
.pseudo_probe_adjust_loop_rr => {
try lower.emit(.none, .@"test", &.{
try lower.encode(.none, .@"test", &.{
.{ .mem = Memory.initSib(.dword, .{
.base = .{ .reg = inst.data.rr.r1 },
.scale_index = .{ .scale = 1, .index = inst.data.rr.r2 },
@ -263,11 +255,11 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
}) },
.{ .reg = inst.data.rr.r1.to32() },
});
try lower.emit(.none, .sub, &.{
try lower.encode(.none, .sub, &.{
.{ .reg = inst.data.rr.r2 },
.{ .imm = .s(page_size) },
});
try lower.emit(.none, .jae, &.{
try lower.encode(.none, .jae, &.{
.{ .imm = lower.reloc(0, .{ .inst = index }, 0) },
});
assert(lower.result_insts_len == pseudo_probe_adjust_loop_insts);
@ -275,47 +267,47 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.pseudo_push_reg_list => try lower.pushPopRegList(.push, inst),
.pseudo_pop_reg_list => try lower.pushPopRegList(.pop, inst),
.pseudo_cfi_def_cfa_ri_s => try lower.emit(.directive, .@".cfi_def_cfa", &.{
.pseudo_cfi_def_cfa_ri_s => try lower.encode(.directive, .@".cfi_def_cfa", &.{
.{ .reg = inst.data.ri.r1 },
.{ .imm = lower.imm(.ri_s, inst.data.ri.i) },
}),
.pseudo_cfi_def_cfa_register_r => try lower.emit(.directive, .@".cfi_def_cfa_register", &.{
.pseudo_cfi_def_cfa_register_r => try lower.encode(.directive, .@".cfi_def_cfa_register", &.{
.{ .reg = inst.data.r.r1 },
}),
.pseudo_cfi_def_cfa_offset_i_s => try lower.emit(.directive, .@".cfi_def_cfa_offset", &.{
.pseudo_cfi_def_cfa_offset_i_s => try lower.encode(.directive, .@".cfi_def_cfa_offset", &.{
.{ .imm = lower.imm(.i_s, inst.data.i.i) },
}),
.pseudo_cfi_adjust_cfa_offset_i_s => try lower.emit(.directive, .@".cfi_adjust_cfa_offset", &.{
.pseudo_cfi_adjust_cfa_offset_i_s => try lower.encode(.directive, .@".cfi_adjust_cfa_offset", &.{
.{ .imm = lower.imm(.i_s, inst.data.i.i) },
}),
.pseudo_cfi_offset_ri_s => try lower.emit(.directive, .@".cfi_offset", &.{
.pseudo_cfi_offset_ri_s => try lower.encode(.directive, .@".cfi_offset", &.{
.{ .reg = inst.data.ri.r1 },
.{ .imm = lower.imm(.ri_s, inst.data.ri.i) },
}),
.pseudo_cfi_val_offset_ri_s => try lower.emit(.directive, .@".cfi_val_offset", &.{
.pseudo_cfi_val_offset_ri_s => try lower.encode(.directive, .@".cfi_val_offset", &.{
.{ .reg = inst.data.ri.r1 },
.{ .imm = lower.imm(.ri_s, inst.data.ri.i) },
}),
.pseudo_cfi_rel_offset_ri_s => try lower.emit(.directive, .@".cfi_rel_offset", &.{
.pseudo_cfi_rel_offset_ri_s => try lower.encode(.directive, .@".cfi_rel_offset", &.{
.{ .reg = inst.data.ri.r1 },
.{ .imm = lower.imm(.ri_s, inst.data.ri.i) },
}),
.pseudo_cfi_register_rr => try lower.emit(.directive, .@".cfi_register", &.{
.pseudo_cfi_register_rr => try lower.encode(.directive, .@".cfi_register", &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
}),
.pseudo_cfi_restore_r => try lower.emit(.directive, .@".cfi_restore", &.{
.pseudo_cfi_restore_r => try lower.encode(.directive, .@".cfi_restore", &.{
.{ .reg = inst.data.r.r1 },
}),
.pseudo_cfi_undefined_r => try lower.emit(.directive, .@".cfi_undefined", &.{
.pseudo_cfi_undefined_r => try lower.encode(.directive, .@".cfi_undefined", &.{
.{ .reg = inst.data.r.r1 },
}),
.pseudo_cfi_same_value_r => try lower.emit(.directive, .@".cfi_same_value", &.{
.pseudo_cfi_same_value_r => try lower.encode(.directive, .@".cfi_same_value", &.{
.{ .reg = inst.data.r.r1 },
}),
.pseudo_cfi_remember_state_none => try lower.emit(.directive, .@".cfi_remember_state", &.{}),
.pseudo_cfi_restore_state_none => try lower.emit(.directive, .@".cfi_restore_state", &.{}),
.pseudo_cfi_escape_bytes => try lower.emit(.directive, .@".cfi_escape", &.{
.pseudo_cfi_remember_state_none => try lower.encode(.directive, .@".cfi_remember_state", &.{}),
.pseudo_cfi_restore_state_none => try lower.encode(.directive, .@".cfi_restore_state", &.{}),
.pseudo_cfi_escape_bytes => try lower.encode(.directive, .@".cfi_escape", &.{
.{ .bytes = inst.data.bytes.get(lower.mir) },
}),
@ -331,7 +323,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.pseudo_dbg_arg_i_s,
.pseudo_dbg_arg_i_u,
.pseudo_dbg_arg_i_64,
.pseudo_dbg_arg_reloc,
.pseudo_dbg_arg_ro,
.pseudo_dbg_arg_fa,
.pseudo_dbg_arg_m,
@ -341,7 +332,6 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.pseudo_dbg_var_i_s,
.pseudo_dbg_var_i_u,
.pseudo_dbg_var_i_64,
.pseudo_dbg_var_reloc,
.pseudo_dbg_var_ro,
.pseudo_dbg_var_fa,
.pseudo_dbg_var_m,
@ -362,7 +352,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
@branchHint(.cold);
assert(lower.err_msg == null);
lower.err_msg = try Zcu.ErrorMsg.create(lower.allocator, lower.src_loc, format, args);
lower.err_msg = try .create(lower.allocator, lower.src_loc, format, args);
return error.LowerFail;
}
@ -404,13 +394,17 @@ pub fn imm(lower: *const Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
};
}
pub fn mem(lower: *Lower, op_index: InstOpIndex, payload: u32) Memory {
var m = lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode();
fn mem(lower: *Lower, op_index: InstOpIndex, payload: u32) Memory {
var m = lower.mir.resolveMemoryExtra(payload).decode();
switch (m) {
.sib => |*sib| switch (sib.base) {
else => {},
.none, .reg, .frame => {},
.table => sib.disp = lower.reloc(op_index, .table, sib.disp).signed,
.rip_inst => |inst_index| sib.disp = lower.reloc(op_index, .{ .inst = inst_index }, sib.disp).signed,
.nav => |nav| sib.disp = lower.reloc(op_index, .{ .nav = nav }, sib.disp).signed,
.uav => |uav| sib.disp = lower.reloc(op_index, .{ .uav = uav }, sib.disp).signed,
.lazy_sym => |lazy_sym| sib.disp = lower.reloc(op_index, .{ .lazy_sym = lazy_sym }, sib.disp).signed,
.extern_func => |extern_func| sib.disp = lower.reloc(op_index, .{ .extern_func = extern_func }, sib.disp).signed,
},
else => {},
}
@ -428,172 +422,8 @@ fn reloc(lower: *Lower, op_index: InstOpIndex, target: Reloc.Target, off: i32) I
return .s(0);
}
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
const emit_prefix = prefix;
var emit_mnemonic = mnemonic;
var emit_ops_storage: [4]Operand = undefined;
const emit_ops = emit_ops_storage[0..ops.len];
for (emit_ops, ops, 0..) |*emit_op, op, op_index| {
emit_op.* = switch (op) {
else => op,
.mem => |mem_op| op: switch (mem_op.base()) {
else => op,
.reloc => |sym_index| {
assert(prefix == .none);
assert(mem_op.sib.disp == 0);
assert(mem_op.sib.scale_index.scale == 0);
if (lower.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
const elf_sym = zo.symbol(sym_index);
if (elf_sym.flags.is_tls) {
// TODO handle extern TLS vars, i.e., emit GD model
if (lower.pic) {
// Here, we currently assume local dynamic TLS vars, and so
// we emit LD model.
_ = lower.reloc(1, .{ .linker_tlsld = sym_index }, 0);
lower.result_insts[lower.result_insts_len] = try .new(.none, .lea, &.{
.{ .reg = .rdi },
.{ .mem = Memory.initRip(.none, 0) },
}, lower.target);
lower.result_insts_len += 1;
_ = lower.reloc(0, .{
.linker_extern_fn = try elf_file.getGlobalSymbol("__tls_get_addr", null),
}, 0);
lower.result_insts[lower.result_insts_len] = try .new(.none, .call, &.{
.{ .imm = .s(0) },
}, lower.target);
lower.result_insts_len += 1;
_ = lower.reloc(@intCast(op_index), .{ .linker_dtpoff = sym_index }, 0);
emit_mnemonic = .lea;
break :op .{ .mem = Memory.initSib(.none, .{
.base = .{ .reg = .rax },
.disp = std.math.minInt(i32),
}) };
} else {
// Since we are linking statically, we emit LE model directly.
lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{
.{ .reg = .rax },
.{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .fs } }) },
}, lower.target);
lower.result_insts_len += 1;
_ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0);
emit_mnemonic = .lea;
break :op .{ .mem = Memory.initSib(.none, .{
.base = .{ .reg = .rax },
.disp = std.math.minInt(i32),
}) };
}
}
if (lower.pic) switch (mnemonic) {
.lea => {
_ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0);
if (!elf_sym.flags.is_extern_ptr) break :op .{ .mem = Memory.initRip(.none, 0) };
emit_mnemonic = .mov;
break :op .{ .mem = Memory.initRip(.ptr, 0) };
},
.mov => {
if (elf_sym.flags.is_extern_ptr) {
const reg = ops[0].reg;
_ = lower.reloc(1, .{ .linker_reloc = sym_index }, 0);
lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{
.{ .reg = reg.to64() },
.{ .mem = Memory.initRip(.qword, 0) },
}, lower.target);
lower.result_insts_len += 1;
break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{
.reg = reg.to64(),
} }) };
}
_ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0);
break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) };
},
else => unreachable,
};
_ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0);
switch (mnemonic) {
.call => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{
.base = .{ .reg = .ds },
}) },
.lea => {
emit_mnemonic = .mov;
break :op .{ .imm = .s(0) };
},
.mov => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{
.base = .{ .reg = .ds },
}) },
else => unreachable,
}
} else if (lower.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
const macho_sym = zo.symbols.items[sym_index];
if (macho_sym.flags.tlv) {
_ = lower.reloc(1, .{ .linker_reloc = sym_index }, 0);
lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{
.{ .reg = .rdi },
.{ .mem = Memory.initRip(.ptr, 0) },
}, lower.target);
lower.result_insts_len += 1;
lower.result_insts[lower.result_insts_len] = try .new(.none, .call, &.{
.{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .rdi } }) },
}, lower.target);
lower.result_insts_len += 1;
emit_mnemonic = .mov;
break :op .{ .reg = .rax };
}
break :op switch (mnemonic) {
.lea => {
_ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0);
if (!macho_sym.flags.is_extern_ptr) break :op .{ .mem = Memory.initRip(.none, 0) };
emit_mnemonic = .mov;
break :op .{ .mem = Memory.initRip(.ptr, 0) };
},
.mov => {
if (macho_sym.flags.is_extern_ptr) {
const reg = ops[0].reg;
_ = lower.reloc(1, .{ .linker_reloc = sym_index }, 0);
lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{
.{ .reg = reg.to64() },
.{ .mem = Memory.initRip(.qword, 0) },
}, lower.target);
lower.result_insts_len += 1;
break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{
.reg = reg.to64(),
} }) };
}
_ = lower.reloc(@intCast(op_index), .{ .linker_reloc = sym_index }, 0);
break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) };
},
else => unreachable,
};
} else {
return lower.fail("TODO: bin format '{s}'", .{@tagName(lower.bin_file.tag)});
}
},
.pcrel => |sym_index| {
assert(prefix == .none);
assert(mem_op.sib.disp == 0);
assert(mem_op.sib.scale_index.scale == 0);
_ = lower.reloc(@intCast(op_index), .{ .linker_pcrel = sym_index }, 0);
break :op switch (lower.bin_file.tag) {
.elf => op,
.macho => switch (mnemonic) {
.lea => .{ .mem = Memory.initRip(.none, 0) },
.mov => .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) },
else => unreachable,
},
else => |tag| return lower.fail("TODO: bin format '{s}'", .{@tagName(tag)}),
};
},
},
};
}
lower.result_insts[lower.result_insts_len] = try .new(emit_prefix, emit_mnemonic, emit_ops, lower.target);
fn encode(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
lower.result_insts[lower.result_insts_len] = try .new(prefix, mnemonic, ops, lower.target);
lower.result_insts_len += 1;
}
@ -618,10 +448,10 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
.rrmi => inst.data.rrix.fixes,
.mi_u, .mi_s => inst.data.x.fixes,
.m => inst.data.x.fixes,
.extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc, .rel => ._,
.nav, .uav, .lazy_sym, .extern_func => ._,
else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),
};
try lower.emit(switch (fixes) {
try lower.encode(switch (fixes) {
inline else => |tag| comptime if (std.mem.indexOfScalar(u8, @tagName(tag), ' ')) |space|
@field(Prefix, @tagName(tag)[0..space])
else
@ -752,22 +582,17 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
.{ .mem = lower.mem(2, inst.data.rrix.payload) },
.{ .imm = lower.imm(inst.ops, inst.data.rrix.i) },
},
.extern_fn_reloc, .rel => &.{
.{ .imm = lower.reloc(0, .{ .linker_extern_fn = inst.data.reloc.sym_index }, inst.data.reloc.off) },
.nav => &.{
.{ .imm = lower.reloc(0, .{ .nav = inst.data.nav.index }, inst.data.nav.off) },
},
.got_reloc, .direct_reloc, .import_reloc => ops: {
const reg = inst.data.rx.r1;
const extra = lower.mir.extraData(bits.SymbolOffset, inst.data.rx.payload).data;
_ = lower.reloc(1, switch (inst.ops) {
.got_reloc => .{ .linker_got = extra.sym_index },
.direct_reloc => .{ .linker_direct = extra.sym_index },
.import_reloc => .{ .linker_import = extra.sym_index },
else => unreachable,
}, extra.off);
break :ops &.{
.{ .reg = reg },
.{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
};
.uav => &.{
.{ .imm = lower.reloc(0, .{ .uav = inst.data.uav }, 0) },
},
.lazy_sym => &.{
.{ .imm = lower.reloc(0, .{ .lazy_sym = inst.data.lazy_sym }, 0) },
},
.extern_func => &.{
.{ .imm = lower.reloc(0, .{ .extern_func = inst.data.extern_func }, 0) },
},
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
});
@ -787,7 +612,7 @@ fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Er
else => unreachable,
} });
while (it.next()) |i| {
try lower.emit(.none, mnemonic, &.{.{
try lower.encode(.none, mnemonic, &.{.{
.reg = callee_preserved_regs[i],
}});
switch (mnemonic) {
@ -801,7 +626,7 @@ fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Er
.push => {
var it = inst.data.reg_list.iterator(.{});
while (it.next()) |i| {
try lower.emit(.directive, .@".cfi_rel_offset", &.{
try lower.encode(.directive, .@".cfi_rel_offset", &.{
.{ .reg = callee_preserved_regs[i] },
.{ .imm = .s(off) },
});
@ -819,12 +644,14 @@ const page_size: i32 = 1 << 12;
const abi = @import("abi.zig");
const assert = std.debug.assert;
const bits = @import("bits.zig");
const codegen = @import("../../codegen.zig");
const encoder = @import("encoder.zig");
const link = @import("../../link.zig");
const std = @import("std");
const Immediate = Instruction.Immediate;
const Instruction = encoder.Instruction;
const InternPool = @import("../../InternPool.zig");
const Lower = @This();
const Memory = Instruction.Memory;
const Mir = @import("Mir.zig");
@ -833,3 +660,4 @@ const Zcu = @import("../../Zcu.zig");
const Operand = Instruction.Operand;
const Prefix = Instruction.Prefix;
const Register = bits.Register;
const Type = @import("../../Type.zig");

View File

@ -9,8 +9,8 @@
instructions: std.MultiArrayList(Inst).Slice,
/// The meaning of this data is determined by `Inst.Tag` value.
extra: []const u32,
local_name_bytes: []const u8,
local_types: []const InternPool.Index,
string_bytes: []const u8,
locals: []const Local,
table: []const Inst.Index,
frame_locs: std.MultiArrayList(FrameLoc).Slice,
@ -1363,9 +1363,6 @@ pub const Inst = struct {
/// Immediate (byte), register operands.
/// Uses `ri` payload.
ir,
/// Relative displacement operand.
/// Uses `reloc` payload.
rel,
/// Register, memory operands.
/// Uses `rx` payload with extra data of type `Memory`.
rm,
@ -1411,21 +1408,18 @@ pub const Inst = struct {
/// References another Mir instruction directly.
/// Uses `inst` payload.
inst,
/// Linker relocation - external function.
/// Uses `reloc` payload.
extern_fn_reloc,
/// Linker relocation - GOT indirection.
/// Uses `rx` payload with extra data of type `bits.SymbolOffset`.
got_reloc,
/// Linker relocation - direct reference.
/// Uses `rx` payload with extra data of type `bits.SymbolOffset`.
direct_reloc,
/// Linker relocation - imports table indirection (binding).
/// Uses `rx` payload with extra data of type `bits.SymbolOffset`.
import_reloc,
/// Linker relocation - threadlocal variable via GOT indirection.
/// Uses `rx` payload with extra data of type `bits.SymbolOffset`.
tlv_reloc,
/// References a nav.
/// Uses `nav` payload.
nav,
/// References an uav.
/// Uses `uav` payload.
uav,
/// References a lazy symbol.
/// Uses `lazy_sym` payload.
lazy_sym,
/// References an external symbol.
/// Uses `extern_func` payload.
extern_func,
// Pseudo instructions:
@ -1560,9 +1554,6 @@ pub const Inst = struct {
/// Uses `i64` payload.
pseudo_dbg_arg_i_64,
/// Local argument.
/// Uses `reloc` payload.
pseudo_dbg_arg_reloc,
/// Local argument.
/// Uses `ro` payload.
pseudo_dbg_arg_ro,
/// Local argument.
@ -1589,9 +1580,6 @@ pub const Inst = struct {
/// Uses `i64` payload.
pseudo_dbg_var_i_64,
/// Local variable.
/// Uses `reloc` payload.
pseudo_dbg_var_reloc,
/// Local variable.
/// Uses `ro` payload.
pseudo_dbg_var_ro,
/// Local variable.
@ -1719,12 +1707,12 @@ pub const Inst = struct {
return std.mem.sliceAsBytes(mir.extra[bytes.payload..])[0..bytes.len];
}
},
/// Relocation for the linker where:
/// * `sym_index` is the index of the target
/// * `off` is the offset from the target
reloc: bits.SymbolOffset,
fa: bits.FrameAddr,
ro: bits.RegisterOffset,
nav: bits.NavOffset,
uav: InternPool.Key.Ptr.BaseAddr.Uav,
lazy_sym: link.File.LazySymbol,
extern_func: Mir.NullTerminatedString,
/// Debug line and column position
line_column: struct {
line: u32,
@ -1787,7 +1775,7 @@ pub const Inst = struct {
pub const RegisterList = struct {
bitset: BitSet,
const BitSet = IntegerBitSet(32);
const BitSet = std.bit_set.IntegerBitSet(32);
const Self = @This();
pub const empty: RegisterList = .{ .bitset = .initEmpty() };
@ -1826,6 +1814,22 @@ pub const RegisterList = struct {
}
};
pub const NullTerminatedString = enum(u32) {
none = std.math.maxInt(u32),
_,
pub fn toSlice(nts: NullTerminatedString, mir: *const Mir) ?[:0]const u8 {
if (nts == .none) return null;
const string_bytes = mir.string_bytes[@intFromEnum(nts)..];
return string_bytes[0..std.mem.indexOfScalar(u8, string_bytes, 0).? :0];
}
};
pub const Local = struct {
name: NullTerminatedString,
type: InternPool.Index,
};
pub const Imm32 = struct {
imm: u32,
};
@ -1861,11 +1865,10 @@ pub const Memory = struct {
size: bits.Memory.Size,
index: Register,
scale: bits.Memory.Scale,
_: u14 = undefined,
_: u13 = undefined,
};
pub fn encode(mem: bits.Memory) Memory {
assert(mem.base != .reloc or mem.mod != .off);
return .{
.info = .{
.base = mem.base,
@ -1887,17 +1890,27 @@ pub const Memory = struct {
.none, .table => undefined,
.reg => |reg| @intFromEnum(reg),
.frame => |frame_index| @intFromEnum(frame_index),
.reloc, .pcrel => |sym_index| sym_index,
.rip_inst => |inst_index| inst_index,
.nav => |nav| @intFromEnum(nav),
.uav => |uav| @intFromEnum(uav.val),
.lazy_sym => |lazy_sym| @intFromEnum(lazy_sym.ty),
.extern_func => |extern_func| @intFromEnum(extern_func),
},
.off = switch (mem.mod) {
.rm => |rm| @bitCast(rm.disp),
.off => |off| @truncate(off),
},
.extra = if (mem.mod == .off)
@intCast(mem.mod.off >> 32)
else
undefined,
.extra = switch (mem.mod) {
.rm => switch (mem.base) {
else => undefined,
.uav => |uav| @intFromEnum(uav.orig_ty),
.lazy_sym => |lazy_sym| @intFromEnum(lazy_sym.kind),
},
.off => switch (mem.base) {
.reg => @intCast(mem.mod.off >> 32),
else => unreachable,
},
},
};
}
@ -1915,9 +1928,11 @@ pub const Memory = struct {
.reg => .{ .reg = @enumFromInt(mem.base) },
.frame => .{ .frame = @enumFromInt(mem.base) },
.table => .table,
.reloc => .{ .reloc = mem.base },
.pcrel => .{ .pcrel = mem.base },
.rip_inst => .{ .rip_inst = mem.base },
.nav => .{ .nav = @enumFromInt(mem.base) },
.uav => .{ .uav = .{ .val = @enumFromInt(mem.base), .orig_ty = @enumFromInt(mem.extra) } },
.lazy_sym => .{ .lazy_sym = .{ .kind = @enumFromInt(mem.extra), .ty = @enumFromInt(mem.base) } },
.extern_func => .{ .extern_func = @enumFromInt(mem.base) },
},
.scale_index = switch (mem.info.index) {
.none => null,
@ -1945,8 +1960,8 @@ pub const Memory = struct {
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
mir.instructions.deinit(gpa);
gpa.free(mir.extra);
gpa.free(mir.local_name_bytes);
gpa.free(mir.local_types);
gpa.free(mir.string_bytes);
gpa.free(mir.locals);
gpa.free(mir.table);
mir.frame_locs.deinit(gpa);
mir.* = undefined;
@ -1970,16 +1985,15 @@ pub fn emit(
const mod = zcu.navFileScope(nav).mod.?;
var e: Emit = .{
.lower = .{
.bin_file = lf,
.target = &mod.resolved_target.result,
.allocator = gpa,
.mir = mir,
.cc = fn_info.cc,
.src_loc = src_loc,
.output_mode = comp.config.output_mode,
.link_mode = comp.config.link_mode,
.pic = mod.pic,
},
.bin_file = lf,
.pt = pt,
.pic = mod.pic,
.atom_index = sym: {
if (lf.cast(.elf)) |ef| break :sym try ef.zigObjectPtr().?.getOrCreateMetadataForNav(zcu, nav);
if (lf.cast(.macho)) |mf| break :sym try mf.getZigObject().?.getOrCreateMetadataForNav(mf, nav);
@ -1992,6 +2006,7 @@ pub fn emit(
},
.debug_output = debug_output,
.code = code,
.prev_di_loc = .{
.line = func.lbrace_line,
.column = func.lbrace_column,
@ -2002,7 +2017,12 @@ pub fn emit(
},
},
.prev_di_pc = 0,
.code_offset_mapping = .empty,
.relocs = .empty,
.table_relocs = .empty,
};
defer e.deinit();
e.emitMir() catch |err| switch (err) {
error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, e.lower.err_msg.?),
error.InvalidInstruction, error.CannotEncode => return zcu.codegenFail(nav, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}),
@ -2010,6 +2030,62 @@ pub fn emit(
};
}
pub fn emitLazy(
mir: Mir,
lf: *link.File,
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
lazy_sym: link.File.LazySymbol,
code: *std.ArrayListUnmanaged(u8),
debug_output: link.File.DebugInfoOutput,
) codegen.CodeGenError!void {
const zcu = pt.zcu;
const comp = zcu.comp;
const gpa = comp.gpa;
const mod = comp.root_mod;
var e: Emit = .{
.lower = .{
.target = &mod.resolved_target.result,
.allocator = gpa,
.mir = mir,
.cc = .auto,
.src_loc = src_loc,
},
.bin_file = lf,
.pt = pt,
.pic = mod.pic,
.atom_index = sym: {
if (lf.cast(.elf)) |ef| break :sym ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_sym) catch |err|
return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
if (lf.cast(.macho)) |mf| break :sym mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_sym) catch |err|
return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
if (lf.cast(.coff)) |cf| {
const atom = cf.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err|
return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
break :sym cf.getAtom(atom).getSymbolIndex().?;
}
if (lf.cast(.plan9)) |p9f| break :sym p9f.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err|
return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
unreachable;
},
.debug_output = debug_output,
.code = code,
.prev_di_loc = undefined,
.prev_di_pc = undefined,
.code_offset_mapping = .empty,
.relocs = .empty,
.table_relocs = .empty,
};
defer e.deinit();
e.emitMir() catch |err| switch (err) {
error.LowerFail, error.EmitFail => return zcu.codegenFailTypeMsg(lazy_sym.ty, e.lower.err_msg.?),
error.InvalidInstruction, error.CannotEncode => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}),
else => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s}", .{@errorName(err)}),
};
}
pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: u32 } {
const fields = std.meta.fields(T);
var i: u32 = index;
@ -2039,9 +2115,10 @@ pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffse
return .{ .reg = frame_loc.base, .off = frame_loc.disp + frame_addr.off };
}
pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
pub fn resolveMemoryExtra(mir: Mir, payload: u32) Memory {
const mem = mir.extraData(Mir.Memory, payload).data;
return switch (mem.info.base) {
.none, .reg, .table, .reloc, .pcrel, .rip_inst => mem,
.none, .reg, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => mem,
.frame => if (mir.frame_locs.len > 0) .{
.info = .{
.base = .reg,
@ -2063,7 +2140,6 @@ const builtin = @import("builtin");
const encoder = @import("encoder.zig");
const std = @import("std");
const IntegerBitSet = std.bit_set.IntegerBitSet;
const InternPool = @import("../../InternPool.zig");
const Mir = @This();
const Register = bits.Register;

View File

@ -4,6 +4,8 @@ const expect = std.testing.expect;
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
const InternPool = @import("../../InternPool.zig");
const link = @import("../../link.zig");
const Mir = @import("Mir.zig");
/// EFLAGS condition codes
@ -750,20 +752,22 @@ pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
pub const RegisterOffset = struct { reg: Register, off: i32 = 0 };
pub const SymbolOffset = struct { sym_index: u32, off: i32 = 0 };
pub const NavOffset = struct { index: InternPool.Nav.Index, off: i32 = 0 };
pub const Memory = struct {
base: Base = .none,
mod: Mod = .{ .rm = .{} },
pub const Base = union(enum(u3)) {
pub const Base = union(enum(u4)) {
none,
reg: Register,
frame: FrameIndex,
table,
reloc: u32,
pcrel: u32,
rip_inst: Mir.Inst.Index,
nav: InternPool.Nav.Index,
uav: InternPool.Key.Ptr.BaseAddr.Uav,
lazy_sym: link.File.LazySymbol,
extern_func: Mir.NullTerminatedString,
pub const Tag = @typeInfo(Base).@"union".tag_type.?;
};
@ -899,7 +903,10 @@ pub const Memory = struct {
pub const Immediate = union(enum) {
signed: i32,
unsigned: u64,
reloc: SymbolOffset,
nav: NavOffset,
uav: InternPool.Key.Ptr.BaseAddr.Uav,
lazy_sym: link.File.LazySymbol,
extern_func: Mir.NullTerminatedString,
pub fn u(x: u64) Immediate {
return .{ .unsigned = x };
@ -909,10 +916,6 @@ pub const Immediate = union(enum) {
return .{ .signed = x };
}
pub fn rel(sym_off: SymbolOffset) Immediate {
return .{ .reloc = sym_off };
}
pub fn format(
imm: Immediate,
comptime _: []const u8,
@ -921,7 +924,10 @@ pub const Immediate = union(enum) {
) @TypeOf(writer).Error!void {
switch (imm) {
inline else => |int| try writer.print("{d}", .{int}),
.reloc => |sym_off| try writer.print("Symbol({[sym_index]d}) + {[off]d}", sym_off),
.nav => |nav_off| try writer.print("Nav({d}) + {d}", .{ @intFromEnum(nav_off.nav), nav_off.off }),
.uav => |uav| try writer.print("Uav({d})", .{@intFromEnum(uav.val)}),
.lazy_sym => |lazy_sym| try writer.print("LazySym({s}, {d})", .{ @tagName(lazy_sym.kind), @intFromEnum(lazy_sym.ty) }),
.extern_func => |extern_func| try writer.print("ExternFunc({d})", .{@intFromEnum(extern_func)}),
}
}
};

View File

@ -138,7 +138,7 @@ pub const Instruction = struct {
.moffs => true,
.rip => false,
.sib => |s| switch (s.base) {
.none, .frame, .table, .reloc, .pcrel, .rip_inst => false,
.none, .frame, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => false,
.reg => |reg| reg.isClass(.segment),
},
};
@ -211,7 +211,7 @@ pub const Instruction = struct {
.none, .imm => 0b00,
.reg => |reg| @truncate(reg.enc() >> 3),
.mem => |mem| switch (mem.base()) {
.none, .frame, .table, .reloc, .pcrel, .rip_inst => 0b00, // rsp, rbp, and rip are not extended
.none, .frame, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => 0b00, // rsp, rbp, and rip are not extended
.reg => |reg| @truncate(reg.enc() >> 3),
},
.bytes => unreachable,
@ -281,9 +281,14 @@ pub const Instruction = struct {
.reg => |reg| try writer.print("{s}", .{@tagName(reg)}),
.frame => |frame_index| try writer.print("{}", .{frame_index}),
.table => try writer.print("Table", .{}),
.reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}),
.pcrel => |sym_index| try writer.print("PcRelSymbol({d})", .{sym_index}),
.rip_inst => |inst_index| try writer.print("RipInst({d})", .{inst_index}),
.nav => |nav| try writer.print("Nav({d})", .{@intFromEnum(nav)}),
.uav => |uav| try writer.print("Uav({d})", .{@intFromEnum(uav.val)}),
.lazy_sym => |lazy_sym| try writer.print("LazySym({s}, {d})", .{
@tagName(lazy_sym.kind),
@intFromEnum(lazy_sym.ty),
}),
.extern_func => |extern_func| try writer.print("ExternFunc({d})", .{@intFromEnum(extern_func)}),
}
if (mem.scaleIndex()) |si| {
if (any) try writer.writeAll(" + ");
@ -718,11 +723,11 @@ pub const Instruction = struct {
try encoder.modRm_indirectDisp32(operand_enc, 0);
try encoder.disp32(undefined);
} else return error.CannotEncode,
.reloc => if (@TypeOf(encoder).options.allow_symbols) {
.nav, .uav, .lazy_sym, .extern_func => if (@TypeOf(encoder).options.allow_symbols) {
try encoder.modRm_indirectDisp32(operand_enc, 0);
try encoder.disp32(undefined);
} else return error.CannotEncode,
.pcrel, .rip_inst => {
.rip_inst => {
try encoder.modRm_RIPDisp32(operand_enc);
try encoder.disp32(sib.disp);
},

View File

@ -951,18 +951,17 @@ pub const GenResult = union(enum) {
};
};
fn genNavRef(
pub fn genNavRef(
lf: *link.File,
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
val: Value,
ty: Type,
nav_index: InternPool.Nav.Index,
target: std.Target,
) CodeGenError!GenResult {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const ty = val.typeOf(zcu);
log.debug("genNavRef: val = {}", .{val.fmtValue(pt)});
log.debug("genNavRef: ty = {}", .{ty.fmt(pt)});
if (!ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
const imm: u64 = switch (@divExact(target.ptrBitWidth(), 8)) {
@ -991,12 +990,10 @@ fn genNavRef(
}
const nav = ip.getNav(nav_index);
assert(!nav.isThreadlocal(ip));
const lib_name, const linkage, const visibility = if (nav.getExtern(ip)) |e|
.{ e.lib_name, e.linkage, e.visibility }
const lib_name, const linkage, const is_threadlocal = if (nav.getExtern(ip)) |e|
.{ e.lib_name, e.linkage, e.is_threadlocal and !zcu.navFileScope(nav_index).mod.?.single_threaded }
else
.{ .none, .internal, .default };
.{ .none, .internal, false };
const name = nav.name;
if (lf.cast(.elf)) |elf_file| {
@ -1004,6 +1001,7 @@ fn genNavRef(
switch (linkage) {
.internal => {
const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
return .{ .mcv = .{ .lea_symbol = sym_index } };
},
.strong, .weak => {
@ -1014,10 +1012,7 @@ fn genNavRef(
.weak => zo.symbol(sym_index).flags.weak = true,
.link_once => unreachable,
}
switch (visibility) {
.default => zo.symbol(sym_index).flags.is_extern_ptr = true,
.hidden, .protected => {},
}
if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
return .{ .mcv = .{ .lea_symbol = sym_index } };
},
.link_once => unreachable,
@ -1027,8 +1022,8 @@ fn genNavRef(
switch (linkage) {
.internal => {
const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
const sym = zo.symbols.items[sym_index];
return .{ .mcv = .{ .lea_symbol = sym.nlist_idx } };
if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
return .{ .mcv = .{ .lea_symbol = sym_index } };
},
.strong, .weak => {
const sym_index = try macho_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip));
@ -1038,10 +1033,7 @@ fn genNavRef(
.weak => zo.symbols.items[sym_index].flags.weak = true,
.link_once => unreachable,
}
switch (visibility) {
.default => zo.symbols.items[sym_index].flags.is_extern_ptr = true,
.hidden, .protected => {},
}
if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
return .{ .mcv = .{ .lea_symbol = sym_index } };
},
.link_once => unreachable,
@ -1071,6 +1063,7 @@ fn genNavRef(
}
}
/// deprecated legacy code path
pub fn genTypedValue(
lf: *link.File,
pt: Zcu.PerThread,
@ -1078,45 +1071,97 @@ pub fn genTypedValue(
val: Value,
target: std.Target,
) CodeGenError!GenResult {
const ip = &pt.zcu.intern_pool;
return switch (try lowerValue(pt, val, &target)) {
.none => .{ .mcv = .none },
.undef => .{ .mcv = .undef },
.immediate => |imm| .{ .mcv = .{ .immediate = imm } },
.lea_nav => |nav| genNavRef(lf, pt, src_loc, .fromInterned(ip.getNav(nav).typeOf(ip)), nav, target),
.lea_uav => |uav| switch (try lf.lowerUav(
pt,
uav.val,
Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu),
src_loc,
)) {
.mcv => |mcv| .{ .mcv = switch (mcv) {
else => unreachable,
.load_direct => |sym_index| .{ .lea_direct = sym_index },
.load_symbol => |sym_index| .{ .lea_symbol = sym_index },
} },
.fail => |em| .{ .fail = em },
},
.load_uav => |uav| lf.lowerUav(
pt,
uav.val,
Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu),
src_loc,
),
};
}
const LowerResult = union(enum) {
none,
undef,
/// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets
/// such as ARM, the immediate will never exceed 32-bits.
immediate: u64,
lea_nav: InternPool.Nav.Index,
lea_uav: InternPool.Key.Ptr.BaseAddr.Uav,
load_uav: InternPool.Key.Ptr.BaseAddr.Uav,
};
pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allocator.Error!LowerResult {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const ty = val.typeOf(zcu);
log.debug("genTypedValue: val = {}", .{val.fmtValue(pt)});
log.debug("lowerValue(@as({}, {}))", .{ ty.fmt(pt), val.fmtValue(pt) });
if (val.isUndef(zcu)) return .{ .mcv = .undef };
if (val.isUndef(zcu)) return .undef;
switch (ty.zigTypeTag(zcu)) {
.void => return .{ .mcv = .none },
.void => return .none,
.pointer => switch (ty.ptrSize(zcu)) {
.slice => {},
else => switch (val.toIntern()) {
.null_value => {
return .{ .mcv = .{ .immediate = 0 } };
return .{ .immediate = 0 };
},
else => switch (ip.indexToKey(val.toIntern())) {
.int => {
return .{ .mcv = .{ .immediate = val.toUnsignedInt(zcu) } };
return .{ .immediate = val.toUnsignedInt(zcu) };
},
.ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
.nav => |nav| return genNavRef(lf, pt, src_loc, val, nav, target),
.uav => |uav| if (Value.fromInterned(uav.val).typeOf(zcu).hasRuntimeBits(zcu))
return switch (try lf.lowerUav(
pt,
uav.val,
Type.fromInterned(uav.orig_ty).ptrAlignment(zcu),
src_loc,
)) {
.mcv => |mcv| return .{ .mcv = switch (mcv) {
.load_direct => |sym_index| .{ .lea_direct = sym_index },
.load_symbol => |sym_index| .{ .lea_symbol = sym_index },
.nav => |nav| {
if (!ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
const imm: u64 = switch (@divExact(target.ptrBitWidth(), 8)) {
1 => 0xaa,
2 => 0xaaaa,
4 => 0xaaaaaaaa,
8 => 0xaaaaaaaaaaaaaaaa,
else => unreachable,
} },
.fail => |em| return .{ .fail = em },
};
return .{ .immediate = imm };
}
if (ty.castPtrToFn(zcu)) |fn_ty| {
if (zcu.typeToFunc(fn_ty).?.is_generic) {
return .{ .immediate = fn_ty.abiAlignment(zcu).toByteUnits().? };
}
} else if (ty.zigTypeTag(zcu) == .pointer) {
const elem_ty = ty.elemType2(zcu);
if (!elem_ty.hasRuntimeBits(zcu)) {
return .{ .immediate = elem_ty.abiAlignment(zcu).toByteUnits().? };
}
}
return .{ .lea_nav = nav };
},
.uav => |uav| if (Value.fromInterned(uav.val).typeOf(zcu).hasRuntimeBits(zcu))
return .{ .lea_uav = uav }
else
return .{ .mcv = .{ .immediate = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu)
.forward(@intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() | 1)) / 3)) } },
return .{ .immediate = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu)
.forward(@intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() | 1)) / 3)) },
else => {},
},
else => {},
@ -1130,39 +1175,35 @@ pub fn genTypedValue(
.signed => @bitCast(val.toSignedInt(zcu)),
.unsigned => val.toUnsignedInt(zcu),
};
return .{ .mcv = .{ .immediate = unsigned } };
return .{ .immediate = unsigned };
}
},
.bool => {
return .{ .mcv = .{ .immediate = @intFromBool(val.toBool()) } };
return .{ .immediate = @intFromBool(val.toBool()) };
},
.optional => {
if (ty.isPtrLikeOptional(zcu)) {
return genTypedValue(
lf,
return lowerValue(
pt,
src_loc,
val.optionalValue(zcu) orelse return .{ .mcv = .{ .immediate = 0 } },
val.optionalValue(zcu) orelse return .{ .immediate = 0 },
target,
);
} else if (ty.abiSize(zcu) == 1) {
return .{ .mcv = .{ .immediate = @intFromBool(!val.isNull(zcu)) } };
return .{ .immediate = @intFromBool(!val.isNull(zcu)) };
}
},
.@"enum" => {
const enum_tag = ip.indexToKey(val.toIntern()).enum_tag;
return genTypedValue(
lf,
return lowerValue(
pt,
src_loc,
Value.fromInterned(enum_tag.int),
target,
);
},
.error_set => {
const err_name = ip.indexToKey(val.toIntern()).err.name;
const error_index = try pt.getErrorValue(err_name);
return .{ .mcv = .{ .immediate = error_index } };
const error_index = ip.getErrorValueIfExists(err_name).?;
return .{ .immediate = error_index };
},
.error_union => {
const err_type = ty.errorUnionSet(zcu);
@ -1171,20 +1212,16 @@ pub fn genTypedValue(
// We use the error type directly as the type.
const err_int_ty = try pt.errorIntType();
switch (ip.indexToKey(val.toIntern()).error_union.val) {
.err_name => |err_name| return genTypedValue(
lf,
.err_name => |err_name| return lowerValue(
pt,
src_loc,
Value.fromInterned(try pt.intern(.{ .err = .{
.ty = err_type.toIntern(),
.name = err_name,
} })),
target,
),
.payload => return genTypedValue(
lf,
.payload => return lowerValue(
pt,
src_loc,
try pt.intValue(err_int_ty, 0),
target,
),
@ -1204,7 +1241,10 @@ pub fn genTypedValue(
else => {},
}
return lf.lowerUav(pt, val.toIntern(), .none, src_loc);
return .{ .load_uav = .{
.val = val.toIntern(),
.orig_ty = (try pt.singleConstPtrType(ty)).toIntern(),
} };
}
pub fn errUnionPayloadOffset(payload_ty: Type, zcu: *Zcu) u64 {

View File

@ -1478,16 +1478,16 @@ pub const WipNav = struct {
pub fn genLocalVarDebugInfo(
wip_nav: *WipNav,
tag: LocalVarTag,
name: []const u8,
opt_name: ?[]const u8,
ty: Type,
loc: Loc,
) UpdateError!void {
assert(wip_nav.func != .none);
try wip_nav.abbrevCode(switch (tag) {
.arg => .arg,
.local_var => .local_var,
.arg => if (opt_name) |_| .arg else .unnamed_arg,
.local_var => if (opt_name) |_| .local_var else unreachable,
});
try wip_nav.strp(name);
if (opt_name) |name| try wip_nav.strp(name);
try wip_nav.refType(ty);
try wip_nav.infoExprLoc(loc);
wip_nav.any_children = true;
@ -1498,7 +1498,7 @@ pub const WipNav = struct {
wip_nav: *WipNav,
src_loc: Zcu.LazySrcLoc,
tag: LocalConstTag,
name: []const u8,
opt_name: ?[]const u8,
val: Value,
) UpdateError!void {
assert(wip_nav.func != .none);
@ -1508,19 +1508,19 @@ pub const WipNav = struct {
const has_runtime_bits = ty.hasRuntimeBits(zcu);
const has_comptime_state = ty.comptimeOnly(zcu) and try ty.onePossibleValue(pt) == null;
try wip_nav.abbrevCode(if (has_runtime_bits and has_comptime_state) switch (tag) {
.comptime_arg => .comptime_arg_runtime_bits_comptime_state,
.local_const => .local_const_runtime_bits_comptime_state,
.comptime_arg => if (opt_name) |_| .comptime_arg_runtime_bits_comptime_state else .unnamed_comptime_arg_runtime_bits_comptime_state,
.local_const => if (opt_name) |_| .local_const_runtime_bits_comptime_state else unreachable,
} else if (has_comptime_state) switch (tag) {
.comptime_arg => .comptime_arg_comptime_state,
.local_const => .local_const_comptime_state,
.comptime_arg => if (opt_name) |_| .comptime_arg_comptime_state else .unnamed_comptime_arg_comptime_state,
.local_const => if (opt_name) |_| .local_const_comptime_state else unreachable,
} else if (has_runtime_bits) switch (tag) {
.comptime_arg => .comptime_arg_runtime_bits,
.local_const => .local_const_runtime_bits,
.comptime_arg => if (opt_name) |_| .comptime_arg_runtime_bits else .unnamed_comptime_arg_runtime_bits,
.local_const => if (opt_name) |_| .local_const_runtime_bits else unreachable,
} else switch (tag) {
.comptime_arg => .comptime_arg,
.local_const => .local_const,
.comptime_arg => if (opt_name) |_| .comptime_arg else .unnamed_comptime_arg,
.local_const => if (opt_name) |_| .local_const else unreachable,
});
try wip_nav.strp(name);
if (opt_name) |name| try wip_nav.strp(name);
try wip_nav.refType(ty);
if (has_runtime_bits) try wip_nav.blockValue(src_loc, val);
if (has_comptime_state) try wip_nav.refValue(val);
@ -4945,10 +4945,15 @@ const AbbrevCode = enum {
empty_inlined_func,
inlined_func,
arg,
unnamed_arg,
comptime_arg,
unnamed_comptime_arg,
comptime_arg_runtime_bits,
unnamed_comptime_arg_runtime_bits,
comptime_arg_comptime_state,
unnamed_comptime_arg_comptime_state,
comptime_arg_runtime_bits_comptime_state,
unnamed_comptime_arg_runtime_bits_comptime_state,
local_var,
local_const,
local_const_runtime_bits,
@ -5734,6 +5739,13 @@ const AbbrevCode = enum {
.{ .location, .exprloc },
},
},
.unnamed_arg = .{
.tag = .formal_parameter,
.attrs = &.{
.{ .type, .ref_addr },
.{ .location, .exprloc },
},
},
.comptime_arg = .{
.tag = .formal_parameter,
.attrs = &.{
@ -5742,6 +5754,13 @@ const AbbrevCode = enum {
.{ .type, .ref_addr },
},
},
.unnamed_comptime_arg = .{
.tag = .formal_parameter,
.attrs = &.{
.{ .const_expr, .flag_present },
.{ .type, .ref_addr },
},
},
.comptime_arg_runtime_bits = .{
.tag = .formal_parameter,
.attrs = &.{
@ -5751,6 +5770,14 @@ const AbbrevCode = enum {
.{ .const_value, .block },
},
},
.unnamed_comptime_arg_runtime_bits = .{
.tag = .formal_parameter,
.attrs = &.{
.{ .const_expr, .flag_present },
.{ .type, .ref_addr },
.{ .const_value, .block },
},
},
.comptime_arg_comptime_state = .{
.tag = .formal_parameter,
.attrs = &.{
@ -5760,6 +5787,14 @@ const AbbrevCode = enum {
.{ .ZIG_comptime_value, .ref_addr },
},
},
.unnamed_comptime_arg_comptime_state = .{
.tag = .formal_parameter,
.attrs = &.{
.{ .const_expr, .flag_present },
.{ .type, .ref_addr },
.{ .ZIG_comptime_value, .ref_addr },
},
},
.comptime_arg_runtime_bits_comptime_state = .{
.tag = .formal_parameter,
.attrs = &.{
@ -5770,6 +5805,15 @@ const AbbrevCode = enum {
.{ .ZIG_comptime_value, .ref_addr },
},
},
.unnamed_comptime_arg_runtime_bits_comptime_state = .{
.tag = .formal_parameter,
.attrs = &.{
.{ .const_expr, .flag_present },
.{ .type, .ref_addr },
.{ .const_value, .block },
.{ .ZIG_comptime_value, .ref_addr },
},
},
.local_var = .{
.tag = .variable,
.attrs = &.{

View File

@ -462,9 +462,6 @@ pub const Flags = packed struct {
/// Whether the symbol is a TLS variable.
is_tls: bool = false,
/// Whether the symbol is an extern pointer (as opposed to function).
is_extern_ptr: bool = false,
};
pub const Extra = struct {

View File

@ -1542,11 +1542,7 @@ pub fn updateNav(
nav.name.toSlice(ip),
@"extern".lib_name.toSlice(ip),
);
if (!ip.isFunctionType(@"extern".ty)) {
const sym = self.symbol(sym_index);
sym.flags.is_extern_ptr = true;
if (@"extern".is_threadlocal) sym.flags.is_tls = true;
}
if (@"extern".is_threadlocal) self.symbol(sym_index).flags.is_tls = true;
if (self.dwarf) |*dwarf| dwarf: {
var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf;
defer debug_wip_nav.deinit();

View File

@ -389,9 +389,6 @@ pub const Flags = packed struct {
/// ZigObject specific flags
/// Whether the symbol has a trampoline
trampoline: bool = false,
/// Whether the symbol is an extern pointer (as opposed to function).
is_extern_ptr: bool = false,
};
pub const SectionFlags = packed struct(u8) {

View File

@ -881,11 +881,7 @@ pub fn updateNav(
const name = @"extern".name.toSlice(ip);
const lib_name = @"extern".lib_name.toSlice(ip);
const sym_index = try self.getGlobalSymbol(macho_file, name, lib_name);
if (!ip.isFunctionType(@"extern".ty)) {
const sym = &self.symbols.items[sym_index];
sym.flags.is_extern_ptr = true;
if (@"extern".is_threadlocal) sym.flags.tlv = true;
}
if (@"extern".is_threadlocal) self.symbols.items[sym_index].flags.tlv = true;
if (self.dwarf) |*dwarf| dwarf: {
var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index) orelse break :dwarf;
defer debug_wip_nav.deinit();

View File

@ -850,7 +850,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt
},
.separate_thread => switch (backend) {
.stage2_llvm => false,
.stage2_c, .stage2_wasm => true,
.stage2_c, .stage2_wasm, .stage2_x86_64 => true,
// TODO: most self-hosted backends should be able to support this without too much work.
else => false,
},