authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-11-05 16:21:01+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-05 15:30:28-04:00
log0fb26b03690eb327164bbc7a21ba2d69d3b1a5b8
tree89fc6873b8dde43180bc115599aa59842340e25b
parent2551946a5102a72b4120b70d400ce4c9ff517415

stage2 RISCV64: introduce MIR


3 files changed, 463 insertions(+), 99 deletions(-)

src/arch/riscv64/CodeGen.zig+146-99
...@@ -5,6 +5,8 @@ const math = std.math;...@@ -5,6 +5,8 @@ const math = std.math;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const Air = @import("../../Air.zig");6const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");7const Zir = @import("../../Zir.zig");
8const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");
8const Liveness = @import("../../Liveness.zig");10const Liveness = @import("../../Liveness.zig");
9const Type = @import("../../type.zig").Type;11const Type = @import("../../type.zig").Type;
10const Value = @import("../../value.zig").Value;12const Value = @import("../../value.zig").Value;
...@@ -47,13 +49,14 @@ arg_index: usize,...@@ -47,13 +49,14 @@ arg_index: usize,
47src_loc: Module.SrcLoc,49src_loc: Module.SrcLoc,
48stack_align: u32,50stack_align: u32,
4951
50prev_di_line: u32,52/// MIR Instructions
51prev_di_column: u32,53mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
54/// MIR extra data
55mir_extra: std.ArrayListUnmanaged(u32) = .{},
56
52/// Byte offset within the source file of the ending curly.57/// Byte offset within the source file of the ending curly.
53end_di_line: u32,58end_di_line: u32,
54end_di_column: u32,59end_di_column: u32,
55/// Relative to the beginning of `code`.
56prev_di_pc: usize,
5760
58/// The value is an offset into the `Function` `code` from the beginning.61/// The value is an offset into the `Function` `code` from the beginning.
59/// To perform the reloc, write 32-bit signed little-endian integer62/// To perform the reloc, write 32-bit signed little-endian integer
...@@ -276,9 +279,6 @@ pub fn generate(...@@ -276,9 +279,6 @@ pub fn generate(
276 .branch_stack = &branch_stack,279 .branch_stack = &branch_stack,
277 .src_loc = src_loc,280 .src_loc = src_loc,
278 .stack_align = undefined,281 .stack_align = undefined,
279 .prev_di_pc = 0,
280 .prev_di_line = module_fn.lbrace_line,
281 .prev_di_column = module_fn.lbrace_column,
282 .end_di_line = module_fn.rbrace_line,282 .end_di_line = module_fn.rbrace_line,
283 .end_di_column = module_fn.rbrace_column,283 .end_di_column = module_fn.rbrace_column,
284 };284 };
...@@ -302,6 +302,30 @@ pub fn generate(...@@ -302,6 +302,30 @@ pub fn generate(
302 else => |e| return e,302 else => |e| return e,
303 };303 };
304304
305 var mir = Mir{
306 .instructions = function.mir_instructions.toOwnedSlice(),
307 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
308 };
309 defer mir.deinit(bin_file.allocator);
310
311 var emit = Emit{
312 .mir = mir,
313 .bin_file = bin_file,
314 .debug_output = debug_output,
315 .target = &bin_file.options.target,
316 .src_loc = src_loc,
317 .code = code,
318 .prev_di_pc = 0,
319 .prev_di_line = module_fn.lbrace_line,
320 .prev_di_column = module_fn.lbrace_column,
321 };
322 defer emit.deinit();
323
324 emit.emitMir() catch |err| switch (err) {
325 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
326 else => |e| return e,
327 };
328
305 if (function.err_msg) |em| {329 if (function.err_msg) |em| {
306 return FnResult{ .fail = em };330 return FnResult{ .fail = em };
307 } else {331 } else {
...@@ -309,13 +333,56 @@ pub fn generate(...@@ -309,13 +333,56 @@ pub fn generate(
309 }333 }
310}334}
311335
336fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
337 const gpa = self.gpa;
338
339 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
340
341 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
342 self.mir_instructions.appendAssumeCapacity(inst);
343 return result_index;
344}
345
346pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
347 const fields = std.meta.fields(@TypeOf(extra));
348 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
349 return self.addExtraAssumeCapacity(extra);
350}
351
352pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
353 const fields = std.meta.fields(@TypeOf(extra));
354 const result = @intCast(u32, self.mir_extra.items.len);
355 inline for (fields) |field| {
356 self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
357 u32 => @field(extra, field.name),
358 i32 => @bitCast(u32, @field(extra, field.name)),
359 else => @compileError("bad field type"),
360 });
361 }
362 return result;
363}
364
312fn gen(self: *Self) !void {365fn gen(self: *Self) !void {
313 try self.dbgSetPrologueEnd();366 _ = try self.addInst(.{
367 .tag = .dbg_prologue_end,
368 .data = .{ .nop = {} },
369 });
370
314 try self.genBody(self.air.getMainBody());371 try self.genBody(self.air.getMainBody());
315 try self.dbgSetEpilogueBegin();372
373 _ = try self.addInst(.{
374 .tag = .dbg_epilogue_begin,
375 .data = .{ .nop = {} },
376 });
316377
317 // Drop them off at the rbrace.378 // Drop them off at the rbrace.
318 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);379 _ = try self.addInst(.{
380 .tag = .dbg_line,
381 .data = .{ .dbg_line_column = .{
382 .line = self.end_di_line,
383 .column = self.end_di_column,
384 } },
385 });
319}386}
320387
321fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {388fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
...@@ -456,79 +523,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -456,79 +523,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
456 }523 }
457}524}
458525
459fn dbgSetPrologueEnd(self: *Self) InnerError!void {
460 switch (self.debug_output) {
461 .dwarf => |dbg_out| {
462 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
463 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
464 },
465 .plan9 => {},
466 .none => {},
467 }
468}
469
470fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
471 switch (self.debug_output) {
472 .dwarf => |dbg_out| {
473 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
474 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
475 },
476 .plan9 => {},
477 .none => {},
478 }
479}
480
481fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
482 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
483 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
484 switch (self.debug_output) {
485 .dwarf => |dbg_out| {
486 // TODO Look into using the DWARF special opcodes to compress this data.
487 // It lets you emit single-byte opcodes that add different numbers to
488 // both the PC and the line number at the same time.
489 try dbg_out.dbg_line.ensureUnusedCapacity(11);
490 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
491 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
492 if (delta_line != 0) {
493 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
494 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
495 }
496 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
497 self.prev_di_pc = self.code.items.len;
498 self.prev_di_line = line;
499 self.prev_di_column = column;
500 self.prev_di_pc = self.code.items.len;
501 },
502 .plan9 => |dbg_out| {
503 if (delta_pc <= 0) return; // only do this when the pc changes
504 // we have already checked the target in the linker to make sure it is compatable
505 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
506
507 // increasing the line number
508 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
509 // increasing the pc
510 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
511 if (d_pc_p9 > 0) {
512 // minus one because if its the last one, we want to leave space to change the line which is one quanta
513 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
514 if (dbg_out.pcop_change_index.*) |pci|
515 dbg_out.dbg_line.items[pci] += 1;
516 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
517 } else if (d_pc_p9 == 0) {
518 // we don't need to do anything, because adding the quant does it for us
519 } else unreachable;
520 if (dbg_out.start_line.* == null)
521 dbg_out.start_line.* = self.prev_di_line;
522 dbg_out.end_line.* = line;
523 // only do this if the pc changed
524 self.prev_di_line = line;
525 self.prev_di_column = column;
526 self.prev_di_pc = self.code.items.len;
527 },
528 .none => {},
529 }
530}
531
532/// Asserts there is already capacity to insert into top branch inst_table.526/// Asserts there is already capacity to insert into top branch inst_table.
533fn processDeath(self: *Self, inst: Air.Inst.Index) void {527fn processDeath(self: *Self, inst: Air.Inst.Index) void {
534 const air_tags = self.air.instructions.items(.tag);528 const air_tags = self.air.instructions.items(.tag);
...@@ -1291,7 +1285,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1291,7 +1285,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1291}1285}
12921286
1293fn airBreakpoint(self: *Self) !void {1287fn airBreakpoint(self: *Self) !void {
1294 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());1288 _ = try self.addInst(.{
1289 .tag = .ebreak,
1290 .data = .{ .nop = {} },
1291 });
1295 return self.finishAirBookkeeping();1292 return self.finishAirBookkeeping();
1296}1293}
12971294
...@@ -1330,7 +1327,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1330,7 +1327,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1330 unreachable;1327 unreachable;
13311328
1332 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });1329 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
1333 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());1330 _ = try self.addInst(.{
1331 .tag = .jalr,
1332 .data = .{ .i_type = .{
1333 .rd = .ra,
1334 .rs1 = .ra,
1335 .imm12 = 0,
1336 } },
1337 });
1334 } else if (func_value.castTag(.extern_fn)) |_| {1338 } else if (func_value.castTag(.extern_fn)) |_| {
1335 return self.fail("TODO implement calling extern functions", .{});1339 return self.fail("TODO implement calling extern functions", .{});
1336 } else {1340 } else {
...@@ -1375,7 +1379,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1375,7 +1379,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1375fn ret(self: *Self, mcv: MCValue) !void {1379fn ret(self: *Self, mcv: MCValue) !void {
1376 const ret_ty = self.fn_type.fnReturnType();1380 const ret_ty = self.fn_type.fnReturnType();
1377 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);1381 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1378 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());1382 _ = try self.addInst(.{
1383 .tag = .jalr,
1384 .data = .{ .i_type = .{
1385 .rd = .zero,
1386 .rs1 = .ra,
1387 .imm12 = 0,
1388 } },
1389 });
1379}1390}
13801391
1381fn airRet(self: *Self, inst: Air.Inst.Index) !void {1392fn airRet(self: *Self, inst: Air.Inst.Index) !void {
...@@ -1414,7 +1425,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -1414,7 +1425,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
14141425
1415fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {1426fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
1416 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;1427 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
1417 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);1428
1429 _ = try self.addInst(.{
1430 .tag = .dbg_line,
1431 .data = .{ .dbg_line_column = .{
1432 .line = dbg_stmt.line,
1433 .column = dbg_stmt.column,
1434 } },
1435 });
1436
1418 return self.finishAirBookkeeping();1437 return self.finishAirBookkeeping();
1419}1438}
14201439
...@@ -1706,7 +1725,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -1706,7 +1725,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1706 }1725 }
17071726
1708 if (mem.eql(u8, asm_source, "ecall")) {1727 if (mem.eql(u8, asm_source, "ecall")) {
1709 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());1728 _ = try self.addInst(.{
1729 .tag = .ecall,
1730 .data = .{ .nop = {} },
1731 });
1710 } else {1732 } else {
1711 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});1733 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
1712 }1734 }
...@@ -1785,29 +1807,54 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -1785,29 +1807,54 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
1785 .immediate => |unsigned_x| {1807 .immediate => |unsigned_x| {
1786 const x = @bitCast(i64, unsigned_x);1808 const x = @bitCast(i64, unsigned_x);
1787 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {1809 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
1788 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());1810 _ = try self.addInst(.{
1789 return;1811 .tag = .addi,
1790 }1812 .data = .{ .i_type = .{
1791 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {1813 .rd = reg,
1814 .rs1 = .zero,
1815 .imm12 = @intCast(i12, x),
1816 } },
1817 });
1818 } else if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
1792 const lo12 = @truncate(i12, x);1819 const lo12 = @truncate(i12, x);
1793 const carry: i32 = if (lo12 < 0) 1 else 0;1820 const carry: i32 = if (lo12 < 0) 1 else 0;
1794 const hi20 = @truncate(i20, (x >> 12) +% carry);1821 const hi20 = @truncate(i20, (x >> 12) +% carry);
17951822
1796 // TODO: add test case for 32-bit immediate1823 // TODO: add test case for 32-bit immediate
1797 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());1824 _ = try self.addInst(.{
1798 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());1825 .tag = .lui,
1799 return;1826 .data = .{ .u_type = .{
1827 .rd = reg,
1828 .imm20 = hi20,
1829 } },
1830 });
1831 _ = try self.addInst(.{
1832 .tag = .addi,
1833 .data = .{ .i_type = .{
1834 .rd = reg,
1835 .rs1 = reg,
1836 .imm12 = lo12,
1837 } },
1838 });
1839 } else {
1840 // li rd, immediate
1841 // "Myriad sequences"
1842 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
1800 }1843 }
1801 // li rd, immediate
1802 // "Myriad sequences"
1803 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
1804 },1844 },
1805 .memory => |addr| {1845 .memory => |addr| {
1806 // The value is in memory at a hard-coded address.1846 // The value is in memory at a hard-coded address.
1807 // If the type is a pointer, it means the pointer address is at this memory location.1847 // If the type is a pointer, it means the pointer address is at this memory location.
1808 try self.genSetReg(ty, reg, .{ .immediate = addr });1848 try self.genSetReg(ty, reg, .{ .immediate = addr });
18091849
1810 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());1850 _ = try self.addInst(.{
1851 .tag = .ld,
1852 .data = .{ .i_type = .{
1853 .rd = reg,
1854 .rs1 = reg,
1855 .imm12 = 0,
1856 } },
1857 });
1811 // LOAD imm=[i12 offset = 0], rs1 =1858 // LOAD imm=[i12 offset = 0], rs1 =
18121859
1813 // return self.fail("TODO implement genSetReg memory for riscv64");1860 // return self.fail("TODO implement genSetReg memory for riscv64");
src/arch/riscv64/Emit.zig created+192
...@@ -0,0 +1,192 @@
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
50 .ebreak => try emit.mirSystem(inst),
51 .ecall => try emit.mirSystem(inst),
52
53 .dbg_line => try emit.mirDbgLine(inst),
54
55 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
56 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
57
58 .lui => try emit.mirUType(inst),
59 }
60 }
61}
62
63pub fn deinit(emit: *Emit) void {
64 emit.* = undefined;
65}
66
67fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
68 const endian = emit.target.cpu.arch.endian();
69 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
70}
71
72fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
73 @setCold(true);
74 assert(emit.err_msg == null);
75 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
76 return error.EmitFail;
77}
78
79fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
80 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
81 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
82 switch (self.debug_output) {
83 .dwarf => |dbg_out| {
84 // TODO Look into using the DWARF special opcodes to compress this data.
85 // It lets you emit single-byte opcodes that add different numbers to
86 // both the PC and the line number at the same time.
87 try dbg_out.dbg_line.ensureUnusedCapacity(11);
88 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
89 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
90 if (delta_line != 0) {
91 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
92 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
93 }
94 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
95 self.prev_di_pc = self.code.items.len;
96 self.prev_di_line = line;
97 self.prev_di_column = column;
98 self.prev_di_pc = self.code.items.len;
99 },
100 .plan9 => |dbg_out| {
101 if (delta_pc <= 0) return; // only do this when the pc changes
102 // we have already checked the target in the linker to make sure it is compatable
103 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
104
105 // increasing the line number
106 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
107 // increasing the pc
108 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
109 if (d_pc_p9 > 0) {
110 // minus one because if its the last one, we want to leave space to change the line which is one quanta
111 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
112 if (dbg_out.pcop_change_index.*) |pci|
113 dbg_out.dbg_line.items[pci] += 1;
114 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
115 } else if (d_pc_p9 == 0) {
116 // we don't need to do anything, because adding the quant does it for us
117 } else unreachable;
118 if (dbg_out.start_line.* == null)
119 dbg_out.start_line.* = self.prev_di_line;
120 dbg_out.end_line.* = line;
121 // only do this if the pc changed
122 self.prev_di_line = line;
123 self.prev_di_column = column;
124 self.prev_di_pc = self.code.items.len;
125 },
126 .none => {},
127 }
128}
129
130fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
131 const tag = emit.mir.instructions.items(.tag)[inst];
132 const i_type = emit.mir.instructions.items(.data)[inst].i_type;
133
134 switch (tag) {
135 .addi => try emit.writeInstruction(Instruction.addi(i_type.rd, i_type.rs1, i_type.imm12)),
136 .jalr => try emit.writeInstruction(Instruction.jalr(i_type.rd, i_type.imm12, i_type.rs1)),
137 .ld => try emit.writeInstruction(Instruction.ld(i_type.rd, i_type.imm12, i_type.rs1)),
138 else => unreachable,
139 }
140}
141
142fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void {
143 const tag = emit.mir.instructions.items(.tag)[inst];
144
145 switch (tag) {
146 .ebreak => try emit.writeInstruction(Instruction.ebreak),
147 .ecall => try emit.writeInstruction(Instruction.ecall),
148 else => unreachable,
149 }
150}
151
152fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
153 const tag = emit.mir.instructions.items(.tag)[inst];
154 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
155
156 switch (tag) {
157 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
158 else => unreachable,
159 }
160}
161
162fn mirDebugPrologueEnd(self: *Emit) !void {
163 switch (self.debug_output) {
164 .dwarf => |dbg_out| {
165 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
166 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
167 },
168 .plan9 => {},
169 .none => {},
170 }
171}
172
173fn mirDebugEpilogueBegin(self: *Emit) !void {
174 switch (self.debug_output) {
175 .dwarf => |dbg_out| {
176 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
177 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
178 },
179 .plan9 => {},
180 .none => {},
181 }
182}
183
184fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void {
185 const tag = emit.mir.instructions.items(.tag)[inst];
186 const u_type = emit.mir.instructions.items(.data)[inst].u_type;
187
188 switch (tag) {
189 .lui => try emit.writeInstruction(Instruction.lui(u_type.rd, u_type.imm20)),
190 else => unreachable,
191 }
192}
src/arch/riscv64/Mir.zig created+125
...@@ -0,0 +1,125 @@
1//! Machine Intermediate Representation.
2//! This data is produced by RISCV64 Codegen or RISCV64 assembly parsing
3//! These instructions have a 1:1 correspondence with machine code instructions
4//! for the target. MIR can be lowered to source-annotated textual assembly code
5//! instructions, or it can be lowered to machine code.
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
7//! so that, for example, the smaller encodings of jump instructions can be used.
8
9const Mir = @This();
10const std = @import("std");
11const builtin = @import("builtin");
12const assert = std.debug.assert;
13
14const bits = @import("bits.zig");
15const Register = bits.Register;
16
17instructions: std.MultiArrayList(Inst).Slice,
18/// The meaning of this data is determined by `Inst.Tag` value.
19extra: []const u32,
20
21pub const Inst = struct {
22 tag: Tag,
23 /// The meaning of this depends on `tag`.
24 data: Data,
25
26 pub const Tag = enum(u16) {
27 addi,
28 /// Pseudo-instruction: End of prologue
29 dbg_prologue_end,
30 /// Pseudo-instruction: Beginning of epilogue
31 dbg_epilogue_begin,
32 /// Pseudo-instruction: Update debug line
33 dbg_line,
34 ebreak,
35 ecall,
36 jalr,
37 ld,
38 lui,
39 };
40
41 /// The position of an MIR instruction within the `Mir` instructions array.
42 pub const Index = u32;
43
44 /// All instructions have a 4-byte payload, which is contained within
45 /// this union. `Tag` determines which union field is active, as well as
46 /// how to interpret the data within.
47 pub const Data = union {
48 /// No additional data
49 ///
50 /// Used by e.g. ebreak
51 nop: void,
52 /// Another instruction.
53 ///
54 /// Used by e.g. b
55 inst: Index,
56 /// A 16-bit immediate value.
57 ///
58 /// Used by e.g. svc
59 imm16: u16,
60 /// Index into `extra`. Meaning of what can be found there is context-dependent.
61 ///
62 /// Used by e.g. load_memory
63 payload: u32,
64 /// A register
65 ///
66 /// Used by e.g. blr
67 reg: Register,
68 /// I-Type
69 ///
70 /// Used by e.g. jalr
71 i_type: struct {
72 rd: Register,
73 rs1: Register,
74 imm12: i12,
75 },
76 /// U-Type
77 ///
78 /// Used by e.g. lui
79 u_type: struct {
80 rd: Register,
81 imm20: i20,
82 },
83 /// Debug info: line and column
84 ///
85 /// Used by e.g. dbg_line
86 dbg_line_column: struct {
87 line: u32,
88 column: u32,
89 },
90 };
91
92 // Make sure we don't accidentally make instructions bigger than expected.
93 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
94 // comptime {
95 // if (builtin.mode != .Debug) {
96 // assert(@sizeOf(Inst) == 8);
97 // }
98 // }
99};
100
101pub fn deinit(mir: *Mir, gpa: *std.mem.Allocator) void {
102 mir.instructions.deinit(gpa);
103 gpa.free(mir.extra);
104 mir.* = undefined;
105}
106
107/// Returns the requested data, as well as the new index which is at the start of the
108/// trailers for the object.
109pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
110 const fields = std.meta.fields(T);
111 var i: usize = index;
112 var result: T = undefined;
113 inline for (fields) |field| {
114 @field(result, field.name) = switch (field.field_type) {
115 u32 => mir.extra[i],
116 i32 => @bitCast(i32, mir.extra[i]),
117 else => @compileError("bad field type"),
118 };
119 i += 1;
120 }
121 return .{
122 .data = result,
123 .end = i,
124 };
125}