authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-03-14 02:44:24-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-05-11 02:17:11-07:00
log2be3033acda53389cac9f3e9a8ca0a3d41348eef
treeaf1a41562ca6ed1f7758c55d625d444509834241
parent28df64cba45595a201f8c2312656922a8c28a67c

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

4 files changed, 119 insertions(+), 42 deletions(-)

lib/std/builtin.zig+2-6
...@@ -759,11 +759,6 @@ else...@@ -759,11 +759,6 @@ else
759pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr: ?usize) noreturn {759pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr: ?usize) noreturn {
760 @setCold(true);760 @setCold(true);
761761
762 // stage2_riscv64 backend doesn't support loops yet.
763 if (builtin.zig_backend == .stage2_riscv64) {
764 unreachable;
765 }
766
767 // For backends that cannot handle the language features depended on by the762 // For backends that cannot handle the language features depended on by the
768 // default panic handler, we have a simpler panic handler:763 // default panic handler, we have a simpler panic handler:
769 if (builtin.zig_backend == .stage2_wasm or764 if (builtin.zig_backend == .stage2_wasm or
...@@ -772,7 +767,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr...@@ -772,7 +767,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
772 builtin.zig_backend == .stage2_x86 or767 builtin.zig_backend == .stage2_x86 or
773 (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho)) or768 (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho)) or
774 builtin.zig_backend == .stage2_sparc64 or769 builtin.zig_backend == .stage2_sparc64 or
775 builtin.zig_backend == .stage2_spirv64)770 builtin.zig_backend == .stage2_spirv64 or
771 builtin.zig_backend == .stage2_riscv64)
776 {772 {
777 while (true) {773 while (true) {
778 @breakpoint();774 @breakpoint();
src/arch/riscv64/CodeGen.zig+29-22
...@@ -301,6 +301,7 @@ pub fn generate(...@@ -301,6 +301,7 @@ pub fn generate(
301 .prev_di_line = func.lbrace_line,301 .prev_di_line = func.lbrace_line,
302 .prev_di_column = func.lbrace_column,302 .prev_di_column = func.lbrace_column,
303 .stack_size = @max(32, function.max_end_stack),303 .stack_size = @max(32, function.max_end_stack),
304 .code_offset_mapping = .{},
304 };305 };
305 defer emit.deinit();306 defer emit.deinit();
306307
...@@ -929,21 +930,16 @@ fn binOpRegister(...@@ -929,21 +930,16 @@ fn binOpRegister(
929 .cmp_gt => .cmp_gt,930 .cmp_gt => .cmp_gt,
930 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),931 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
931 };932 };
932 const mir_data: Mir.Inst.Data = switch (tag) {
933 .add,
934 .sub,
935 .cmp_eq,
936 => .{ .r_type = .{
937 .rd = dest_reg,
938 .rs1 = lhs_reg,
939 .rs2 = rhs_reg,
940 } },
941 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
942 };
943933
944 _ = try self.addInst(.{934 _ = try self.addInst(.{
945 .tag = mir_tag,935 .tag = mir_tag,
946 .data = mir_data,936 .data = .{
937 .r_type = .{
938 .rd = dest_reg,
939 .rs1 = lhs_reg,
940 .rs2 = rhs_reg,
941 },
942 },
947 });943 });
948944
949 return MCValue{ .register = dest_reg };945 return MCValue{ .register = dest_reg };
...@@ -1636,7 +1632,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1636,7 +1632,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
16361632
1637 const dst_mcv = switch (src_mcv) {1633 const dst_mcv = switch (src_mcv) {
1638 .register => |src_reg| dst: {1634 .register => |src_reg| dst: {
1639 self.register_manager.getRegAssumeFree(src_reg, inst);1635 try self.register_manager.getReg(src_reg, inst);
1640 break :dst src_mcv;1636 break :dst src_mcv;
1641 },1637 },
1642 else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),1638 else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
...@@ -1914,6 +1910,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1914,6 +1910,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
1914 self.processDeath(operand);1910 self.processDeath(operand);
1915 }1911 }
1916 try self.genBody(then_body);1912 try self.genBody(then_body);
1913 // point at the to-be-generated else case
1914 try self.performReloc(reloc, @intCast(self.mir_instructions.len));
19171915
1918 // Revert to the previous register and stack allocation state.1916 // Revert to the previous register and stack allocation state.
19191917
...@@ -1929,7 +1927,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1929,7 +1927,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
1929 self.next_stack_offset = parent_next_stack_offset;1927 self.next_stack_offset = parent_next_stack_offset;
1930 self.register_manager.free_registers = parent_free_registers;1928 self.register_manager.free_registers = parent_free_registers;
19311929
1932 try self.performReloc(reloc);
1933 const else_branch = self.branch_stack.addOneAssumeCapacity();1930 const else_branch = self.branch_stack.addOneAssumeCapacity();
1934 else_branch.* = .{};1931 else_branch.* = .{};
19351932
...@@ -2014,8 +2011,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2014,8 +2011,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2014 var item = self.branch_stack.pop();2011 var item = self.branch_stack.pop();
2015 item.deinit(self.gpa);2012 item.deinit(self.gpa);
2016 }2013 }
2017
2018 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
2019}2014}
20202015
2021fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {2016fn 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 {...@@ -2027,12 +2022,12 @@ fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {
2027 };2022 };
20282023
2029 return try self.addInst(.{2024 return try self.addInst(.{
2030 .tag = .beq,2025 .tag = .bne,
2031 .data = .{2026 .data = .{
2032 .b_type = .{2027 .b_type = .{
2033 .rs1 = reg,2028 .rs1 = reg,
2034 .rs2 = .zero,2029 .rs2 = .zero,
2035 .imm12 = 0, // patched later.2030 .inst = undefined,
2036 },2031 },
2037 },2032 },
2038 });2033 });
...@@ -2218,7 +2213,13 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -2218,7 +2213,13 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
2218 try self.genBody(body);2213 try self.genBody(body);
22192214
2220 for (self.blocks.getPtr(inst).?.relocs.items) |reloc| {2215 for (self.blocks.getPtr(inst).?.relocs.items) |reloc| {
2221 try self.performReloc(reloc);2216 // here we are relocing to point at the instruction after the block.
2217 // [then case]
2218 // [jump to end] // this is reloced
2219 // [else case]
2220 // [jump to end] // this is reloced
2221 // [this isn't generated yet] // point to here
2222 try self.performReloc(reloc, @intCast(self.mir_instructions.len));
2222 }2223 }
22232224
2224 const result = self.blocks.getPtr(inst).?.mcv;2225 const result = self.blocks.getPtr(inst).?.mcv;
...@@ -2233,11 +2234,14 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -2233,11 +2234,14 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
2233 // return self.finishAir(inst, .dead, .{ condition, .none, .none });2234 // return self.finishAir(inst, .dead, .{ condition, .none, .none });
2234}2235}
22352236
2236fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {2237fn performReloc(self: *Self, inst: Mir.Inst.Index, target: Mir.Inst.Index) !void {
2237 const tag = self.mir_instructions.items(.tag)[inst];2238 const tag = self.mir_instructions.items(.tag)[inst];
22382239
2239 switch (tag) {2240 switch (tag) {
2240 .beq => self.mir_instructions.items(.data)[inst].b_type.imm12 = @intCast(inst),2241 .bne,
2242 .beq,
2243 => self.mir_instructions.items(.data)[inst].b_type.inst = target,
2244 .jal => self.mir_instructions.items(.data)[inst].j_type.inst = target,
2241 else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}),2245 else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}),
2242 }2246 }
2243}2247}
...@@ -2283,7 +2287,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -2283,7 +2287,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
2283 .data = .{2287 .data = .{
2284 .j_type = .{2288 .j_type = .{
2285 .rd = .ra,2289 .rd = .ra,
2286 .imm21 = undefined, // populated later through performReloc2290 .inst = undefined,
2287 },2291 },
2288 },2292 },
2289 }));2293 }));
...@@ -2467,6 +2471,9 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner...@@ -2467,6 +2471,9 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
2467 }2471 }
2468 },2472 },
2469 .stack_offset, .load_symbol => {2473 .stack_offset, .load_symbol => {
2474 if (true)
2475 return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)});
2476
2470 if (abi_size <= 8) {2477 if (abi_size <= 8) {
2471 const reg = try self.copyToTmpRegister(ty, src_val);2478 const reg = try self.copyToTmpRegister(ty, src_val);
2472 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });2479 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
src/arch/riscv64/Emit.zig+86-12
...@@ -26,8 +26,14 @@ prev_di_line: u32,...@@ -26,8 +26,14 @@ prev_di_line: u32,
26prev_di_column: u32,26prev_di_column: u32,
27/// Relative to the beginning of `code`.27/// Relative to the beginning of `code`.
28prev_di_pc: usize,28prev_di_pc: usize,
2929/// Function's stack size. Used for backpatching.
30stack_size: u32,30stack_size: u32,
31/// For backward branches: stores the code offset of the target
32/// instruction
33///
34/// For forward branches: stores the code offset of the branch
35/// instruction
36code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
3137
32const log = std.log.scoped(.emit);38const log = std.log.scoped(.emit);
3339
...@@ -100,6 +106,10 @@ pub fn emitMir(...@@ -100,6 +106,10 @@ pub fn emitMir(
100}106}
101107
102pub fn deinit(emit: *Emit) void {108pub fn deinit(emit: *Emit) void {
109 const comp = emit.bin_file.comp;
110 const gpa = comp.gpa;
111
112 emit.code_offset_mapping.deinit(gpa);
103 emit.* = undefined;113 emit.* = undefined;
104}114}
105115
...@@ -118,10 +128,8 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {...@@ -118,10 +128,8 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
118}128}
119129
120fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {130fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
121 log.debug("Line: {} {}\n", .{ line, emit.prev_di_line });
122 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));131 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
123 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;132 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
124 log.debug("(advance pc={d} and line={d})", .{ delta_pc, delta_line });
125 switch (emit.debug_output) {133 switch (emit.debug_output) {
126 .dwarf => |dw| {134 .dwarf => |dw| {
127 if (column != emit.prev_di_column) try dw.setColumn(column);135 if (column != emit.prev_di_column) try dw.setColumn(column);
...@@ -166,7 +174,7 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -166,7 +174,7 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
166 switch (tag) {174 switch (tag) {
167 .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)),175 .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)),
168 .sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)),176 .sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)),
169 .cmp_eq => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),177 .cmp_gt => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),
170 else => unreachable,178 else => unreachable,
171 }179 }
172}180}
...@@ -175,8 +183,17 @@ fn mirBType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -175,8 +183,17 @@ fn mirBType(emit: *Emit, inst: Mir.Inst.Index) !void {
175 const tag = emit.mir.instructions.items(.tag)[inst];183 const tag = emit.mir.instructions.items(.tag)[inst];
176 const b_type = emit.mir.instructions.items(.data)[inst].b_type;184 const b_type = emit.mir.instructions.items(.data)[inst].b_type;
177185
186 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(b_type.inst).?)) - @as(i64, @intCast(emit.code.items.len));
187
178 switch (tag) {188 switch (tag) {
179 .beq => try emit.writeInstruction(Instruction.beq(b_type.rs1, b_type.rs2, b_type.imm12)),189 .beq => {
190 log.debug("beq: {} offset={}", .{ inst, offset });
191 try emit.writeInstruction(Instruction.beq(b_type.rs1, b_type.rs2, @intCast(offset)));
192 },
193 .bne => {
194 log.debug("bne: {} offset={}", .{ inst, offset });
195 try emit.writeInstruction(Instruction.bne(b_type.rs1, b_type.rs2, @intCast(offset)));
196 },
180 else => unreachable,197 else => unreachable,
181 }198 }
182}199}
...@@ -215,9 +232,12 @@ fn mirJType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -215,9 +232,12 @@ fn mirJType(emit: *Emit, inst: Mir.Inst.Index) !void {
215 const tag = emit.mir.instructions.items(.tag)[inst];232 const tag = emit.mir.instructions.items(.tag)[inst];
216 const j_type = emit.mir.instructions.items(.data)[inst].j_type;233 const j_type = emit.mir.instructions.items(.data)[inst].j_type;
217234
235 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(j_type.inst).?)) - @as(i64, @intCast(emit.code.items.len));
236
218 switch (tag) {237 switch (tag) {
219 .jal => {238 .jal => {
220 try emit.writeInstruction(Instruction.jal(j_type.rd, j_type.imm21));239 log.debug("jal: {} offset={}", .{ inst, offset });
240 try emit.writeInstruction(Instruction.jal(j_type.rd, @intCast(offset)));
221 },241 },
222 else => unreachable,242 else => unreachable,
223 }243 }
...@@ -304,12 +324,8 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -304,12 +324,8 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
304 },324 },
305325
306 .j => {326 .j => {
307 const target = data.inst;327 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(data.inst).?)) - @as(i64, @intCast(emit.code.items.len));
308 const offset: i12 = @intCast(emit.code.items.len);328 try emit.writeInstruction(Instruction.jal(.s0, @intCast(offset)));
309 _ = target;
310
311 try emit.writeInstruction(Instruction.jal(.s0, offset));
312 unreachable; // TODO: mirPsuedo j
313 },329 },
314330
315 else => unreachable,331 else => unreachable,
...@@ -401,7 +417,51 @@ fn isLoad(tag: Mir.Inst.Tag) bool {...@@ -401,7 +417,51 @@ fn isLoad(tag: Mir.Inst.Tag) bool {
401 };417 };
402}418}
403419
420pub fn isBranch(tag: Mir.Inst.Tag) bool {
421 return switch (tag) {
422 .beq => true,
423 .bne => true,
424 .jal => true,
425 .j => true,
426 else => false,
427 };
428}
429
430pub fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
431 const tag = emit.mir.instructions.items(.tag)[inst];
432 const data = emit.mir.instructions.items(.data)[inst];
433
434 switch (tag) {
435 .bne,
436 .beq,
437 => return data.b_type.inst,
438 .jal => return data.j_type.inst,
439 .j => return data.inst,
440 else => std.debug.panic("branchTarget {s}", .{@tagName(tag)}),
441 }
442}
443
444fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
445 const tag = emit.mir.instructions.items(.tag)[inst];
446
447 return switch (tag) {
448 .dbg_line,
449 .dbg_epilogue_begin,
450 .dbg_prologue_end,
451 => 0,
452
453 .psuedo_epilogue => 12, // 3 * 4
454 .psuedo_prologue => 16, // 4 * 4
455
456 .abs => 12, // 3 * 4
457
458 else => 4,
459 };
460}
461
404fn lowerMir(emit: *Emit) !void {462fn lowerMir(emit: *Emit) !void {
463 const comp = emit.bin_file.comp;
464 const gpa = comp.gpa;
405 const mir_tags = emit.mir.instructions.items(.tag);465 const mir_tags = emit.mir.instructions.items(.tag);
406 const mir_datas = emit.mir.instructions.items(.data);466 const mir_datas = emit.mir.instructions.items(.data);
407467
...@@ -419,5 +479,19 @@ fn lowerMir(emit: *Emit) !void {...@@ -419,5 +479,19 @@ fn lowerMir(emit: *Emit) !void {
419 mir_datas[inst].i_type.imm12 = -(casted_size - 12 - offset);479 mir_datas[inst].i_type.imm12 = -(casted_size - 12 - offset);
420 }480 }
421 }481 }
482
483 if (isBranch(tag)) {
484 const target_inst = emit.branchTarget(inst);
485 try emit.code_offset_mapping.put(gpa, target_inst, 0);
486 }
487 }
488 var current_code_offset: usize = 0;
489
490 for (0..mir_tags.len) |index| {
491 const inst = @as(u32, @intCast(index));
492 if (emit.code_offset_mapping.getPtr(inst)) |offset| {
493 offset.* = current_code_offset;
494 }
495 current_code_offset += emit.instructionSize(inst);
422 }496 }
423}497}
src/arch/riscv64/Mir.zig+2-2
...@@ -158,14 +158,14 @@ pub const Inst = struct {...@@ -158,14 +158,14 @@ pub const Inst = struct {
158 b_type: struct {158 b_type: struct {
159 rs1: Register,159 rs1: Register,
160 rs2: Register,160 rs2: Register,
161 imm12: i13,161 inst: Inst.Index,
162 },162 },
163 /// J-Type163 /// J-Type
164 ///164 ///
165 /// Used by e.g. jal165 /// Used by e.g. jal
166 j_type: struct {166 j_type: struct {
167 rd: Register,167 rd: Register,
168 imm21: i21,168 inst: Inst.Index,
169 },169 },
170 /// U-Type170 /// U-Type
171 ///171 ///