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");