master
  1prologue: []const Instruction,
  2body: []const Instruction,
  3epilogue: []const Instruction,
  4literals: []const u32,
  5nav_relocs: []const Reloc.Nav,
  6uav_relocs: []const Reloc.Uav,
  7lazy_relocs: []const Reloc.Lazy,
  8global_relocs: []const Reloc.Global,
  9literal_relocs: []const Reloc.Literal,
 10
 11pub const Reloc = struct {
 12    label: u32,
 13    addend: u64 align(@alignOf(u32)) = 0,
 14
 15    pub const Nav = struct {
 16        nav: InternPool.Nav.Index,
 17        reloc: Reloc,
 18    };
 19
 20    pub const Uav = struct {
 21        uav: InternPool.Key.Ptr.BaseAddr.Uav,
 22        reloc: Reloc,
 23    };
 24
 25    pub const Lazy = struct {
 26        symbol: link.File.LazySymbol,
 27        reloc: Reloc,
 28    };
 29
 30    pub const Global = struct {
 31        name: [*:0]const u8,
 32        reloc: Reloc,
 33    };
 34
 35    pub const Literal = struct {
 36        label: u32,
 37    };
 38};
 39
 40pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
 41    assert(mir.body.ptr + mir.body.len == mir.prologue.ptr);
 42    assert(mir.prologue.ptr + mir.prologue.len == mir.epilogue.ptr);
 43    gpa.free(mir.body.ptr[0 .. mir.body.len + mir.prologue.len + mir.epilogue.len]);
 44    gpa.free(mir.literals);
 45    gpa.free(mir.nav_relocs);
 46    gpa.free(mir.uav_relocs);
 47    gpa.free(mir.lazy_relocs);
 48    gpa.free(mir.global_relocs);
 49    gpa.free(mir.literal_relocs);
 50    mir.* = undefined;
 51}
 52
 53pub fn emit(
 54    mir: Mir,
 55    lf: *link.File,
 56    pt: Zcu.PerThread,
 57    src_loc: Zcu.LazySrcLoc,
 58    func_index: InternPool.Index,
 59    atom_index: u32,
 60    w: *std.Io.Writer,
 61    debug_output: link.File.DebugInfoOutput,
 62) !void {
 63    _ = debug_output;
 64    const zcu = pt.zcu;
 65    const ip = &zcu.intern_pool;
 66    const func = zcu.funcInfo(func_index);
 67    const nav = ip.getNav(func.owner_nav);
 68    const mod = zcu.navFileScope(func.owner_nav).mod.?;
 69    const target = &mod.resolved_target.result;
 70    mir_log.debug("{f}:", .{nav.fqn.fmt(ip)});
 71
 72    const func_align = switch (nav.status.fully_resolved.alignment) {
 73        .none => switch (mod.optimize_mode) {
 74            .Debug, .ReleaseSafe, .ReleaseFast => target_util.defaultFunctionAlignment(target),
 75            .ReleaseSmall => target_util.minFunctionAlignment(target),
 76        },
 77        else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
 78    };
 79    const code_len = mir.prologue.len + mir.body.len + mir.epilogue.len;
 80    const literals_align_gap = -%code_len & (@divExact(
 81        @as(u5, @intCast(func_align.minStrict(.@"16").toByteUnits().?)),
 82        Instruction.size,
 83    ) - 1);
 84    try w.rebase(w.end, Instruction.size * (code_len + literals_align_gap + mir.literals.len));
 85    emitInstructionsForward(w, mir.prologue) catch unreachable;
 86    emitInstructionsBackward(w, mir.body) catch unreachable;
 87    const body_end: u32 = @intCast(w.end);
 88    emitInstructionsBackward(w, mir.epilogue) catch unreachable;
 89    w.splatByteAll(0, Instruction.size * literals_align_gap) catch unreachable;
 90    w.writeAll(@ptrCast(mir.literals)) catch unreachable;
 91    mir_log.debug("", .{});
 92
 93    for (mir.nav_relocs) |nav_reloc| try emitReloc(
 94        lf,
 95        zcu,
 96        atom_index,
 97        switch (try @import("../../codegen.zig").genNavRef(
 98            lf,
 99            pt,
100            src_loc,
101            nav_reloc.nav,
102            &mod.resolved_target.result,
103        )) {
104            .sym_index => |sym_index| sym_index,
105            .fail => |em| return zcu.codegenFailMsg(func.owner_nav, em),
106        },
107        mir.body[nav_reloc.reloc.label],
108        body_end - Instruction.size * (1 + nav_reloc.reloc.label),
109        nav_reloc.reloc.addend,
110        if (ip.getNav(nav_reloc.nav).getExtern(ip)) |_| .got_load else .direct,
111    );
112    for (mir.uav_relocs) |uav_reloc| try emitReloc(
113        lf,
114        zcu,
115        atom_index,
116        switch (try lf.lowerUav(
117            pt,
118            uav_reloc.uav.val,
119            ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu),
120            src_loc,
121        )) {
122            .sym_index => |sym_index| sym_index,
123            .fail => |em| return zcu.codegenFailMsg(func.owner_nav, em),
124        },
125        mir.body[uav_reloc.reloc.label],
126        body_end - Instruction.size * (1 + uav_reloc.reloc.label),
127        uav_reloc.reloc.addend,
128        .direct,
129    );
130    for (mir.lazy_relocs) |lazy_reloc| try emitReloc(
131        lf,
132        zcu,
133        atom_index,
134        if (lf.cast(.elf)) |ef|
135            ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_reloc.symbol) catch |err|
136                return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
137        else if (lf.cast(.macho)) |mf|
138            mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_reloc.symbol) catch |err|
139                return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
140        else
141            return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}),
142        mir.body[lazy_reloc.reloc.label],
143        body_end - Instruction.size * (1 + lazy_reloc.reloc.label),
144        lazy_reloc.reloc.addend,
145        .direct,
146    );
147    for (mir.global_relocs) |global_reloc| try emitReloc(
148        lf,
149        zcu,
150        atom_index,
151        if (lf.cast(.elf)) |ef|
152            try ef.getGlobalSymbol(std.mem.span(global_reloc.name), null)
153        else if (lf.cast(.macho)) |mf|
154            try mf.getGlobalSymbol(std.mem.span(global_reloc.name), null)
155        else
156            return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}),
157        mir.body[global_reloc.reloc.label],
158        body_end - Instruction.size * (1 + global_reloc.reloc.label),
159        global_reloc.reloc.addend,
160        .direct,
161    );
162    const literal_reloc_offset: i19 = @intCast(mir.epilogue.len + literals_align_gap);
163    for (mir.literal_relocs) |literal_reloc| {
164        var instruction = mir.body[literal_reloc.label];
165        instruction.load_store.register_literal.group.imm19 += literal_reloc_offset;
166        instruction.write(
167            w.buffered()[body_end - Instruction.size * (1 + literal_reloc.label) ..][0..Instruction.size],
168        );
169    }
170}
171
172fn emitInstructionsForward(w: *std.Io.Writer, instructions: []const Instruction) !void {
173    for (instructions) |instruction| try emitInstruction(w, instruction);
174}
175fn emitInstructionsBackward(w: *std.Io.Writer, instructions: []const Instruction) !void {
176    var instruction_index = instructions.len;
177    while (instruction_index > 0) {
178        instruction_index -= 1;
179        try emitInstruction(w, instructions[instruction_index]);
180    }
181}
182fn emitInstruction(w: *std.Io.Writer, instruction: Instruction) !void {
183    mir_log.debug("    {f}", .{instruction});
184    instruction.write(try w.writableArray(Instruction.size));
185}
186
187fn emitReloc(
188    lf: *link.File,
189    zcu: *Zcu,
190    atom_index: u32,
191    sym_index: u32,
192    instruction: Instruction,
193    offset: u32,
194    addend: u64,
195    kind: enum { direct, got_load },
196) !void {
197    const gpa = zcu.gpa;
198    switch (instruction.decode()) {
199        else => unreachable,
200        .data_processing_immediate => |decoded| if (lf.cast(.elf)) |ef| {
201            const zo = ef.zigObjectPtr().?;
202            const atom = zo.symbol(atom_index).atom(ef).?;
203            const r_type: std.elf.R_AARCH64 = switch (decoded.decode()) {
204                else => unreachable,
205                .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) {
206                    .adr => switch (kind) {
207                        .direct => .ADR_PREL_LO21,
208                        .got_load => unreachable,
209                    },
210                    .adrp => switch (kind) {
211                        .direct => .ADR_PREL_PG_HI21,
212                        .got_load => .ADR_GOT_PAGE,
213                    },
214                },
215                .add_subtract_immediate => |add_subtract_immediate| switch (add_subtract_immediate.group.op) {
216                    .add => switch (kind) {
217                        .direct => .ADD_ABS_LO12_NC,
218                        .got_load => unreachable,
219                    },
220                    .sub => unreachable,
221                },
222            };
223            try atom.addReloc(gpa, .{
224                .r_offset = offset,
225                .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
226                .r_addend = @bitCast(addend),
227            }, zo);
228        } else if (lf.cast(.macho)) |mf| {
229            const zo = mf.getZigObject().?;
230            const atom = zo.symbols.items[atom_index].getAtom(mf).?;
231            switch (decoded.decode()) {
232                else => unreachable,
233                .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) {
234                    .adr => unreachable,
235                    .adrp => try atom.addReloc(mf, .{
236                        .tag = .@"extern",
237                        .offset = offset,
238                        .target = sym_index,
239                        .addend = @bitCast(addend),
240                        .type = switch (kind) {
241                            .direct => .page,
242                            .got_load => .got_load_page,
243                        },
244                        .meta = .{
245                            .pcrel = true,
246                            .has_subtractor = false,
247                            .length = 2,
248                            .symbolnum = @intCast(sym_index),
249                        },
250                    }),
251                },
252                .add_subtract_immediate => |add_subtract_immediate| switch (add_subtract_immediate.group.op) {
253                    .add => try atom.addReloc(mf, .{
254                        .tag = .@"extern",
255                        .offset = offset,
256                        .target = sym_index,
257                        .addend = @bitCast(addend),
258                        .type = switch (kind) {
259                            .direct => .pageoff,
260                            .got_load => .got_load_pageoff,
261                        },
262                        .meta = .{
263                            .pcrel = false,
264                            .has_subtractor = false,
265                            .length = 2,
266                            .symbolnum = @intCast(sym_index),
267                        },
268                    }),
269                    .sub => unreachable,
270                },
271            }
272        },
273        .branch_exception_generating_system => |decoded| if (lf.cast(.elf)) |ef| {
274            const zo = ef.zigObjectPtr().?;
275            const atom = zo.symbol(atom_index).atom(ef).?;
276            const r_type: std.elf.R_AARCH64 = switch (decoded.decode().unconditional_branch_immediate.group.op) {
277                .b => .JUMP26,
278                .bl => .CALL26,
279            };
280            try atom.addReloc(gpa, .{
281                .r_offset = offset,
282                .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
283                .r_addend = @bitCast(addend),
284            }, zo);
285        } else if (lf.cast(.macho)) |mf| {
286            const zo = mf.getZigObject().?;
287            const atom = zo.symbols.items[atom_index].getAtom(mf).?;
288            try atom.addReloc(mf, .{
289                .tag = .@"extern",
290                .offset = offset,
291                .target = sym_index,
292                .addend = @bitCast(addend),
293                .type = .branch,
294                .meta = .{
295                    .pcrel = true,
296                    .has_subtractor = false,
297                    .length = 2,
298                    .symbolnum = @intCast(sym_index),
299                },
300            });
301        },
302        .load_store => |decoded| if (lf.cast(.elf)) |ef| {
303            const zo = ef.zigObjectPtr().?;
304            const atom = zo.symbol(atom_index).atom(ef).?;
305            const r_type: std.elf.R_AARCH64 = switch (decoded.decode().register_unsigned_immediate.decode()) {
306                .integer => |integer| switch (integer.decode()) {
307                    .unallocated, .prfm => unreachable,
308                    .strb, .ldrb, .ldrsb => switch (kind) {
309                        .direct => .LDST8_ABS_LO12_NC,
310                        .got_load => unreachable,
311                    },
312                    .strh, .ldrh, .ldrsh => switch (kind) {
313                        .direct => .LDST16_ABS_LO12_NC,
314                        .got_load => unreachable,
315                    },
316                    .ldrsw => switch (kind) {
317                        .direct => .LDST32_ABS_LO12_NC,
318                        .got_load => unreachable,
319                    },
320                    inline .str, .ldr => |encoded, mnemonic| switch (encoded.sf) {
321                        .word => .LDST32_ABS_LO12_NC,
322                        .doubleword => switch (kind) {
323                            .direct => .LDST64_ABS_LO12_NC,
324                            .got_load => switch (mnemonic) {
325                                else => comptime unreachable,
326                                .str => unreachable,
327                                .ldr => .LD64_GOT_LO12_NC,
328                            },
329                        },
330                    },
331                },
332                .vector => |vector| switch (kind) {
333                    .direct => switch (vector.group.opc1.decode(vector.group.size)) {
334                        .byte => .LDST8_ABS_LO12_NC,
335                        .half => .LDST16_ABS_LO12_NC,
336                        .single => .LDST32_ABS_LO12_NC,
337                        .double => .LDST64_ABS_LO12_NC,
338                        .quad => .LDST128_ABS_LO12_NC,
339                    },
340                    .got_load => unreachable,
341                },
342            };
343            try atom.addReloc(gpa, .{
344                .r_offset = offset,
345                .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
346                .r_addend = @bitCast(addend),
347            }, zo);
348        } else if (lf.cast(.macho)) |mf| {
349            const zo = mf.getZigObject().?;
350            const atom = zo.symbols.items[atom_index].getAtom(mf).?;
351            try atom.addReloc(mf, .{
352                .tag = .@"extern",
353                .offset = offset,
354                .target = sym_index,
355                .addend = @bitCast(addend),
356                .type = switch (kind) {
357                    .direct => .pageoff,
358                    .got_load => .got_load_pageoff,
359                },
360                .meta = .{
361                    .pcrel = false,
362                    .has_subtractor = false,
363                    .length = 2,
364                    .symbolnum = @intCast(sym_index),
365                },
366            });
367        },
368    }
369}
370
371const Air = @import("../../Air.zig");
372const assert = std.debug.assert;
373const mir_log = std.log.scoped(.mir);
374const Instruction = @import("encoding.zig").Instruction;
375const InternPool = @import("../../InternPool.zig");
376const link = @import("../../link.zig");
377const Mir = @This();
378const std = @import("std");
379const target_util = @import("../../target.zig");
380const Zcu = @import("../../Zcu.zig");
381const ZigType = @import("../../Type.zig");