master
  1//! This file contains the functionality for emitting RISC-V MIR as machine code
  2
  3bin_file: *link.File,
  4lower: Lower,
  5debug_output: link.File.DebugInfoOutput,
  6w: *std.Io.Writer,
  7
  8prev_di_line: u32,
  9prev_di_column: u32,
 10/// Relative to the beginning of `code`.
 11prev_di_pc: usize,
 12
 13code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
 14relocs: std.ArrayList(Reloc) = .empty,
 15
 16pub const Error = Lower.Error || std.Io.Writer.Error || error{
 17    EmitFail,
 18};
 19
 20pub fn emitMir(emit: *Emit) Error!void {
 21    const gpa = emit.bin_file.comp.gpa;
 22    log.debug("mir instruction len: {}", .{emit.lower.mir.instructions.len});
 23    for (0..emit.lower.mir.instructions.len) |mir_i| {
 24        const mir_index: Mir.Inst.Index = @intCast(mir_i);
 25        try emit.code_offset_mapping.putNoClobber(
 26            emit.lower.allocator,
 27            mir_index,
 28            @intCast(emit.w.end),
 29        );
 30        const lowered = try emit.lower.lowerMir(mir_index, .{ .allow_frame_locs = true });
 31        var lowered_relocs = lowered.relocs;
 32        for (lowered.insts, 0..) |lowered_inst, lowered_index| {
 33            const start_offset: u32 = @intCast(emit.w.end);
 34            try emit.w.writeInt(u32, lowered_inst.toU32(), .little);
 35
 36            while (lowered_relocs.len > 0 and
 37                lowered_relocs[0].lowered_inst_index == lowered_index) : ({
 38                lowered_relocs = lowered_relocs[1..];
 39            }) switch (lowered_relocs[0].target) {
 40                .inst => |target| try emit.relocs.append(emit.lower.allocator, .{
 41                    .source = start_offset,
 42                    .target = target,
 43                    .offset = 0,
 44                    .fmt = std.meta.activeTag(lowered_inst),
 45                }),
 46                .load_symbol_reloc => |symbol| {
 47                    const elf_file = emit.bin_file.cast(.elf).?;
 48                    const zo = elf_file.zigObjectPtr().?;
 49
 50                    const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
 51                    const sym = zo.symbol(symbol.sym_index);
 52
 53                    if (emit.lower.pic) {
 54                        return emit.fail("know when to emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)});
 55                    }
 56
 57                    const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
 58                    const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
 59
 60                    try atom_ptr.addReloc(gpa, .{
 61                        .r_offset = start_offset,
 62                        .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
 63                        .r_addend = 0,
 64                    }, zo);
 65
 66                    try atom_ptr.addReloc(gpa, .{
 67                        .r_offset = start_offset + 4,
 68                        .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
 69                        .r_addend = 0,
 70                    }, zo);
 71                },
 72                .load_tlv_reloc => |symbol| {
 73                    const elf_file = emit.bin_file.cast(.elf).?;
 74                    const zo = elf_file.zigObjectPtr().?;
 75
 76                    const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
 77
 78                    const R_RISCV = std.elf.R_RISCV;
 79
 80                    try atom_ptr.addReloc(gpa, .{
 81                        .r_offset = start_offset,
 82                        .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20),
 83                        .r_addend = 0,
 84                    }, zo);
 85
 86                    try atom_ptr.addReloc(gpa, .{
 87                        .r_offset = start_offset + 4,
 88                        .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD),
 89                        .r_addend = 0,
 90                    }, zo);
 91
 92                    try atom_ptr.addReloc(gpa, .{
 93                        .r_offset = start_offset + 8,
 94                        .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I),
 95                        .r_addend = 0,
 96                    }, zo);
 97                },
 98                .call_extern_fn_reloc => |symbol| {
 99                    const elf_file = emit.bin_file.cast(.elf).?;
100                    const zo = elf_file.zigObjectPtr().?;
101                    const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
102
103                    const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);
104
105                    try atom_ptr.addReloc(gpa, .{
106                        .r_offset = start_offset,
107                        .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
108                        .r_addend = 0,
109                    }, zo);
110                },
111            };
112        }
113        std.debug.assert(lowered_relocs.len == 0);
114
115        if (lowered.insts.len == 0) {
116            const mir_inst = emit.lower.mir.instructions.get(mir_index);
117            switch (mir_inst.tag) {
118                else => unreachable,
119                .pseudo_dbg_prologue_end => {
120                    switch (emit.debug_output) {
121                        .dwarf => |dw| {
122                            try dw.setPrologueEnd();
123                            log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
124                                emit.prev_di_line, emit.prev_di_column,
125                            });
126                            try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
127                        },
128                        .none => {},
129                    }
130                },
131                .pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine(
132                    mir_inst.data.pseudo_dbg_line_column.line,
133                    mir_inst.data.pseudo_dbg_line_column.column,
134                ),
135                .pseudo_dbg_epilogue_begin => {
136                    switch (emit.debug_output) {
137                        .dwarf => |dw| {
138                            try dw.setEpilogueBegin();
139                            log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
140                                emit.prev_di_line, emit.prev_di_column,
141                            });
142                            try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
143                        },
144                        .none => {},
145                    }
146                },
147                .pseudo_dead => {},
148            }
149        }
150    }
151    try emit.fixupRelocs();
152}
153
154pub fn deinit(emit: *Emit) void {
155    emit.relocs.deinit(emit.lower.allocator);
156    emit.code_offset_mapping.deinit(emit.lower.allocator);
157    emit.* = undefined;
158}
159
160const Reloc = struct {
161    /// Offset of the instruction.
162    source: usize,
163    /// Target of the relocation.
164    target: Mir.Inst.Index,
165    /// Offset of the relocation within the instruction.
166    offset: u32,
167    /// Format of the instruction, used to determine how to modify it.
168    fmt: encoding.Lir.Format,
169};
170
171fn fixupRelocs(emit: *Emit) Error!void {
172    for (emit.relocs.items) |reloc| {
173        log.debug("target inst: {f}", .{emit.lower.mir.instructions.get(reloc.target)});
174        const target = emit.code_offset_mapping.get(reloc.target) orelse
175            return emit.fail("relocation target not found!", .{});
176
177        const disp = @as(i32, @intCast(target)) - @as(i32, @intCast(reloc.source));
178        const code = emit.w.buffered()[reloc.source + reloc.offset ..][0..4];
179
180        switch (reloc.fmt) {
181            .J => riscv_util.writeInstJ(code, @bitCast(disp)),
182            .B => riscv_util.writeInstB(code, @bitCast(disp)),
183            else => return emit.fail("tried to reloc format type {s}", .{@tagName(reloc.fmt)}),
184        }
185    }
186}
187
188fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
189    const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line);
190    const delta_pc: usize = emit.w.end - emit.prev_di_pc;
191    log.debug("  (advance pc={d} and line={d})", .{ delta_pc, delta_line });
192    switch (emit.debug_output) {
193        .dwarf => |dw| {
194            if (column != emit.prev_di_column) try dw.setColumn(column);
195            if (delta_line == 0) return; // TODO: fix these edge cases.
196            try dw.advancePCAndLine(delta_line, delta_pc);
197            emit.prev_di_line = line;
198            emit.prev_di_column = column;
199            emit.prev_di_pc = emit.w.end;
200        },
201        .none => {},
202    }
203}
204
205fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
206    return switch (emit.lower.fail(format, args)) {
207        error.LowerFail => error.EmitFail,
208        else => |e| e,
209    };
210}
211
212const link = @import("../../link.zig");
213const log = std.log.scoped(.emit);
214const mem = std.mem;
215const std = @import("std");
216
217const Emit = @This();
218const Lower = @import("Lower.zig");
219const Mir = @import("Mir.zig");
220const riscv_util = @import("../../link/riscv.zig");
221const Elf = @import("../../link/Elf.zig");
222const encoding = @import("encoding.zig");