Commit 2be3033acd

David Rubin <daviru007@icloud.com>
2024-03-14 10:44:24
riscv: implement basic branching
we use a code offset map in Emit.zig to pre-compute what byte offset each MIR instruction is at. this is important because they can be of different size
1 parent 28df64c
Changed files (4)
lib
src
lib/std/builtin.zig
@@ -759,11 +759,6 @@ else
 pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr: ?usize) noreturn {
     @setCold(true);
 
-    // stage2_riscv64 backend doesn't support loops yet.
-    if (builtin.zig_backend == .stage2_riscv64) {
-        unreachable;
-    }
-
     // For backends that cannot handle the language features depended on by the
     // default panic handler, we have a simpler panic handler:
     if (builtin.zig_backend == .stage2_wasm or
@@ -772,7 +767,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
         builtin.zig_backend == .stage2_x86 or
         (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho)) or
         builtin.zig_backend == .stage2_sparc64 or
-        builtin.zig_backend == .stage2_spirv64)
+        builtin.zig_backend == .stage2_spirv64 or
+        builtin.zig_backend == .stage2_riscv64)
     {
         while (true) {
             @breakpoint();
src/arch/riscv64/CodeGen.zig
@@ -301,6 +301,7 @@ pub fn generate(
         .prev_di_line = func.lbrace_line,
         .prev_di_column = func.lbrace_column,
         .stack_size = @max(32, function.max_end_stack),
+        .code_offset_mapping = .{},
     };
     defer emit.deinit();
 
@@ -929,21 +930,16 @@ fn binOpRegister(
         .cmp_gt => .cmp_gt,
         else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
     };
-    const mir_data: Mir.Inst.Data = switch (tag) {
-        .add,
-        .sub,
-        .cmp_eq,
-        => .{ .r_type = .{
-            .rd = dest_reg,
-            .rs1 = lhs_reg,
-            .rs2 = rhs_reg,
-        } },
-        else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
-    };
 
     _ = try self.addInst(.{
         .tag = mir_tag,
-        .data = mir_data,
+        .data = .{
+            .r_type = .{
+                .rd = dest_reg,
+                .rs1 = lhs_reg,
+                .rs2 = rhs_reg,
+            },
+        },
     });
 
     return MCValue{ .register = dest_reg };
@@ -1636,7 +1632,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
 
         const dst_mcv = switch (src_mcv) {
             .register => |src_reg| dst: {
-                self.register_manager.getRegAssumeFree(src_reg, inst);
+                try self.register_manager.getReg(src_reg, inst);
                 break :dst src_mcv;
             },
             else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
@@ -1914,6 +1910,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
         self.processDeath(operand);
     }
     try self.genBody(then_body);
+    // point at the to-be-generated else case
+    try self.performReloc(reloc, @intCast(self.mir_instructions.len));
 
     // Revert to the previous register and stack allocation state.
 
@@ -1929,7 +1927,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
     self.next_stack_offset = parent_next_stack_offset;
     self.register_manager.free_registers = parent_free_registers;
 
-    try self.performReloc(reloc);
     const else_branch = self.branch_stack.addOneAssumeCapacity();
     else_branch.* = .{};
 
@@ -2014,8 +2011,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
         var item = self.branch_stack.pop();
         item.deinit(self.gpa);
     }
-
-    return self.finishAir(inst, .unreach, .{ .none, .none, .none });
 }
 
 fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {
@@ -2027,12 +2022,12 @@ fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {
     };
 
     return try self.addInst(.{
-        .tag = .beq,
+        .tag = .bne,
         .data = .{
             .b_type = .{
                 .rs1 = reg,
                 .rs2 = .zero,
-                .imm12 = 0, // patched later.
+                .inst = undefined,
             },
         },
     });
@@ -2218,7 +2213,13 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
     try self.genBody(body);
 
     for (self.blocks.getPtr(inst).?.relocs.items) |reloc| {
-        try self.performReloc(reloc);
+        // here we are relocing to point at the instruction after the block.
+        // [then case]
+        // [jump to end] // this is reloced
+        // [else case]
+        // [jump to end] // this is reloced
+        // [this isn't generated yet] // point to here
+        try self.performReloc(reloc, @intCast(self.mir_instructions.len));
     }
 
     const result = self.blocks.getPtr(inst).?.mcv;
@@ -2233,11 +2234,14 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
     // return self.finishAir(inst, .dead, .{ condition, .none, .none });
 }
 
-fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
+fn performReloc(self: *Self, inst: Mir.Inst.Index, target: Mir.Inst.Index) !void {
     const tag = self.mir_instructions.items(.tag)[inst];
 
     switch (tag) {
-        .beq => self.mir_instructions.items(.data)[inst].b_type.imm12 = @intCast(inst),
+        .bne,
+        .beq,
+        => self.mir_instructions.items(.data)[inst].b_type.inst = target,
+        .jal => self.mir_instructions.items(.data)[inst].j_type.inst = target,
         else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}),
     }
 }
@@ -2283,7 +2287,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
         .data = .{
             .j_type = .{
                 .rd = .ra,
-                .imm21 = undefined, // populated later through performReloc
+                .inst = undefined,
             },
         },
     }));
@@ -2467,6 +2471,9 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
             }
         },
         .stack_offset, .load_symbol => {
+            if (true)
+                return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)});
+
             if (abi_size <= 8) {
                 const reg = try self.copyToTmpRegister(ty, src_val);
                 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
src/arch/riscv64/Emit.zig
@@ -26,8 +26,14 @@ prev_di_line: u32,
 prev_di_column: u32,
 /// Relative to the beginning of `code`.
 prev_di_pc: usize,
-
+/// Function's stack size. Used for backpatching.
 stack_size: u32,
+/// For backward branches: stores the code offset of the target
+/// instruction
+///
+/// For forward branches: stores the code offset of the branch
+/// instruction
+code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
 
 const log = std.log.scoped(.emit);
 
@@ -100,6 +106,10 @@ pub fn emitMir(
 }
 
 pub fn deinit(emit: *Emit) void {
+    const comp = emit.bin_file.comp;
+    const gpa = comp.gpa;
+
+    emit.code_offset_mapping.deinit(gpa);
     emit.* = undefined;
 }
 
@@ -118,10 +128,8 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
 }
 
 fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
-    log.debug("Line: {} {}\n", .{ line, emit.prev_di_line });
     const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
     const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
-    log.debug("(advance pc={d} and line={d})", .{ delta_pc, delta_line });
     switch (emit.debug_output) {
         .dwarf => |dw| {
             if (column != emit.prev_di_column) try dw.setColumn(column);
@@ -166,7 +174,7 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
     switch (tag) {
         .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)),
         .sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)),
-        .cmp_eq => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),
+        .cmp_gt => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),
         else => unreachable,
     }
 }
@@ -175,8 +183,17 @@ fn mirBType(emit: *Emit, inst: Mir.Inst.Index) !void {
     const tag = emit.mir.instructions.items(.tag)[inst];
     const b_type = emit.mir.instructions.items(.data)[inst].b_type;
 
+    const offset = @as(i64, @intCast(emit.code_offset_mapping.get(b_type.inst).?)) - @as(i64, @intCast(emit.code.items.len));
+
     switch (tag) {
-        .beq => try emit.writeInstruction(Instruction.beq(b_type.rs1, b_type.rs2, b_type.imm12)),
+        .beq => {
+            log.debug("beq: {} offset={}", .{ inst, offset });
+            try emit.writeInstruction(Instruction.beq(b_type.rs1, b_type.rs2, @intCast(offset)));
+        },
+        .bne => {
+            log.debug("bne: {} offset={}", .{ inst, offset });
+            try emit.writeInstruction(Instruction.bne(b_type.rs1, b_type.rs2, @intCast(offset)));
+        },
         else => unreachable,
     }
 }
@@ -215,9 +232,12 @@ fn mirJType(emit: *Emit, inst: Mir.Inst.Index) !void {
     const tag = emit.mir.instructions.items(.tag)[inst];
     const j_type = emit.mir.instructions.items(.data)[inst].j_type;
 
+    const offset = @as(i64, @intCast(emit.code_offset_mapping.get(j_type.inst).?)) - @as(i64, @intCast(emit.code.items.len));
+
     switch (tag) {
         .jal => {
-            try emit.writeInstruction(Instruction.jal(j_type.rd, j_type.imm21));
+            log.debug("jal: {} offset={}", .{ inst, offset });
+            try emit.writeInstruction(Instruction.jal(j_type.rd, @intCast(offset)));
         },
         else => unreachable,
     }
@@ -304,12 +324,8 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
         },
 
         .j => {
-            const target = data.inst;
-            const offset: i12 = @intCast(emit.code.items.len);
-            _ = target;
-
-            try emit.writeInstruction(Instruction.jal(.s0, offset));
-            unreachable; // TODO: mirPsuedo j
+            const offset = @as(i64, @intCast(emit.code_offset_mapping.get(data.inst).?)) - @as(i64, @intCast(emit.code.items.len));
+            try emit.writeInstruction(Instruction.jal(.s0, @intCast(offset)));
         },
 
         else => unreachable,
@@ -401,7 +417,51 @@ fn isLoad(tag: Mir.Inst.Tag) bool {
     };
 }
 
+pub fn isBranch(tag: Mir.Inst.Tag) bool {
+    return switch (tag) {
+        .beq => true,
+        .bne => true,
+        .jal => true,
+        .j => true,
+        else => false,
+    };
+}
+
+pub fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+    const data = emit.mir.instructions.items(.data)[inst];
+
+    switch (tag) {
+        .bne,
+        .beq,
+        => return data.b_type.inst,
+        .jal => return data.j_type.inst,
+        .j => return data.inst,
+        else => std.debug.panic("branchTarget {s}", .{@tagName(tag)}),
+    }
+}
+
+fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+
+    return switch (tag) {
+        .dbg_line,
+        .dbg_epilogue_begin,
+        .dbg_prologue_end,
+        => 0,
+
+        .psuedo_epilogue => 12, // 3 * 4
+        .psuedo_prologue => 16, // 4 * 4
+
+        .abs => 12, // 3 * 4
+
+        else => 4,
+    };
+}
+
 fn lowerMir(emit: *Emit) !void {
+    const comp = emit.bin_file.comp;
+    const gpa = comp.gpa;
     const mir_tags = emit.mir.instructions.items(.tag);
     const mir_datas = emit.mir.instructions.items(.data);
 
@@ -419,5 +479,19 @@ fn lowerMir(emit: *Emit) !void {
                 mir_datas[inst].i_type.imm12 = -(casted_size - 12 - offset);
             }
         }
+
+        if (isBranch(tag)) {
+            const target_inst = emit.branchTarget(inst);
+            try emit.code_offset_mapping.put(gpa, target_inst, 0);
+        }
+    }
+    var current_code_offset: usize = 0;
+
+    for (0..mir_tags.len) |index| {
+        const inst = @as(u32, @intCast(index));
+        if (emit.code_offset_mapping.getPtr(inst)) |offset| {
+            offset.* = current_code_offset;
+        }
+        current_code_offset += emit.instructionSize(inst);
     }
 }
src/arch/riscv64/Mir.zig
@@ -158,14 +158,14 @@ pub const Inst = struct {
         b_type: struct {
             rs1: Register,
             rs2: Register,
-            imm12: i13,
+            inst: Inst.Index,
         },
         /// J-Type
         ///
         /// Used by e.g. jal
         j_type: struct {
             rd: Register,
-            imm21: i21,
+            inst: Inst.Index,
         },
         /// U-Type
         ///