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
759759pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr: ?usize) noreturn {
760760 @setCold(true);
761761
762 // stage2_riscv64 backend doesn't support loops yet.
763 if (builtin.zig_backend == .stage2_riscv64) {
764 unreachable;
765 }
766
767762 // For backends that cannot handle the language features depended on by the
768763 // default panic handler, we have a simpler panic handler:
769764 if (builtin.zig_backend == .stage2_wasm or
......@@ -772,7 +767,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
772767 builtin.zig_backend == .stage2_x86 or
773768 (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho)) or
774769 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)
776772 {
777773 while (true) {
778774 @breakpoint();
src/arch/riscv64/CodeGen.zig+29-22
......@@ -301,6 +301,7 @@ pub fn generate(
301301 .prev_di_line = func.lbrace_line,
302302 .prev_di_column = func.lbrace_column,
303303 .stack_size = @max(32, function.max_end_stack),
304 .code_offset_mapping = .{},
304305 };
305306 defer emit.deinit();
306307
......@@ -929,21 +930,16 @@ fn binOpRegister(
929930 .cmp_gt => .cmp_gt,
930931 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
931932 };
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
944934 _ = try self.addInst(.{
945935 .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 },
947943 });
948944
949945 return MCValue{ .register = dest_reg };
......@@ -1636,7 +1632,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
16361632
16371633 const dst_mcv = switch (src_mcv) {
16381634 .register => |src_reg| dst: {
1639 self.register_manager.getRegAssumeFree(src_reg, inst);
1635 try self.register_manager.getReg(src_reg, inst);
16401636 break :dst src_mcv;
16411637 },
16421638 else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
......@@ -1914,6 +1910,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
19141910 self.processDeath(operand);
19151911 }
19161912 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
19181916 // Revert to the previous register and stack allocation state.
19191917
......@@ -1929,7 +1927,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
19291927 self.next_stack_offset = parent_next_stack_offset;
19301928 self.register_manager.free_registers = parent_free_registers;
19311929
1932 try self.performReloc(reloc);
19331930 const else_branch = self.branch_stack.addOneAssumeCapacity();
19341931 else_branch.* = .{};
19351932
......@@ -2014,8 +2011,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
20142011 var item = self.branch_stack.pop();
20152012 item.deinit(self.gpa);
20162013 }
2017
2018 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
20192014}
20202015
20212016fn 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 {
20272022 };
20282023
20292024 return try self.addInst(.{
2030 .tag = .beq,
2025 .tag = .bne,
20312026 .data = .{
20322027 .b_type = .{
20332028 .rs1 = reg,
20342029 .rs2 = .zero,
2035 .imm12 = 0, // patched later.
2030 .inst = undefined,
20362031 },
20372032 },
20382033 });
......@@ -2218,7 +2213,13 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
22182213 try self.genBody(body);
22192214
22202215 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));
22222223 }
22232224
22242225 const result = self.blocks.getPtr(inst).?.mcv;
......@@ -2233,11 +2234,14 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
22332234 // return self.finishAir(inst, .dead, .{ condition, .none, .none });
22342235}
22352236
2236fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
2237fn performReloc(self: *Self, inst: Mir.Inst.Index, target: Mir.Inst.Index) !void {
22372238 const tag = self.mir_instructions.items(.tag)[inst];
22382239
22392240 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,
22412245 else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}),
22422246 }
22432247}
......@@ -2283,7 +2287,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
22832287 .data = .{
22842288 .j_type = .{
22852289 .rd = .ra,
2286 .imm21 = undefined, // populated later through performReloc
2290 .inst = undefined,
22872291 },
22882292 },
22892293 }));
......@@ -2467,6 +2471,9 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
24672471 }
24682472 },
24692473 .stack_offset, .load_symbol => {
2474 if (true)
2475 return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)});
2476
24702477 if (abi_size <= 8) {
24712478 const reg = try self.copyToTmpRegister(ty, src_val);
24722479 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
src/arch/riscv64/Emit.zig+86-12
......@@ -26,8 +26,14 @@ prev_di_line: u32,
2626prev_di_column: u32,
2727/// Relative to the beginning of `code`.
2828prev_di_pc: usize,
29
29/// Function's stack size. Used for backpatching.
3030stack_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
3238const log = std.log.scoped(.emit);
3339
......@@ -100,6 +106,10 @@ pub fn emitMir(
100106}
101107
102108pub 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);
103113 emit.* = undefined;
104114}
105115
......@@ -118,10 +128,8 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
118128}
119129
120130fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
121 log.debug("Line: {} {}\n", .{ line, emit.prev_di_line });
122131 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
123132 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 });
125133 switch (emit.debug_output) {
126134 .dwarf => |dw| {
127135 if (column != emit.prev_di_column) try dw.setColumn(column);
......@@ -166,7 +174,7 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
166174 switch (tag) {
167175 .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)),
168176 .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)),
170178 else => unreachable,
171179 }
172180}
......@@ -175,8 +183,17 @@ fn mirBType(emit: *Emit, inst: Mir.Inst.Index) !void {
175183 const tag = emit.mir.instructions.items(.tag)[inst];
176184 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
178188 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 },
180197 else => unreachable,
181198 }
182199}
......@@ -215,9 +232,12 @@ fn mirJType(emit: *Emit, inst: Mir.Inst.Index) !void {
215232 const tag = emit.mir.instructions.items(.tag)[inst];
216233 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
218237 switch (tag) {
219238 .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)));
221241 },
222242 else => unreachable,
223243 }
......@@ -304,12 +324,8 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
304324 },
305325
306326 .j => {
307 const target = data.inst;
308 const offset: i12 = @intCast(emit.code.items.len);
309 _ = target;
310
311 try emit.writeInstruction(Instruction.jal(.s0, offset));
312 unreachable; // TODO: mirPsuedo j
327 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(data.inst).?)) - @as(i64, @intCast(emit.code.items.len));
328 try emit.writeInstruction(Instruction.jal(.s0, @intCast(offset)));
313329 },
314330
315331 else => unreachable,
......@@ -401,7 +417,51 @@ fn isLoad(tag: Mir.Inst.Tag) bool {
401417 };
402418}
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
404462fn lowerMir(emit: *Emit) !void {
463 const comp = emit.bin_file.comp;
464 const gpa = comp.gpa;
405465 const mir_tags = emit.mir.instructions.items(.tag);
406466 const mir_datas = emit.mir.instructions.items(.data);
407467
......@@ -419,5 +479,19 @@ fn lowerMir(emit: *Emit) !void {
419479 mir_datas[inst].i_type.imm12 = -(casted_size - 12 - offset);
420480 }
421481 }
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);
422496 }
423497}
src/arch/riscv64/Mir.zig+2-2
......@@ -158,14 +158,14 @@ pub const Inst = struct {
158158 b_type: struct {
159159 rs1: Register,
160160 rs2: Register,
161 imm12: i13,
161 inst: Inst.Index,
162162 },
163163 /// J-Type
164164 ///
165165 /// Used by e.g. jal
166166 j_type: struct {
167167 rd: Register,
168 imm21: i21,
168 inst: Inst.Index,
169169 },
170170 /// U-Type
171171 ///