From 55b28c7e4438b3b8404a0fd703aad45db9dfe2ff Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 2 May 2024 02:07:28 -0700 Subject: [PATCH] riscv: PRO member function calls this is enough progress for us to be able to call `stdout.write`! --- src/arch/riscv64/CodeGen.zig | 363 ++++++++++++++++++++++++++++------- src/arch/riscv64/abi.zig | 7 +- 2 files changed, 291 insertions(+), 79 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index f2432bc57d..3aa01c3b1c 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -932,6 +932,25 @@ fn gen(self: *Self) !void { const backpatch_fp_add = try self.addPseudo(.pseudo_dead); const backpatch_spill_callee_preserved_regs = try self.addPseudo(.pseudo_dead); + switch (self.ret_mcv.long) { + .none, .unreach => {}, + .indirect => { + // The address where to store the return value for the caller is in a + // register which the callee is free to clobber. Therefore, we purposely + // spill it to stack immediately. + const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(Type.usize, mod)); + try self.genSetMem( + .{ .frame = frame_index }, + 0, + Type.usize, + self.ret_mcv.long.address().offset(-self.ret_mcv.short.indirect.off), + ); + self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } }; + tracking_log.debug("spill {} to {}", .{ self.ret_mcv.long, frame_index }); + }, + else => unreachable, + } + try self.genBody(self.air.getMainBody()); for (self.exitlude_jump_relocs.items) |jmp_reloc| { @@ -1306,12 +1325,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { var it = self.register_manager.free_registers.iterator(.{ .kind = .unset }); while (it.next()) |index| { const tracked_inst = self.register_manager.registers[index]; + tracking_log.debug("tracked inst: {}", .{tracked_inst}); const tracking = self.getResolvedInstValue(tracked_inst); for (tracking.getRegs()) |reg| { if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break; - } else return self.fail( - \\%{} takes up these regs: {any}, however those regs don't use it - , .{ index, tracking.getRegs() }); + } else return std.debug.panic( + \\%{} takes up these regs: {any}, however these regs {any}, don't use it + , .{ tracked_inst, tracking.getRegs(), RegisterManager.regAtTrackedIndex(@intCast(index)) }); } } } @@ -1540,7 +1560,7 @@ fn symbolIndex(self: *Self) !u32 { const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); break :blk atom_index; }, - else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}), + else => return self.fail("TODO symbolIndex {s}", .{@tagName(self.bin_file.tag)}), }; } @@ -1961,7 +1981,7 @@ fn binOp( switch (lhs_ty.zigTypeTag(zcu)) { .Float => return self.fail("TODO binary operations on floats", .{}), .Vector => return self.fail("TODO binary operations on vectors", .{}), - .Int, .Enum => { + .Int, .Enum, .ErrorSet => { assert(lhs_ty.eql(rhs_ty, zcu)); const int_info = lhs_ty.intInfo(zcu); if (int_info.bits <= 64) { @@ -2304,8 +2324,127 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); + const zcu = self.bin_file.comp.module.?; + const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + + const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.typeOf(extra.lhs); + const rhs_ty = self.typeOf(extra.rhs); + + const int_info = lhs_ty.intInfo(zcu); + + if (!math.isPowerOfTwo(int_info.bits) or !(int_info.bits >= 8)) { + return self.fail("TODO: airSubWithOverflow non-power of 2 and less than 8 bits", .{}); + } + + const tuple_ty = self.typeOfIndex(inst); + const result_mcv = try self.allocRegOrMem(inst, false); + const offset = result_mcv.load_frame; + + const lhs_reg, const lhs_lock = blk: { + if (lhs == .register) break :blk .{ lhs.register, null }; + + const lhs_reg, const lhs_lock = try self.allocReg(); + try self.genSetReg(lhs_ty, lhs_reg, lhs); + break :blk .{ lhs_reg, lhs_lock }; + }; + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); + + const rhs_reg, const rhs_lock = blk: { + if (rhs == .register) break :blk .{ rhs.register, null }; + + const rhs_reg, const rhs_lock = try self.allocReg(); + try self.genSetReg(rhs_ty, rhs_reg, rhs); + break :blk .{ rhs_reg, rhs_lock }; + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + const dest_reg, const dest_lock = try self.allocReg(); + defer self.register_manager.unlockReg(dest_lock); + + switch (int_info.signedness) { + .unsigned => return self.fail("TODO: airSubWithOverflow unsigned", .{}), + .signed => { + switch (int_info.bits) { + 64 => { + // result + _ = try self.addInst(.{ + .tag = .sub, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = dest_reg, + .rs1 = lhs_reg, + .rs2 = rhs_reg, + } }, + }); + + try self.genSetMem( + .{ .frame = offset.index }, + offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))), + lhs_ty, + .{ .register = dest_reg }, + ); + + // overflow check + const overflow_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = 0 }); + + _ = try self.addInst(.{ + .tag = .slt, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = overflow_reg, + .rs1 = overflow_reg, + .rs2 = rhs_reg, + } }, + }); + + _ = try self.addInst(.{ + .tag = .slt, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = rhs_reg, + .rs1 = rhs_reg, + .rs2 = lhs_reg, + } }, + }); + + _ = try self.addInst(.{ + .tag = .xor, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = lhs_reg, + .rs1 = overflow_reg, + .rs2 = rhs_reg, + } }, + }); + + const overflow_mcv = try self.binOp( + .cmp_neq, + .{ .register = overflow_reg }, + Type.usize, + .{ .register = rhs_reg }, + Type.usize, + ); + + try self.genSetMem( + .{ .frame = offset.index }, + offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))), + Type.u1, + overflow_mcv, + ); + + break :result result_mcv; + }, + else => |int_bits| return self.fail("TODO: airSubWithOverflow signed {}", .{int_bits}), + } + }, + } + }; + + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { @@ -2644,8 +2783,25 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { /// T to E!T fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { + const zcu = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}); + + const eu_ty = ty_op.ty.toType(); + const pl_ty = eu_ty.errorUnionPayload(zcu); + const err_ty = eu_ty.errorUnionSet(zcu); + const operand = try self.resolveInst(ty_op.operand); + + const result: MCValue = result: { + if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .{ .immediate = 0 }; + + const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(eu_ty, zcu)); + const pl_off: i32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); + const err_off: i32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); + try self.genSetMem(.{ .frame = frame_index }, pl_off, pl_ty, operand); + try self.genSetMem(.{ .frame = frame_index }, err_off, err_ty, .{ .immediate = 0 }); + break :result .{ .load_frame = .{ .index = frame_index } }; + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -3361,8 +3517,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { .rs1 = dst_reg, } }, }); - - return self.fail("TODO: airStructFieldVal register with field_off > 0", .{}); } break :result if (field_off == 0) dst_mcv else try self.copyToNewRegister(inst, dst_mcv); @@ -3444,7 +3598,6 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { } fn airArg(self: *Self, inst: Air.Inst.Index) !void { - const zcu = self.bin_file.comp.module.?; var arg_index = self.arg_index; // we skip over args that have no bits @@ -3453,31 +3606,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { const src_mcv = self.args[arg_index]; - const arg_ty = self.typeOfIndex(inst); - const dst_mcv = switch (src_mcv) { - .register => dst: { - const frame = try self.allocFrameIndex(FrameAlloc.init(.{ - .size = Type.usize.abiSize(zcu), - .alignment = Type.usize.abiAlignment(zcu), - })); - const dst_mcv: MCValue = .{ .load_frame = .{ .index = frame } }; - try self.genCopy(Type.usize, dst_mcv, src_mcv); - break :dst dst_mcv; - }, - .register_pair => dst: { - const frame = try self.allocFrameIndex(FrameAlloc.init(.{ - .size = Type.usize.abiSize(zcu) * 2, - .alignment = Type.usize.abiAlignment(zcu), - })); - const dst_mcv: MCValue = .{ .load_frame = .{ .index = frame } }; - try self.genCopy(arg_ty, dst_mcv, src_mcv); - break :dst dst_mcv; - }, - .load_frame => src_mcv, - else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}), - }; + const dst_mcv = try self.allocRegOrMem(inst, false); + try self.genCopy(arg_ty, dst_mcv, src_mcv); try self.genArgDbgInfo(inst, src_mcv); break :result dst_mcv; @@ -3612,7 +3744,68 @@ fn genCall( stack_frame_align.* = stack_frame_align.max(needed_call_frame.abi_align); } - for (call_info.args, 0..) |mc_arg, arg_i| try self.genCopy(arg_tys[arg_i], mc_arg, args[arg_i]); + var reg_locks = std.ArrayList(?RegisterLock).init(allocator); + defer reg_locks.deinit(); + try reg_locks.ensureTotalCapacity(8); + defer for (reg_locks.items) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); + + const frame_indices = try allocator.alloc(FrameIndex, args.len); + defer allocator.free(frame_indices); + + switch (call_info.return_value.long) { + .none, .unreach => {}, + .indirect => |reg_off| try self.register_manager.getReg(reg_off.reg, null), + else => unreachable, + } + for (call_info.args, args, arg_tys, frame_indices) |dst_arg, src_arg, arg_ty, *frame_index| { + switch (dst_arg) { + .none => {}, + .register => |reg| { + try self.register_manager.getReg(reg, null); + try reg_locks.append(self.register_manager.lockReg(reg)); + }, + .register_pair => |regs| { + for (regs) |reg| try self.register_manager.getReg(reg, null); + try reg_locks.appendSlice(&self.register_manager.lockRegs(2, regs)); + }, + .indirect => |reg_off| { + frame_index.* = try self.allocFrameIndex(FrameAlloc.initType(arg_ty, zcu)); + try self.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg); + try self.register_manager.getReg(reg_off.reg, null); + try reg_locks.append(self.register_manager.lockReg(reg_off.reg)); + }, + else => return self.fail("TODO: genCall set arg {s}", .{@tagName(dst_arg)}), + } + } + + switch (call_info.return_value.long) { + .none, .unreach => {}, + .indirect => |reg_off| { + const ret_ty = Type.fromInterned(fn_info.return_type); + const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(ret_ty, zcu)); + try self.genSetReg(Type.usize, reg_off.reg, .{ + .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, + }); + call_info.return_value.short = .{ .load_frame = .{ .index = frame_index } }; + try reg_locks.append(self.register_manager.lockReg(reg_off.reg)); + }, + else => unreachable, + } + + for (call_info.args, arg_tys, args, frame_indices) |dst_arg, arg_ty, src_arg, frame_index| { + switch (dst_arg) { + .register_pair => try self.genCopy(arg_ty, dst_arg, src_arg), + .register => |dst_reg| try self.genSetReg( + arg_ty, + dst_reg, + src_arg, + ), + .indirect => |reg_off| try self.genSetReg(Type.usize, reg_off.reg, .{ + .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, + }), + else => return self.fail("TODO: genCall actual set {s}", .{@tagName(dst_arg)}), + } + } // Due to incremental compilation, how function calls are generated depends // on linking. @@ -3715,9 +3908,10 @@ fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void { defer self.register_manager.unlockReg(lock); try self.genSetReg(Type.usize, reg_off.reg, self.ret_mcv.long); - try self.genCopy( + try self.genSetMem( + .{ .reg = reg_off.reg }, + reg_off.off, ret_ty, - .{ .register_offset = reg_off }, .{ .air_ref = un_op }, ); }, @@ -3745,6 +3939,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { switch (self.ret_mcv.short) { .none => {}, .register, .register_pair => try self.load(self.ret_mcv.short, ptr, ptr_ty), + .indirect => |reg_off| try self.genSetReg(ptr_ty, reg_off.reg, ptr), else => unreachable, } self.ret_mcv.liveOut(self, inst); @@ -4058,7 +4253,7 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) _ = maybe_inst; - const err_off = errUnionErrorOffset(eu_ty.errorUnionPayload(zcu), zcu); + const err_off: u31 = @intCast(errUnionErrorOffset(eu_ty.errorUnionPayload(zcu), zcu)); switch (eu_mcv) { .register => |reg| { @@ -4081,15 +4276,25 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) ); } - return_mcv = try self.binOp( + return try self.binOp( .cmp_neq, return_mcv, Type.u16, .{ .immediate = 0 }, Type.u16, ); - - return return_mcv; + }, + .load_frame => |frame_addr| { + return self.binOp( + .cmp_neq, + .{ .load_frame = .{ + .index = frame_addr.index, + .off = frame_addr.off + err_off, + } }, + Type.anyerror, + .{ .immediate = 0 }, + Type.anyerror, + ); }, else => return self.fail("TODO implement isErr for {}", .{eu_mcv}), } @@ -4839,7 +5044,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! const zcu = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(zcu)); - if (abi_size > 8) return self.fail("tried to set reg with size {}", .{abi_size}); + if (abi_size > 8) return std.debug.panic("tried to set reg with size {}", .{abi_size}); switch (src_mcv) { .dead => unreachable, @@ -4924,7 +5129,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! if (src_reg.id() == reg.id()) return; - // mov reg, src_reg + // mv reg, src_reg _ = try self.addInst(.{ .tag = .pseudo, .ops = .pseudo_mv, @@ -4934,20 +5139,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! } }, }); }, - .register_pair => |pair| try self.genSetReg(ty, reg, .{ .register = pair[0] }), - .memory => |addr| { - try self.genSetReg(ty, reg, .{ .immediate = addr }); - - _ = try self.addInst(.{ - .tag = .ld, - .ops = .rri, - .data = .{ .i_type = .{ - .rd = reg, - .rs1 = reg, - .imm12 = Immediate.s(0), - } }, - }); - }, + .register_pair => return self.fail("genSetReg should we allow reg -> reg_pair?", .{}), .load_frame => |frame| { _ = try self.addInst(.{ .tag = .pseudo, @@ -4966,28 +5158,49 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! } }, }); }, - .lea_frame => |frame| { + .memory => |addr| { + try self.genSetReg(ty, reg, .{ .immediate = addr }); + + _ = try self.addInst(.{ + .tag = .ld, + .ops = .rri, + .data = .{ .i_type = .{ + .rd = reg, + .rs1 = reg, + .imm12 = Immediate.s(0), + } }, + }); + }, + .lea_frame, .register_offset => { _ = try self.addInst(.{ .tag = .pseudo, .ops = .pseudo_lea_rm, .data = .{ .rm = .{ .r = reg, - .m = .{ - .base = .{ .frame = frame.index }, - .mod = .{ - .rm = .{ - .size = self.memSize(ty), - .disp = frame.off, + .m = switch (src_mcv) { + .register_offset => |reg_off| .{ + .base = .{ .reg = reg_off.reg }, + .mod = .{ + .rm = .{ + .size = self.memSize(ty), + .disp = reg_off.off, + }, }, }, + .lea_frame => |frame| .{ + .base = .{ .frame = frame.index }, + .mod = .{ + .rm = .{ + .size = self.memSize(ty), + .disp = frame.off, + }, + }, + }, + else => unreachable, }, } }, }); }, - .load_symbol => { - try self.genSetReg(ty, reg, src_mcv.address()); - try self.genSetReg(ty, reg, .{ .indirect = .{ .reg = reg } }); - }, .indirect => |reg_off| { const load_tag: Mir.Inst.Tag = switch (abi_size) { 1 => .lb, @@ -5022,6 +5235,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! }) }, }); }, + .load_symbol => { + try self.genSetReg(ty, reg, src_mcv.address()); + try self.genSetReg(ty, reg, .{ .indirect = .{ .reg = reg } }); + }, .air_ref => |ref| try self.genSetReg(ty, reg, try self.resolveInst(ref)), else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}), } @@ -5052,7 +5269,6 @@ fn genSetMem( src_mcv, .{ .immediate = abi_size }, ), - .register_offset, .memory, .indirect, @@ -5100,14 +5316,15 @@ fn genSetMem( _ = try self.addInst(.{ .tag = .pseudo, .ops = .pseudo_store_rm, - .data = .{ - .rm = .{ .r = reg, .m = .{ + .data = .{ .rm = .{ + .r = reg, + .m = .{ .base = .{ .frame = frame_index }, .mod = .{ .rm = .{ .size = Memory.Size.fromByteSize(src_size), } }, - } }, - }, + }, + } }, }); try self.genSetMem(base, disp, ty, frame_mcv); try self.freeValue(frame_mcv); diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index e9d0db7136..41edd60c67 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -149,12 +149,7 @@ pub fn classifySystem(ty: Type, zcu: *Module) [8]Class { // anyerror!void can fit into one register if (payload_bits == 0) return result; - if (payload_bits <= 64) { - result[1] = .integer; - return result; - } - - std.debug.panic("TODO: classifySystem ErrorUnion > 64 bit payload", .{}); + return memory_class; }, .Struct => { const layout = ty.containerLayout(zcu);