Commit 55b28c7e44

David Rubin <daviru007@icloud.com>
2024-05-02 11:07:28
riscv: PRO member function calls
this is enough progress for us to be able to call `stdout.write`!
1 parent c457f35
Changed files (2)
src
arch
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);
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);