mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 22:09:49 +00:00
x86_64: remove linker references from codegen
This commit is contained in:
parent
c95b1bf2d3
commit
ba53b14028
@ -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;
|
||||
|
||||
@ -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});
|
||||
|
||||
@ -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
@ -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");
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)}),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -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);
|
||||
},
|
||||
|
||||
156
src/codegen.zig
156
src/codegen.zig
@ -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 {
|
||||
|
||||
@ -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 = &.{
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user