| 1 | //! This file contains the functionality for emitting RISC-V MIR as machine code |
| 2 | |
| 3 | bin_file: *link.File, |
| 4 | lower: Lower, |
| 5 | debug_output: link.File.DebugInfoOutput, |
| 6 | w: *std.Io.Writer, |
| 7 | |
| 8 | prev_di_line: u32, |
| 9 | prev_di_column: u32, |
| 10 | /// Relative to the beginning of `code`. |
| 11 | prev_di_pc: usize, |
| 12 | |
| 13 | code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, |
| 14 | relocs: std.ArrayList(Reloc) = .empty, |
| 15 | |
| 16 | pub const Error = Lower.Error || std.Io.Writer.Error || error{ |
| 17 | EmitFail, |
| 18 | }; |
| 19 | |
| 20 | pub 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(@backingInt(symbol.atom_index)).atom(elf_file).?; |
| 51 | const sym = zo.symbol(@backingInt(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 = @backingInt(std.elf.R_RISCV.HI20); |
| 58 | const lo_r_type: u32 = @backingInt(std.elf.R_RISCV.LO12_I); |
| 59 | |
| 60 | try atom_ptr.addReloc(gpa, .{ |
| 61 | .r_offset = start_offset, |
| 62 | .r_info = (@as(u64, @backingInt(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, @backingInt(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(@backingInt(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, @backingInt(symbol.sym_index)) << 32) | @backingInt(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, @backingInt(symbol.sym_index)) << 32) | @backingInt(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, @backingInt(symbol.sym_index)) << 32) | @backingInt(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(@backingInt(symbol.atom_index)).atom(elf_file).?; |
| 102 | |
| 103 | const r_type: u32 = @backingInt(std.elf.R_RISCV.CALL_PLT); |
| 104 | |
| 105 | try atom_ptr.addReloc(gpa, .{ |
| 106 | .r_offset = start_offset, |
| 107 | .r_info = (@as(u64, @backingInt(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 | |
| 154 | pub 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 | |
| 160 | const 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 | |
| 171 | fn 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 | |
| 188 | fn 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 | |
| 205 | fn 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 | |
| 212 | const link = @import("../../link.zig"); |
| 213 | const log = std.log.scoped(.emit); |
| 214 | const mem = std.mem; |
| 215 | const std = @import("std"); |
| 216 | |
| 217 | const Emit = @This(); |
| 218 | const Lower = @import("Lower.zig"); |
| 219 | const Mir = @import("Mir.zig"); |
| 220 | const riscv_util = @import("../../link/riscv.zig"); |
| 221 | const Elf = @import("../../link/Elf.zig"); |
| 222 | const encoding = @import("encoding.zig"); |