1//! This file contains the functionality for lowering AArch64 MIR into 2//! machine code 3 4const Emit = @This(); 5const std = @import("std"); 6const math = std.math; 7const Mir = @import("Mir.zig"); 8const bits = @import("bits.zig"); 9const link = @import("../../link.zig"); 10const Module = @import("../../Module.zig"); 11const ErrorMsg = Module.ErrorMsg; 12const assert = std.debug.assert; 13const DW = std.dwarf; 14const leb128 = std.leb; 15const Instruction = bits.Instruction; 16const Register = bits.Register; 17const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; 18 19mir: Mir, 20bin_file: *link.File, 21debug_output: DebugInfoOutput, 22target: *const std.Target, 23err_msg: ?*ErrorMsg = null, 24src_loc: Module.SrcLoc, 25code: *std.ArrayList(u8), 26 27prev_di_line: u32, 28prev_di_column: u32, 29/// Relative to the beginning of `code`. 30prev_di_pc: usize, 31 32const InnerError = error{ 33 OutOfMemory, 34 EmitFail, 35}; 36 37pub fn emitMir( 38 emit: *Emit, 39) InnerError!void { 40 const mir_tags = emit.mir.instructions.items(.tag); 41 42 // Emit machine code 43 for (mir_tags) |tag, index| { 44 const inst = @intCast(u32, index); 45 switch (tag) { 46 .addi => try emit.mirIType(inst), 47 .jalr => try emit.mirIType(inst), 48 .ld => try emit.mirIType(inst), 49 .sd => try emit.mirIType(inst), 50 51 .ebreak => try emit.mirSystem(inst), 52 .ecall => try emit.mirSystem(inst), 53 54 .dbg_line => try emit.mirDbgLine(inst), 55 56 .dbg_prologue_end => try emit.mirDebugPrologueEnd(), 57 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), 58 59 .nop => try emit.mirNop(inst), 60 .ret => try emit.mirNop(inst), 61 62 .lui => try emit.mirUType(inst), 63 } 64 } 65} 66 67pub fn deinit(emit: *Emit) void { 68 emit.* = undefined; 69} 70 71fn writeInstruction(emit: *Emit, instruction: Instruction) !void { 72 const endian = emit.target.cpu.arch.endian(); 73 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian); 74} 75 76fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { 77 @setCold(true); 78 assert(emit.err_msg == null); 79 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); 80 return error.EmitFail; 81} 82 83fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { 84 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); 85 const delta_pc: usize = self.code.items.len - self.prev_di_pc; 86 switch (self.debug_output) { 87 .dwarf => |dbg_out| { 88 // TODO Look into using the DWARF special opcodes to compress this data. 89 // It lets you emit single-byte opcodes that add different numbers to 90 // both the PC and the line number at the same time. 91 try dbg_out.dbg_line.ensureUnusedCapacity(11); 92 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); 93 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; 94 if (delta_line != 0) { 95 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); 96 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; 97 } 98 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); 99 self.prev_di_pc = self.code.items.len; 100 self.prev_di_line = line; 101 self.prev_di_column = column; 102 self.prev_di_pc = self.code.items.len; 103 }, 104 .plan9 => |dbg_out| { 105 if (delta_pc <= 0) return; // only do this when the pc changes 106 // we have already checked the target in the linker to make sure it is compatable 107 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable; 108 109 // increasing the line number 110 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); 111 // increasing the pc 112 const d_pc_p9 = @intCast(i64, delta_pc) - quant; 113 if (d_pc_p9 > 0) { 114 // minus one because if its the last one, we want to leave space to change the line which is one quanta 115 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant); 116 if (dbg_out.pcop_change_index.*) |pci| 117 dbg_out.dbg_line.items[pci] += 1; 118 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1); 119 } else if (d_pc_p9 == 0) { 120 // we don't need to do anything, because adding the quant does it for us 121 } else unreachable; 122 if (dbg_out.start_line.* == null) 123 dbg_out.start_line.* = self.prev_di_line; 124 dbg_out.end_line.* = line; 125 // only do this if the pc changed 126 self.prev_di_line = line; 127 self.prev_di_column = column; 128 self.prev_di_pc = self.code.items.len; 129 }, 130 .none => {}, 131 } 132} 133 134fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void { 135 const tag = emit.mir.instructions.items(.tag)[inst]; 136 const i_type = emit.mir.instructions.items(.data)[inst].i_type; 137 138 switch (tag) { 139 .addi => try emit.writeInstruction(Instruction.addi(i_type.rd, i_type.rs1, i_type.imm12)), 140 .jalr => try emit.writeInstruction(Instruction.jalr(i_type.rd, i_type.imm12, i_type.rs1)), 141 .ld => try emit.writeInstruction(Instruction.ld(i_type.rd, i_type.imm12, i_type.rs1)), 142 .sd => try emit.writeInstruction(Instruction.sd(i_type.rd, i_type.imm12, i_type.rs1)), 143 else => unreachable, 144 } 145} 146 147fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void { 148 const tag = emit.mir.instructions.items(.tag)[inst]; 149 150 switch (tag) { 151 .ebreak => try emit.writeInstruction(Instruction.ebreak), 152 .ecall => try emit.writeInstruction(Instruction.ecall), 153 else => unreachable, 154 } 155} 156 157fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { 158 const tag = emit.mir.instructions.items(.tag)[inst]; 159 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; 160 161 switch (tag) { 162 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column), 163 else => unreachable, 164 } 165} 166 167fn mirDebugPrologueEnd(self: *Emit) !void { 168 switch (self.debug_output) { 169 .dwarf => |dbg_out| { 170 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); 171 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); 172 }, 173 .plan9 => {}, 174 .none => {}, 175 } 176} 177 178fn mirDebugEpilogueBegin(self: *Emit) !void { 179 switch (self.debug_output) { 180 .dwarf => |dbg_out| { 181 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); 182 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); 183 }, 184 .plan9 => {}, 185 .none => {}, 186 } 187} 188 189fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void { 190 const tag = emit.mir.instructions.items(.tag)[inst]; 191 const u_type = emit.mir.instructions.items(.data)[inst].u_type; 192 193 switch (tag) { 194 .lui => try emit.writeInstruction(Instruction.lui(u_type.rd, u_type.imm20)), 195 else => unreachable, 196 } 197} 198 199fn mirNop(emit: *Emit, inst: Mir.Inst.Index) !void { 200 const tag = emit.mir.instructions.items(.tag)[inst]; 201 202 switch (tag) { 203 .nop => try emit.writeInstruction(Instruction.addi(.zero, .zero, 0)), 204 .ret => try emit.writeInstruction(Instruction.jalr(.zero, 0, .ra)), 205 else => unreachable, 206 } 207} 208