From 5e770407cf50ae8cd103644c8ca297da52adb5b8 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Wed, 13 Mar 2024 18:40:35 -0700 Subject: [PATCH] riscv: basic function arguments - rename setRegOrMem -> setValue - a naive method of passing arguments by register - gather the prologue and epilogue and generate them in Emit.zig. this is cleaner because we have the final stack size in the emit step. - define the "fa" register set, which contains the RISC-V calling convention defined function argument registers --- src/arch/riscv64/CodeGen.zig | 275 ++++++++++++++++------------------- src/arch/riscv64/Emit.zig | 96 ++++++++---- src/arch/riscv64/Mir.zig | 7 +- src/arch/riscv64/abi.zig | 18 ++- 4 files changed, 217 insertions(+), 179 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index de78dd75dc..4e91c0852a 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -34,7 +34,10 @@ const Register = bits.Register; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const callee_preserved_regs = abi.callee_preserved_regs; +/// General Purpose const gp = abi.RegisterClass.gp; +/// Function Args +const fa = abi.RegisterClass.fa; const InnerError = CodeGenError || error{OutOfRegisters}; @@ -297,6 +300,7 @@ pub fn generate( .prev_di_pc = 0, .prev_di_line = func.lbrace_line, .prev_di_column = func.lbrace_column, + .stack_size = @max(32, function.max_end_stack), }; defer emit.deinit(); @@ -349,14 +353,9 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { } fn gen(self: *Self) !void { - const mod = self.bin_file.comp.module.?; - const cc = self.fn_type.fnCallingConvention(mod); - - if (cc == .Naked) return self.fail("TODO: gen support callconv(.{s})", .{@tagName(cc)}); - _ = try self.addInst(.{ .tag = .psuedo_prologue, - .data = .{ .imm12 = 0 }, // Backpatched later. + .data = .{ .nop = {} }, // Backpatched later. }); _ = try self.addInst(.{ @@ -366,32 +365,6 @@ fn gen(self: *Self) !void { try self.genBody(self.air.getMainBody()); - // Backpatch prologue stack size - if (math.cast(i12, self.max_end_stack)) |casted_stack_size| { - self.mir_instructions.items(.data)[0].imm12 = casted_stack_size; - } else return self.fail("TODO support larger stack sizes, got {}", .{self.max_end_stack}); - - _ = try self.addInst(.{ - .tag = .dbg_epilogue_begin, - .data = .{ .nop = {} }, - }); - - // exitlude jumps - if (self.exitlude_jump_relocs.items.len > 0 and - self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) - { - // If the last Mir instruction (apart from the - // dbg_epilogue_begin) is the last exitlude jump - // relocation (which would just jump one instruction - // further), it can be safely removed - self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); - } - - for (self.exitlude_jump_relocs.items) |jmp_reloc| { - _ = jmp_reloc; - return self.fail("TODO add branches in RISCV64", .{}); - } - // Drop them off at the rbrace. _ = try self.addInst(.{ .tag = .dbg_line, @@ -501,7 +474,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .fpext => try self.airFpext(inst), .intcast => try self.airIntCast(inst), .trunc => try self.airTrunc(inst), - .int_from_bool => try self.airIntFromBool(inst), + .int_from_bool => try self.airIntFromBool(inst), .is_non_null => try self.airIsNonNull(inst), .is_non_null_ptr => try self.airIsNonNullPtr(inst), .is_null => try self.airIsNull(inst), @@ -513,17 +486,17 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .load => try self.airLoad(inst), .loop => try self.airLoop(inst), .not => try self.airNot(inst), - .int_from_ptr => try self.airIntFromPtr(inst), - .ret => try self.airRet(inst), - .ret_safe => try self.airRet(inst), // TODO + .int_from_ptr => try self.airIntFromPtr(inst), + .ret => try self.airRet(inst, false), + .ret_safe => try self.airRet(inst, true), .ret_load => try self.airRetLoad(inst), .store => try self.airStore(inst, false), .store_safe => try self.airStore(inst, true), .struct_field_ptr=> try self.airStructFieldPtr(inst), .struct_field_val=> try self.airStructFieldVal(inst), .array_to_slice => try self.airArrayToSlice(inst), - .float_from_int => try self.airFloatFromInt(inst), - .int_from_float => try self.airIntFromFloat(inst), + .float_from_int => try self.airFloatFromInt(inst), + .int_from_float => try self.airIntFromFloat(inst), .cmpxchg_strong => try self.airCmpxchg(inst), .cmpxchg_weak => try self.airCmpxchg(inst), .atomic_rmw => try self.airAtomicRmw(inst), @@ -792,6 +765,7 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { const stack_offset = try self.allocMemPtr(inst); + log.debug("airAlloc offset: {}", .{stack_offset}); return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); } @@ -1468,30 +1442,30 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind return true; } -fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { +fn load(self: *Self, dst_mcv: MCValue, src_ptr: MCValue, ptr_ty: Type) InnerError!void { const mod = self.bin_file.comp.module.?; const elem_ty = ptr_ty.childType(mod); - switch (ptr) { + switch (src_ptr) { .none => unreachable, .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), - .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), - .register => |src_reg| try self.setRegOrMem(elem_ty, dst_mcv, .{ .register = src_reg }), + .immediate => |imm| try self.setValue(elem_ty, dst_mcv, .{ .memory = imm }), + .ptr_stack_offset => |off| try self.setValue(elem_ty, dst_mcv, .{ .stack_offset = off }), + .register => try self.setValue(elem_ty, dst_mcv, src_ptr), .memory, .stack_offset, => { const reg = try self.register_manager.allocReg(null, gp); const reg_lock = self.register_manager.lockRegAssumeUnused(reg); - defer self.register_manager.unlockReg(reg_lock); + errdefer self.register_manager.unlockReg(reg_lock); - try self.genSetReg(ptr_ty, reg, ptr); + try self.genSetReg(ptr_ty, reg, src_ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); }, .load_symbol => { - const reg = try self.copyToTmpRegister(ptr_ty, ptr); + const reg = try self.copyToTmpRegister(ptr_ty, src_ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); }, } @@ -1524,34 +1498,18 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) !void { +fn store(self: *Self, dst_ptr: MCValue, src_val: MCValue, ptr_ty: Type, value_ty: Type) !void { _ = ptr_ty; - log.debug("storing {s}", .{@tagName(ptr)}); + log.debug("storing {s}", .{@tagName(dst_ptr)}); - switch (ptr) { + switch (dst_ptr) { .none => unreachable, .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .immediate => |imm| { - try self.setRegOrMem(value_ty, .{ .memory = imm }, value); - }, - .ptr_stack_offset => |off| { - try self.genSetStack(value_ty, off, value); - }, - .register => { - return self.fail("TODO implement storing to MCValue.register", .{}); - }, - .memory => { - return self.fail("TODO implement storing to MCValue.memory", .{}); - }, - .stack_offset => { - return self.fail("TODO implement storing to MCValue.stack_offset", .{}); - }, - .load_symbol => { - return self.fail("TODO implement storing to MCValue.load_symbol", .{}); - }, + .ptr_stack_offset => |off| try self.genSetStack(value_ty, off, src_val), + else => return self.fail("TODO implement storing to MCValue.{s}", .{@tagName(dst_ptr)}), } } @@ -1629,29 +1587,32 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { } fn airArg(self: *Self, inst: Air.Inst.Index) !void { - const arg_index = self.arg_index; - self.arg_index += 1; + var arg_index = self.arg_index; - const ty = self.typeOfIndex(inst); - _ = ty; + // we skip over args that have no bits + while (self.args[arg_index] == .none) arg_index += 1; + self.arg_index = arg_index + 1; - const result = self.args[arg_index]; - // TODO support stack-only arguments - // TODO Copy registers to the stack - const mcv = result; - try self.genArgDbgInfo(inst, mcv); + const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { + const arg_ty = self.typeOfIndex(inst); + _ = arg_ty; + const src_mcv = self.args[arg_index]; - if (self.liveness.isUnused(inst)) - return self.finishAirBookkeeping(); + const dst_mcv = switch (src_mcv) { + .register => |src_reg| dst: { + self.register_manager.getRegAssumeFree(src_reg, inst); + break :dst src_mcv; + }, + // don't need to allocate anything, can just be used immediately. + .stack_offset => src_mcv, + else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}), + }; - switch (mcv) { - .register => |reg| { - self.register_manager.getRegAssumeFree(reg, inst); - }, - else => {}, - } + try self.genArgDbgInfo(inst, src_mcv); + break :result dst_mcv; + }; - return self.finishAir(inst, mcv, .{ .none, .none, .none }); + return self.finishAir(inst, result, .{ .none, .none, .none }); } fn airTrap(self: *Self) !void { @@ -1704,26 +1665,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const arg = args[arg_i]; const arg_ty = self.typeOf(arg); const arg_mcv = try self.resolveInst(args[arg_i]); - - switch (mc_arg) { - .none => continue, - .undef => unreachable, - .immediate => unreachable, - .unreach => unreachable, - .dead => unreachable, - .memory => unreachable, - .register => |reg| { - try self.register_manager.getReg(reg, null); - try self.genSetReg(arg_ty, reg, arg_mcv); - }, - .stack_offset => |off| try self.genSetStack(arg_ty, off, arg_mcv), - .ptr_stack_offset => { - return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); - }, - .load_symbol => { - return self.fail("TODO implement calling with MCValue.load_symbol", .{}); - }, - } + try self.setValue(arg_ty, mc_arg, arg_mcv); } if (try self.air.value(callee, mod)) |func_value| { @@ -1791,19 +1733,39 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier fn ret(self: *Self, mcv: MCValue) !void { const mod = self.bin_file.comp.module.?; const ret_ty = self.fn_type.fnReturnType(mod); - try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); + try self.setValue(ret_ty, self.ret_mcv, mcv); + // Just add space for an instruction, patch this later const index = try self.addInst(.{ - .tag = .nop, + .tag = .ret, .data = .{ .nop = {} }, }); + try self.exitlude_jump_relocs.append(self.gpa, index); } -fn airRet(self: *Self, inst: Air.Inst.Index) !void { +fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void { + if (safety) { + // safe + } else { + // not safe + } + const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try self.resolveInst(un_op); + + _ = try self.addInst(.{ + .tag = .dbg_epilogue_begin, + .data = .{ .nop = {} }, + }); + + _ = try self.addInst(.{ + .tag = .psuedo_epilogue, + .data = .{ .nop = {} }, + }); + try self.ret(operand); + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } @@ -1983,7 +1945,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); // TODO make sure the destination stack offset / register does not already have something // going on there. - try self.setRegOrMem(self.typeOfIndex(else_key), canon_mcv, else_value); + try self.setValue(self.typeOfIndex(else_key), canon_mcv, else_value); // TODO track the new register / stack allocation } try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); @@ -2010,7 +1972,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); // TODO make sure the destination stack offset / register does not already have something // going on there. - try self.setRegOrMem(self.typeOfIndex(then_key), parent_mcv, then_value); + try self.setValue(self.typeOfIndex(then_key), parent_mcv, then_value); // TODO track the new register / stack allocation } @@ -2195,7 +2157,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { /// Send control flow to the `index` of `self.code`. fn jump(self: *Self, index: Mir.Inst.Index) !void { _ = try self.addInst(.{ - .tag = .psuedo_jump, + .tag = .j, .data = .{ .inst = index, }, @@ -2270,7 +2232,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { if (block_mcv == .none) { block_data.mcv = operand_mcv; } else { - try self.setRegOrMem(self.typeOfIndex(block), block_mcv, operand_mcv); + try self.setValue(self.typeOfIndex(block), block_mcv, operand_mcv); } } return self.brVoid(block); @@ -2415,28 +2377,32 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT } /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. -fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { - if (!loc.isMutable()) { - return std.debug.panic("tried to setRegOrMem immutable: {s}", .{@tagName(loc)}); +fn setValue(self: *Self, ty: Type, dst_val: MCValue, src_val: MCValue) !void { + // There isn't anything to store + if (dst_val == .none) return; + + if (!dst_val.isMutable()) { + return std.debug.panic("tried to setValue immutable: {s}", .{@tagName(dst_val)}); } - switch (loc) { - .none => return, - .register => |reg| return self.genSetReg(ty, reg, val), - .stack_offset => |off| return self.genSetStack(ty, off, val), - else => return self.fail("TODO: setRegOrMem {s}", .{@tagName(loc)}), + switch (dst_val) { + .register => |reg| return self.genSetReg(ty, reg, src_val), + .stack_offset => |off| return self.genSetStack(ty, off, src_val), + .memory => |addr| return self.genSetMem(ty, addr, src_val), + else => return self.fail("TODO: setValue {s}", .{@tagName(dst_val)}), } } -fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { +/// Sets the value of `src_val` into stack memory at `stack_offset`. +fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) InnerError!void { const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); - switch (mcv) { + switch (src_val) { .none => return, .dead => unreachable, .immediate => { - const reg = try self.copyToTmpRegister(ty, mcv); + const reg = try self.copyToTmpRegister(ty, src_val); return self.genSetStack(ty, stack_offset, .{ .register = reg }); }, .register => |reg| { @@ -2456,8 +2422,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .tag = tag, .data = .{ .i_type = .{ .rd = reg, - .rs1 = .sp, - .imm12 = @intCast(stack_offset), + .rs1 = .s0, + .imm12 = math.cast(i12, stack_offset) orelse { + return self.fail("TODO: genSetStack bigger stack values", .{}); + }, } }, }); }, @@ -2466,7 +2434,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }, .stack_offset, .load_symbol => { if (abi_size <= 8) { - const reg = try self.copyToTmpRegister(ty, mcv); + const reg = try self.copyToTmpRegister(ty, src_val); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); } @@ -2485,7 +2453,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const count_reg = regs[3]; const tmp_reg = regs[4]; - switch (mcv) { + switch (src_val) { .stack_offset => |offset| { if (offset == stack_offset) return; try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset }); @@ -2511,7 +2479,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }, }); }, - else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(mcv)}), + else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_val)}), } try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); @@ -2520,10 +2488,20 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro // memcpy(src, dst, len) try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); }, - else => return self.fail("TODO: genSetStack {s}", .{@tagName(mcv)}), + else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)}), } } +fn genSetMem(self: *Self, ty: Type, addr: u64, src_val: MCValue) InnerError!void { + const mod = self.bin_file.comp.module.?; + const abi_size: u32 = @intCast(ty.abiSize(mod)); + _ = abi_size; + _ = addr; + _ = src_val; + + return self.fail("TODO: genSetMem", .{}); +} + fn genInlineMemcpy( self: *Self, src: Register, @@ -2541,11 +2519,12 @@ fn genInlineMemcpy( return self.fail("TODO: genInlineMemcpy", .{}); } -fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { +/// Sets the value of `src_val` into `reg`. Assumes you have a lock on it. +fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!void { const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); - switch (mcv) { + switch (src_val) { .dead => unreachable, .ptr_stack_offset => return self.fail("TODO genSetReg ptr_stack_offset", .{}), .unreach, .none => return, // Nothing to do. @@ -2634,8 +2613,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .tag = tag, .data = .{ .i_type = .{ .rd = reg, - .rs1 = .sp, - .imm12 = @intCast(off), + .rs1 = .s0, + .imm12 = math.cast(i12, off) orelse { + return self.fail("TODO: genSetReg support larger stack sizes", .{}); + }, } }, }); }, @@ -2685,7 +2666,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); const dest = try self.allocRegOrMem(inst, true); - try self.setRegOrMem(self.typeOfIndex(inst), dest, operand); + try self.setValue(self.typeOfIndex(inst), dest, operand); break :result dest; }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); @@ -2912,7 +2893,6 @@ const CallMCValues = struct { /// Caller must call `CallMCValues.deinit`. fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { const mod = self.bin_file.comp.module.?; - const ip = &mod.intern_pool; const fn_info = mod.typeToFunc(fn_ty).?; const cc = fn_info.cc; var result: CallMCValues = .{ @@ -2935,21 +2915,20 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { return result; }, .Unspecified, .C => { - // LP64D ABI - // - // TODO make this generic with other ABIs, in particular - // with different hardware floating-point calling - // conventions - var stack_offset: u32 = 0; - - for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| { - const param_type = Type.fromInterned(ty); - const param_size: u32 = @intCast(param_type.abiSize(mod)); - - result_arg.* = .{ .stack_offset = stack_offset }; - stack_offset += param_size; + if (result.args.len > 8) { + return self.fail("TODO: support more than 8 function args", .{}); } + for (0..result.args.len) |i| { + const arg_reg = try self.register_manager.allocReg(null, fa); + result.args[i] = .{ .register = arg_reg }; + } + + // stack_offset = num s registers spilled + local var space + var stack_offset: u32 = 0; + _ = &stack_offset; + // TODO: spill used s registers here + result.stack_byte_count = stack_offset; result.stack_align = .@"16"; }, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 6e2b638b76..3cdf3ce48e 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -27,6 +27,8 @@ prev_di_column: u32, /// Relative to the beginning of `code`. prev_di_pc: usize, +stack_size: u32, + const log = std.log.scoped(.emit); const InnerError = error{ @@ -39,10 +41,8 @@ pub fn emitMir( ) InnerError!void { const mir_tags = emit.mir.instructions.items(.tag); - // TODO: compute branch offsets - // try emit.lowerMir(); + try emit.lowerMir(); - // Emit machine code for (mir_tags, 0..) |tag, index| { const inst = @as(u32, @intCast(index)); log.debug("emitMir: {s}", .{@tagName(tag)}); @@ -70,7 +70,9 @@ pub fn emitMir( .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), .psuedo_prologue => try emit.mirPsuedo(inst), - .psuedo_jump => try emit.mirPsuedo(inst), + .psuedo_epilogue => try emit.mirPsuedo(inst), + + .j => try emit.mirPsuedo(inst), .mv => try emit.mirRR(inst), @@ -80,13 +82,15 @@ pub fn emitMir( .lui => try emit.mirUType(inst), .ld => try emit.mirIType(inst), - .sd => try emit.mirIType(inst), .lw => try emit.mirIType(inst), - .sw => try emit.mirIType(inst), .lh => try emit.mirIType(inst), - .sh => try emit.mirIType(inst), .lb => try emit.mirIType(inst), + + .sd => try emit.mirIType(inst), + .sw => try emit.mirIType(inst), + .sh => try emit.mirIType(inst), .sb => try emit.mirIType(inst), + .ldr_ptr_stack => try emit.mirIType(inst), .load_symbol => try emit.mirLoadSymbol(inst), @@ -170,8 +174,6 @@ 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 inst = b_type.imm12; - switch (tag) { .beq => try emit.writeInstruction(Instruction.beq(b_type.rs1, b_type.rs2, b_type.imm12)), else => unreachable, @@ -187,12 +189,13 @@ fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void { .jalr => try emit.writeInstruction(Instruction.jalr(i_type.rd, i_type.imm12, i_type.rs1)), .ld => try emit.writeInstruction(Instruction.ld(i_type.rd, i_type.imm12, i_type.rs1)), - .sd => try emit.writeInstruction(Instruction.sd(i_type.rd, i_type.imm12, i_type.rs1)), .lw => try emit.writeInstruction(Instruction.lw(i_type.rd, i_type.imm12, i_type.rs1)), - .sw => try emit.writeInstruction(Instruction.sw(i_type.rd, i_type.imm12, i_type.rs1)), .lh => try emit.writeInstruction(Instruction.lh(i_type.rd, i_type.imm12, i_type.rs1)), - .sh => try emit.writeInstruction(Instruction.sh(i_type.rd, i_type.imm12, i_type.rs1)), .lb => try emit.writeInstruction(Instruction.lb(i_type.rd, i_type.imm12, i_type.rs1)), + + .sd => try emit.writeInstruction(Instruction.sd(i_type.rd, i_type.imm12, i_type.rs1)), + .sw => try emit.writeInstruction(Instruction.sw(i_type.rd, i_type.imm12, i_type.rs1)), + .sh => try emit.writeInstruction(Instruction.sh(i_type.rd, i_type.imm12, i_type.rs1)), .sb => try emit.writeInstruction(Instruction.sb(i_type.rd, i_type.imm12, i_type.rs1)), .ldr_ptr_stack => try emit.writeInstruction(Instruction.add(i_type.rd, i_type.rs1, .sp)), @@ -262,21 +265,44 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .psuedo_prologue => { - const imm12 = data.imm12; - const stack_size: i12 = @max(32, imm12); + const stack_size: i12 = math.cast(i12, emit.stack_size) orelse { + return emit.fail("TODO: mirPsuedo support larger stack sizes", .{}); + }; + // Decrement sp by num s registers + local var space try emit.writeInstruction(Instruction.addi(.sp, .sp, -stack_size)); + + // Spill ra try emit.writeInstruction(Instruction.sd(.ra, stack_size - 8, .sp)); + + // Spill s0 try emit.writeInstruction(Instruction.sd(.s0, stack_size - 16, .sp)); + + // Setup s0 try emit.writeInstruction(Instruction.addi(.s0, .sp, stack_size)); }, + .psuedo_epilogue => { + const stack_size: i12 = math.cast(i12, emit.stack_size) orelse { + return emit.fail("TODO: mirPsuedo support larger stack sizes", .{}); + }; - .psuedo_jump => { + // Restore ra + try emit.writeInstruction(Instruction.ld(.ra, stack_size - 16, .sp)); + + // Restore s0 + try emit.writeInstruction(Instruction.ld(.s0, stack_size - 16, .sp)); + + // Increment sp back to previous value + try emit.writeInstruction(Instruction.addi(.sp, .sp, stack_size)); + }, + + .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 }, else => unreachable, @@ -348,27 +374,43 @@ fn mirLoadSymbol(emit: *Emit, inst: Mir.Inst.Index) !void { } } -fn isBranch(tag: Mir.Inst.Tag) bool { - switch (tag) { - .psuedo_jump => true, +fn isStore(tag: Mir.Inst.Tag) bool { + return switch (tag) { + .sb => true, + .sh => true, + .sw => true, + .sd => true, else => false, - } + }; +} + +fn isLoad(tag: Mir.Inst.Tag) bool { + return switch (tag) { + .lb => true, + .lh => true, + .lw => true, + .ld => true, + else => false, + }; } fn lowerMir(emit: *Emit) !void { - const comp = emit.bin_file.comp; - const gpa = comp.gpa; const mir_tags = emit.mir.instructions.items(.tag); - - _ = gpa; + const mir_datas = emit.mir.instructions.items(.data); for (mir_tags, 0..) |tag, index| { const inst: u32 = @intCast(index); - if (isBranch(tag)) { - const target_inst = emit.mir.instructions.items(.data)[inst].inst; - - _ = target_inst; + if (isStore(tag) or isLoad(tag)) { + const data = mir_datas[inst].i_type; + // TODO: probably create a psuedo instruction for s0 loads/stores instead of this. + if (data.rs1 == .s0) { + const casted_size = math.cast(i12, emit.stack_size) orelse { + return emit.fail("TODO: support bigger stack sizes lowerMir", .{}); + }; + const offset = mir_datas[inst].i_type.imm12; + mir_datas[inst].i_type.imm12 = -(casted_size - 12 - offset); + } } } } diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 738012022c..4154c129cd 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -39,6 +39,8 @@ pub const Inst = struct { sub, jal, + /// Jumps. Uses `inst` payload. + j, // TODO: Maybe create a special data for compares that includes the ops /// Compare equal, uses r_type @@ -81,8 +83,9 @@ pub const Inst = struct { /// Psuedo-instruction that will generate a backpatched /// function prologue. psuedo_prologue, - /// Jumps. Uses `inst` payload. - psuedo_jump, + /// Psuedo-instruction that will generate a backpatched + /// function epilogue + psuedo_epilogue, // TODO: add description load_symbol, diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index be3ac590a2..4d72219d8d 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -92,10 +92,15 @@ pub fn classifyType(ty: Type, mod: *Module) Class { } pub const callee_preserved_regs = [_]Register{ - .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, + // NOTE: we use s0 as a psuedo stack pointer, so it's not included. + .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, }; -const allocatable_registers = callee_preserved_regs; +pub const function_arg_regs = [_]Register{ + .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7, +}; + +const allocatable_registers = callee_preserved_regs ++ function_arg_regs; pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); // Register classes @@ -109,4 +114,13 @@ pub const RegisterClass = struct { }, true); break :blk set; }; + + pub const fa: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = callee_preserved_regs.len, + .end = callee_preserved_regs.len + function_arg_regs.len, + }, true); + break :blk set; + }; };