mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
223 lines
8.8 KiB
Zig
223 lines
8.8 KiB
Zig
//! This file contains the functionality for emitting RISC-V MIR as machine code
|
|
|
|
bin_file: *link.File,
|
|
lower: Lower,
|
|
debug_output: link.File.DebugInfoOutput,
|
|
w: *std.Io.Writer,
|
|
|
|
prev_di_line: u32,
|
|
prev_di_column: u32,
|
|
/// Relative to the beginning of `code`.
|
|
prev_di_pc: usize,
|
|
|
|
code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
|
|
relocs: std.ArrayListUnmanaged(Reloc) = .empty,
|
|
|
|
pub const Error = Lower.Error || std.Io.Writer.Error || error{
|
|
EmitFail,
|
|
};
|
|
|
|
pub fn emitMir(emit: *Emit) Error!void {
|
|
const gpa = emit.bin_file.comp.gpa;
|
|
log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len});
|
|
for (0..emit.lower.mir.instructions.len) |mir_i| {
|
|
const mir_index: Mir.Inst.Index = @intCast(mir_i);
|
|
try emit.code_offset_mapping.putNoClobber(
|
|
emit.lower.allocator,
|
|
mir_index,
|
|
@intCast(emit.w.end),
|
|
);
|
|
const lowered = try emit.lower.lowerMir(mir_index, .{ .allow_frame_locs = true });
|
|
var lowered_relocs = lowered.relocs;
|
|
for (lowered.insts, 0..) |lowered_inst, lowered_index| {
|
|
const start_offset: u32 = @intCast(emit.w.end);
|
|
try emit.w.writeInt(u32, lowered_inst.toU32(), .little);
|
|
|
|
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| try emit.relocs.append(emit.lower.allocator, .{
|
|
.source = start_offset,
|
|
.target = target,
|
|
.offset = 0,
|
|
.fmt = std.meta.activeTag(lowered_inst),
|
|
}),
|
|
.load_symbol_reloc => |symbol| {
|
|
const elf_file = emit.bin_file.cast(.elf).?;
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
|
|
const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
|
|
const sym = zo.symbol(symbol.sym_index);
|
|
|
|
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);
|
|
const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
|
|
|
|
try atom_ptr.addReloc(gpa, .{
|
|
.r_offset = start_offset,
|
|
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
|
|
.r_addend = 0,
|
|
}, zo);
|
|
|
|
try atom_ptr.addReloc(gpa, .{
|
|
.r_offset = start_offset + 4,
|
|
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
|
|
.r_addend = 0,
|
|
}, zo);
|
|
},
|
|
.load_tlv_reloc => |symbol| {
|
|
const elf_file = emit.bin_file.cast(.elf).?;
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
|
|
const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
|
|
|
|
const R_RISCV = std.elf.R_RISCV;
|
|
|
|
try atom_ptr.addReloc(gpa, .{
|
|
.r_offset = start_offset,
|
|
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20),
|
|
.r_addend = 0,
|
|
}, zo);
|
|
|
|
try atom_ptr.addReloc(gpa, .{
|
|
.r_offset = start_offset + 4,
|
|
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD),
|
|
.r_addend = 0,
|
|
}, zo);
|
|
|
|
try atom_ptr.addReloc(gpa, .{
|
|
.r_offset = start_offset + 8,
|
|
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I),
|
|
.r_addend = 0,
|
|
}, zo);
|
|
},
|
|
.call_extern_fn_reloc => |symbol| {
|
|
const elf_file = emit.bin_file.cast(.elf).?;
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
|
|
|
|
const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);
|
|
|
|
try atom_ptr.addReloc(gpa, .{
|
|
.r_offset = start_offset,
|
|
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
|
|
.r_addend = 0,
|
|
}, zo);
|
|
},
|
|
};
|
|
}
|
|
std.debug.assert(lowered_relocs.len == 0);
|
|
|
|
if (lowered.insts.len == 0) {
|
|
const mir_inst = emit.lower.mir.instructions.get(mir_index);
|
|
switch (mir_inst.tag) {
|
|
else => unreachable,
|
|
.pseudo_dbg_prologue_end => {
|
|
switch (emit.debug_output) {
|
|
.dwarf => |dw| {
|
|
try dw.setPrologueEnd();
|
|
log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
|
|
emit.prev_di_line, emit.prev_di_column,
|
|
});
|
|
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
|
|
},
|
|
.none => {},
|
|
}
|
|
},
|
|
.pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine(
|
|
mir_inst.data.pseudo_dbg_line_column.line,
|
|
mir_inst.data.pseudo_dbg_line_column.column,
|
|
),
|
|
.pseudo_dbg_epilogue_begin => {
|
|
switch (emit.debug_output) {
|
|
.dwarf => |dw| {
|
|
try dw.setEpilogueBegin();
|
|
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
|
|
emit.prev_di_line, emit.prev_di_column,
|
|
});
|
|
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
|
|
},
|
|
.none => {},
|
|
}
|
|
},
|
|
.pseudo_dead => {},
|
|
}
|
|
}
|
|
}
|
|
try emit.fixupRelocs();
|
|
}
|
|
|
|
pub fn deinit(emit: *Emit) void {
|
|
emit.relocs.deinit(emit.lower.allocator);
|
|
emit.code_offset_mapping.deinit(emit.lower.allocator);
|
|
emit.* = undefined;
|
|
}
|
|
|
|
const Reloc = struct {
|
|
/// Offset of the instruction.
|
|
source: usize,
|
|
/// Target of the relocation.
|
|
target: Mir.Inst.Index,
|
|
/// Offset of the relocation within the instruction.
|
|
offset: u32,
|
|
/// Format of the instruction, used to determine how to modify it.
|
|
fmt: encoding.Lir.Format,
|
|
};
|
|
|
|
fn fixupRelocs(emit: *Emit) Error!void {
|
|
for (emit.relocs.items) |reloc| {
|
|
log.debug("target inst: {f}", .{emit.lower.mir.instructions.get(reloc.target)});
|
|
const target = emit.code_offset_mapping.get(reloc.target) orelse
|
|
return emit.fail("relocation target not found!", .{});
|
|
|
|
const disp = @as(i32, @intCast(target)) - @as(i32, @intCast(reloc.source));
|
|
const code = emit.w.buffered()[reloc.source + reloc.offset ..][0..4];
|
|
|
|
switch (reloc.fmt) {
|
|
.J => riscv_util.writeInstJ(code, @bitCast(disp)),
|
|
.B => riscv_util.writeInstB(code, @bitCast(disp)),
|
|
else => return emit.fail("tried to reloc format type {s}", .{@tagName(reloc.fmt)}),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
|
|
const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line);
|
|
const delta_pc: usize = emit.w.end - emit.prev_di_pc;
|
|
log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
|
|
switch (emit.debug_output) {
|
|
.dwarf => |dw| {
|
|
if (column != emit.prev_di_column) try dw.setColumn(column);
|
|
if (delta_line == 0) return; // TODO: fix these edge cases.
|
|
try dw.advancePCAndLine(delta_line, delta_pc);
|
|
emit.prev_di_line = line;
|
|
emit.prev_di_column = column;
|
|
emit.prev_di_pc = emit.w.end;
|
|
},
|
|
.none => {},
|
|
}
|
|
}
|
|
|
|
fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
|
|
return switch (emit.lower.fail(format, args)) {
|
|
error.LowerFail => error.EmitFail,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
|
|
const link = @import("../../link.zig");
|
|
const log = std.log.scoped(.emit);
|
|
const mem = std.mem;
|
|
const std = @import("std");
|
|
|
|
const Emit = @This();
|
|
const Lower = @import("Lower.zig");
|
|
const Mir = @import("Mir.zig");
|
|
const riscv_util = @import("../../link/riscv.zig");
|
|
const Elf = @import("../../link/Elf.zig");
|
|
const encoding = @import("encoding.zig");
|