| ... | @@ -38,9 +38,16 @@ const callee_preserved_regs = abi.callee_preserved_regs; | ... | @@ -38,9 +38,16 @@ const callee_preserved_regs = abi.callee_preserved_regs; |
| 38 | const gp = abi.RegisterClass.gp; | 38 | const gp = abi.RegisterClass.gp; |
| 39 | /// Function Args | 39 | /// Function Args |
| 40 | const fa = abi.RegisterClass.fa; | 40 | const fa = abi.RegisterClass.fa; |
| | 41 | /// Temporary Use |
| | 42 | const tp = abi.RegisterClass.tp; |
| 41 | | 43 | |
| 42 | const InnerError = CodeGenError || error{OutOfRegisters}; | 44 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 43 | | 45 | |
| | 46 | const RegisterView = enum(u1) { |
| | 47 | caller, |
| | 48 | callee, |
| | 49 | }; |
| | 50 | |
| 44 | gpa: Allocator, | 51 | gpa: Allocator, |
| 45 | air: Air, | 52 | air: Air, |
| 46 | liveness: Liveness, | 53 | liveness: Liveness, |
| ... | @@ -82,8 +89,8 @@ branch_stack: *std.ArrayList(Branch), | ... | @@ -82,8 +89,8 @@ branch_stack: *std.ArrayList(Branch), |
| 82 | | 89 | |
| 83 | // Key is the block instruction | 90 | // Key is the block instruction |
| 84 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, | 91 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 85 | | | |
| 86 | register_manager: RegisterManager = .{}, | 92 | register_manager: RegisterManager = .{}, |
| | 93 | |
| 87 | /// Maps offset to what is stored there. | 94 | /// Maps offset to what is stored there. |
| 88 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | 95 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 89 | | 96 | |
| ... | @@ -99,6 +106,7 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, | ... | @@ -99,6 +106,7 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 99 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | 106 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 100 | | 107 | |
| 101 | const SymbolOffset = struct { sym: u32, off: i32 = 0 }; | 108 | const SymbolOffset = struct { sym: u32, off: i32 = 0 }; |
| | 109 | const RegisterOffset = struct { reg: Register, off: i32 = 0 }; |
| 102 | | 110 | |
| 103 | const MCValue = union(enum) { | 111 | const MCValue = union(enum) { |
| 104 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. | 112 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. |
| ... | @@ -119,6 +127,8 @@ const MCValue = union(enum) { | ... | @@ -119,6 +127,8 @@ const MCValue = union(enum) { |
| 119 | load_symbol: SymbolOffset, | 127 | load_symbol: SymbolOffset, |
| 120 | /// The value is in a target-specific register. | 128 | /// The value is in a target-specific register. |
| 121 | register: Register, | 129 | register: Register, |
| | 130 | /// The value is split across two registers |
| | 131 | register_pair: [2]Register, |
| 122 | /// The value is in memory at a hard-coded address. | 132 | /// The value is in memory at a hard-coded address. |
| 123 | /// If the type is a pointer, it means the pointer address is at this memory location. | 133 | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 124 | memory: u64, | 134 | memory: u64, |
| ... | @@ -127,10 +137,15 @@ const MCValue = union(enum) { | ... | @@ -127,10 +137,15 @@ const MCValue = union(enum) { |
| 127 | stack_offset: u32, | 137 | stack_offset: u32, |
| 128 | /// The value is a pointer to one of the stack variables (payload is stack offset). | 138 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 129 | ptr_stack_offset: u32, | 139 | ptr_stack_offset: u32, |
| | 140 | air_ref: Air.Inst.Ref, |
| | 141 | /// The value is in memory at a constant offset from the address in a register. |
| | 142 | indirect: RegisterOffset, |
| | 143 | /// The value is a constant offset from the value in a register. |
| | 144 | register_offset: RegisterOffset, |
| 130 | | 145 | |
| 131 | fn isMemory(mcv: MCValue) bool { | 146 | fn isMemory(mcv: MCValue) bool { |
| 132 | return switch (mcv) { | 147 | return switch (mcv) { |
| 133 | .memory, .stack_offset => true, | 148 | .memory, .indirect, .load_frame => true, |
| 134 | else => false, | 149 | else => false, |
| 135 | }; | 150 | }; |
| 136 | } | 151 | } |
| ... | @@ -151,15 +166,85 @@ const MCValue = union(enum) { | ... | @@ -151,15 +166,85 @@ const MCValue = union(enum) { |
| 151 | .immediate, | 166 | .immediate, |
| 152 | .memory, | 167 | .memory, |
| 153 | .ptr_stack_offset, | 168 | .ptr_stack_offset, |
| | 169 | .indirect, |
| 154 | .undef, | 170 | .undef, |
| 155 | .load_symbol, | 171 | .load_symbol, |
| | 172 | .air_ref, |
| 156 | => false, | 173 | => false, |
| 157 | | 174 | |
| 158 | .register, | 175 | .register, |
| | 176 | .register_pair, |
| | 177 | .register_offset, |
| 159 | .stack_offset, | 178 | .stack_offset, |
| 160 | => true, | 179 | => true, |
| 161 | }; | 180 | }; |
| 162 | } | 181 | } |
| | 182 | |
| | 183 | fn address(mcv: MCValue) MCValue { |
| | 184 | return switch (mcv) { |
| | 185 | .none, |
| | 186 | .unreach, |
| | 187 | .dead, |
| | 188 | .immediate, |
| | 189 | .ptr_stack_offset, |
| | 190 | .register_offset, |
| | 191 | .undef, |
| | 192 | .air_ref, |
| | 193 | => unreachable, // not in memory |
| | 194 | |
| | 195 | .memory => |addr| .{ .immediate = addr }, |
| | 196 | .stack_offset => |off| .{ .ptr_stack_offset = off }, |
| | 197 | .indirect => |reg_off| switch (reg_off.off) { |
| | 198 | 0 => .{ .register = reg_off.reg }, |
| | 199 | else => .{ .register_offset = reg_off }, |
| | 200 | }, |
| | 201 | }; |
| | 202 | } |
| | 203 | |
| | 204 | fn deref(mcv: MCValue) MCValue { |
| | 205 | return switch (mcv) { |
| | 206 | .none, |
| | 207 | .unreach, |
| | 208 | .dead, |
| | 209 | .memory, |
| | 210 | .indirect, |
| | 211 | .undef, |
| | 212 | .air_ref, |
| | 213 | .stack_offset, |
| | 214 | .register_pair, |
| | 215 | .load_symbol, |
| | 216 | => unreachable, // not a pointer |
| | 217 | |
| | 218 | .immediate => |addr| .{ .memory = addr }, |
| | 219 | .ptr_stack_offset => |off| .{ .stack_offset = off }, |
| | 220 | .register => |reg| .{ .indirect = .{ .reg = reg } }, |
| | 221 | .register_offset => |reg_off| .{ .indirect = reg_off }, |
| | 222 | }; |
| | 223 | } |
| | 224 | |
| | 225 | fn offset(mcv: MCValue, off: i32) MCValue { |
| | 226 | return switch (mcv) { |
| | 227 | .none, |
| | 228 | .unreach, |
| | 229 | .dead, |
| | 230 | .undef, |
| | 231 | .air_ref, |
| | 232 | => unreachable, // not valid |
| | 233 | .register_pair, |
| | 234 | .memory, |
| | 235 | .indirect, |
| | 236 | .stack_offset, |
| | 237 | .load_symbol, |
| | 238 | => switch (off) { |
| | 239 | 0 => mcv, |
| | 240 | else => unreachable, // not offsettable |
| | 241 | }, |
| | 242 | .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) }, |
| | 243 | .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } }, |
| | 244 | .register_offset => |reg_off| .{ .register_offset = .{ .reg = reg_off.reg, .off = reg_off.off + off } }, |
| | 245 | .ptr_stack_offset => |stack_off| .{ .ptr_stack_offset = @intCast(@as(i64, @intCast(stack_off)) +% off) }, |
| | 246 | }; |
| | 247 | } |
| 163 | }; | 248 | }; |
| 164 | | 249 | |
| 165 | const Branch = struct { | 250 | const Branch = struct { |
| ... | @@ -211,6 +296,11 @@ const BigTomb = struct { | ... | @@ -211,6 +296,11 @@ const BigTomb = struct { |
| 211 | | 296 | |
| 212 | const Self = @This(); | 297 | const Self = @This(); |
| 213 | | 298 | |
| | 299 | const CallView = enum(u1) { |
| | 300 | callee, |
| | 301 | caller, |
| | 302 | }; |
| | 303 | |
| 214 | pub fn generate( | 304 | pub fn generate( |
| 215 | lf: *link.File, | 305 | lf: *link.File, |
| 216 | src_loc: Module.SrcLoc, | 306 | src_loc: Module.SrcLoc, |
| ... | @@ -261,7 +351,7 @@ pub fn generate( | ... | @@ -261,7 +351,7 @@ pub fn generate( |
| 261 | defer function.blocks.deinit(gpa); | 351 | defer function.blocks.deinit(gpa); |
| 262 | defer function.exitlude_jump_relocs.deinit(gpa); | 352 | defer function.exitlude_jump_relocs.deinit(gpa); |
| 263 | | 353 | |
| 264 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 354 | var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) { |
| 265 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 355 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 266 | error.OutOfRegisters => return Result{ | 356 | error.OutOfRegisters => return Result{ |
| 267 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 357 | .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| ... | @@ -284,6 +374,14 @@ pub fn generate( | ... | @@ -284,6 +374,14 @@ pub fn generate( |
| 284 | else => |e| return e, | 374 | else => |e| return e, |
| 285 | }; | 375 | }; |
| 286 | | 376 | |
| | 377 | // Create list of registers to save in the prologue. |
| | 378 | var save_reg_list = Mir.RegisterList{}; |
| | 379 | for (callee_preserved_regs) |reg| { |
| | 380 | if (function.register_manager.isRegAllocated(reg)) { |
| | 381 | save_reg_list.push(&callee_preserved_regs, reg); |
| | 382 | } |
| | 383 | } |
| | 384 | |
| 287 | var mir = Mir{ | 385 | var mir = Mir{ |
| 288 | .instructions = function.mir_instructions.toOwnedSlice(), | 386 | .instructions = function.mir_instructions.toOwnedSlice(), |
| 289 | .extra = try function.mir_extra.toOwnedSlice(gpa), | 387 | .extra = try function.mir_extra.toOwnedSlice(gpa), |
| ... | @@ -300,8 +398,10 @@ pub fn generate( | ... | @@ -300,8 +398,10 @@ pub fn generate( |
| 300 | .prev_di_pc = 0, | 398 | .prev_di_pc = 0, |
| 301 | .prev_di_line = func.lbrace_line, | 399 | .prev_di_line = func.lbrace_line, |
| 302 | .prev_di_column = func.lbrace_column, | 400 | .prev_di_column = func.lbrace_column, |
| 303 | .stack_size = @max(32, function.max_end_stack), | | |
| 304 | .code_offset_mapping = .{}, | 401 | .code_offset_mapping = .{}, |
| | 402 | // need to at least decrease the sp by -8 |
| | 403 | .stack_size = @max(8, mem.alignForward(u32, function.max_end_stack, 16)), |
| | 404 | .save_reg_list = save_reg_list, |
| 305 | }; | 405 | }; |
| 306 | defer emit.deinit(); | 406 | defer emit.deinit(); |
| 307 | | 407 | |
| ... | @@ -629,6 +729,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -629,6 +729,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 629 | } | 729 | } |
| 630 | } | 730 | } |
| 631 | | 731 | |
| | 732 | fn feed(self: *Self, bt: *Liveness.BigTomb, operand: Air.Inst.Ref) !void { |
| | 733 | if (bt.feed()) if (operand.toIndex()) |inst| self.processDeath(inst); |
| | 734 | } |
| | 735 | |
| 632 | /// Asserts there is already capacity to insert into top branch inst_table. | 736 | /// Asserts there is already capacity to insert into top branch inst_table. |
| 633 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { | 737 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 634 | // When editing this function, note that the logic must synchronize with `reuseOperand`. | 738 | // When editing this function, note that the logic must synchronize with `reuseOperand`. |
| ... | @@ -639,7 +743,7 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { | ... | @@ -639,7 +743,7 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 639 | .register => |reg| { | 743 | .register => |reg| { |
| 640 | self.register_manager.freeReg(reg); | 744 | self.register_manager.freeReg(reg); |
| 641 | }, | 745 | }, |
| 642 | else => {}, // TODO process stack allocation death | 746 | else => {}, // TODO process stack allocation death by freeing it to be reused later |
| 643 | } | 747 | } |
| 644 | } | 748 | } |
| 645 | | 749 | |
| ... | @@ -650,17 +754,11 @@ fn finishAirBookkeeping(self: *Self) void { | ... | @@ -650,17 +754,11 @@ fn finishAirBookkeeping(self: *Self) void { |
| 650 | } | 754 | } |
| 651 | } | 755 | } |
| 652 | | 756 | |
| 653 | fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { | 757 | fn finishAirResult(self: *Self, inst: Air.Inst.Index, result: MCValue) void { |
| 654 | var tomb_bits = self.liveness.getTombBits(inst); | 758 | if (self.liveness.isUnused(inst)) switch (result) { |
| 655 | for (operands) |op| { | 759 | .none, .dead, .unreach => {}, |
| 656 | const dies = @as(u1, @truncate(tomb_bits)) != 0; | 760 | else => unreachable, // Why didn't the result die? |
| 657 | tomb_bits >>= 1; | 761 | } else { |
| 658 | if (!dies) continue; | | |
| 659 | const op_index = op.toIndex() orelse continue; | | |
| 660 | self.processDeath(op_index); | | |
| 661 | } | | |
| 662 | const is_used = @as(u1, @truncate(tomb_bits)) == 0; | | |
| 663 | if (is_used) { | | |
| 664 | log.debug("%{d} => {}", .{ inst, result }); | 762 | log.debug("%{d} => {}", .{ inst, result }); |
| 665 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 763 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 666 | branch.inst_table.putAssumeCapacityNoClobber(inst, result); | 764 | branch.inst_table.putAssumeCapacityNoClobber(inst, result); |
| ... | @@ -682,6 +780,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live | ... | @@ -682,6 +780,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 682 | self.finishAirBookkeeping(); | 780 | self.finishAirBookkeeping(); |
| 683 | } | 781 | } |
| 684 | | 782 | |
| | 783 | fn finishAir( |
| | 784 | self: *Self, |
| | 785 | inst: Air.Inst.Index, |
| | 786 | result: MCValue, |
| | 787 | operands: [Liveness.bpi - 1]Air.Inst.Ref, |
| | 788 | ) !void { |
| | 789 | var tomb_bits = self.liveness.getTombBits(inst); |
| | 790 | for (operands) |op| { |
| | 791 | const dies = @as(u1, @truncate(tomb_bits)) != 0; |
| | 792 | tomb_bits >>= 1; |
| | 793 | if (!dies) continue; |
| | 794 | self.processDeath(op.toIndexAllowNone() orelse continue); |
| | 795 | } |
| | 796 | self.finishAirResult(inst, result); |
| | 797 | } |
| | 798 | |
| 685 | fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { | 799 | fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { |
| 686 | const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; | 800 | const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; |
| 687 | try table.ensureUnusedCapacity(self.gpa, additional_count); | 801 | try table.ensureUnusedCapacity(self.gpa, additional_count); |
| ... | @@ -716,6 +830,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { | ... | @@ -716,6 +830,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 716 | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | 830 | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 717 | const mod = self.bin_file.comp.module.?; | 831 | const mod = self.bin_file.comp.module.?; |
| 718 | const elem_ty = self.typeOfIndex(inst); | 832 | const elem_ty = self.typeOfIndex(inst); |
| | 833 | |
| 719 | const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { | 834 | const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { |
| 720 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); | 835 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); |
| 721 | }; | 836 | }; |
| ... | @@ -728,12 +843,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | ... | @@ -728,12 +843,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 728 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 843 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 729 | if (abi_size <= ptr_bytes) { | 844 | if (abi_size <= ptr_bytes) { |
| 730 | if (self.register_manager.tryAllocReg(inst, gp)) |reg| { | 845 | if (self.register_manager.tryAllocReg(inst, gp)) |reg| { |
| 731 | return MCValue{ .register = reg }; | 846 | return .{ .register = reg }; |
| 732 | } | 847 | } |
| 733 | } | 848 | } |
| 734 | } | 849 | } |
| 735 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); | 850 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 736 | return MCValue{ .stack_offset = stack_offset }; | 851 | return .{ .stack_offset = stack_offset }; |
| 737 | } | 852 | } |
| 738 | | 853 | |
| 739 | /// Allocates a register from the general purpose set and returns the Register and the Lock. | 854 | /// Allocates a register from the general purpose set and returns the Register and the Lock. |
| ... | @@ -746,6 +861,12 @@ fn allocReg(self: *Self) !struct { Register, RegisterLock } { | ... | @@ -746,6 +861,12 @@ fn allocReg(self: *Self) !struct { Register, RegisterLock } { |
| 746 | } | 861 | } |
| 747 | | 862 | |
| 748 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { | 863 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| | 864 | const mod = self.bin_file.comp.module.?; |
| | 865 | const elem_ty = self.typeOfIndex(inst); |
| | 866 | |
| | 867 | // there isn't anything to spill |
| | 868 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return; |
| | 869 | |
| 749 | const stack_mcv = try self.allocRegOrMem(inst, false); | 870 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 750 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); | 871 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 751 | const reg_mcv = self.getResolvedInstValue(inst); | 872 | const reg_mcv = self.getResolvedInstValue(inst); |
| ... | @@ -759,7 +880,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void | ... | @@ -759,7 +880,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 759 | /// allocated. A second call to `copyToTmpRegister` may return the same register. | 880 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 760 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 881 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 761 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { | 882 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 762 | const reg = try self.register_manager.allocReg(null, gp); | 883 | const reg = try self.register_manager.allocReg(null, tp); |
| 763 | try self.genSetReg(ty, reg, mcv); | 884 | try self.genSetReg(ty, reg, mcv); |
| 764 | return reg; | 885 | return reg; |
| 765 | } | 886 | } |
| ... | @@ -830,7 +951,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -830,7 +951,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 830 | math.divCeil(u32, src_storage_bits, 64) catch unreachable and | 951 | math.divCeil(u32, src_storage_bits, 64) catch unreachable and |
| 831 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { | 952 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { |
| 832 | const dst_mcv = try self.allocRegOrMem(inst, true); | 953 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 833 | try self.setValue(min_ty, dst_mcv, src_mcv); | 954 | try self.genCopy(min_ty, dst_mcv, src_mcv); |
| 834 | break :dst dst_mcv; | 955 | break :dst dst_mcv; |
| 835 | }; | 956 | }; |
| 836 | | 957 | |
| ... | @@ -1261,43 +1382,48 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1261,43 +1382,48 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1261 | | 1382 | |
| 1262 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { | 1383 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { |
| 1263 | if (int_info.signedness == .unsigned) { | 1384 | if (int_info.signedness == .unsigned) { |
| 1264 | const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset; | 1385 | switch (int_info.bits) { |
| 1265 | | 1386 | 1...8 => { |
| 1266 | const max_val = std.math.pow(u16, 2, int_info.bits) - 1; | 1387 | const max_val = std.math.pow(u16, 2, int_info.bits) - 1; |
| 1267 | | | |
| 1268 | const overflow_reg, const overflow_lock = try self.allocReg(); | | |
| 1269 | defer self.register_manager.unlockReg(overflow_lock); | | |
| 1270 | | 1388 | |
| 1271 | const add_reg, const add_lock = blk: { | 1389 | const overflow_reg, const overflow_lock = try self.allocReg(); |
| 1272 | if (add_result_mcv == .register) break :blk .{ add_result_mcv.register, null }; | 1390 | defer self.register_manager.unlockReg(overflow_lock); |
| 1273 | | | |
| 1274 | const add_reg, const add_lock = try self.allocReg(); | | |
| 1275 | try self.genSetReg(lhs_ty, add_reg, add_result_mcv); | | |
| 1276 | break :blk .{ add_reg, add_lock }; | | |
| 1277 | }; | | |
| 1278 | defer if (add_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1279 | | | |
| 1280 | _ = try self.addInst(.{ | | |
| 1281 | .tag = .andi, | | |
| 1282 | .data = .{ .i_type = .{ | | |
| 1283 | .rd = overflow_reg, | | |
| 1284 | .rs1 = add_reg, | | |
| 1285 | .imm12 = @intCast(max_val), | | |
| 1286 | } }, | | |
| 1287 | }); | | |
| 1288 | | 1391 | |
| 1289 | const overflow_mcv = try self.binOp( | 1392 | const add_reg, const add_lock = blk: { |
| 1290 | .cmp_neq, | 1393 | if (add_result_mcv == .register) break :blk .{ add_result_mcv.register, null }; |
| 1291 | null, | | |
| 1292 | .{ .register = overflow_reg }, | | |
| 1293 | .{ .register = add_reg }, | | |
| 1294 | lhs_ty, | | |
| 1295 | lhs_ty, | | |
| 1296 | ); | | |
| 1297 | | 1394 | |
| 1298 | try self.genSetStack(Type.u1, @intCast(overflow_offset), overflow_mcv); | 1395 | const add_reg, const add_lock = try self.allocReg(); |
| | 1396 | try self.genSetReg(lhs_ty, add_reg, add_result_mcv); |
| | 1397 | break :blk .{ add_reg, add_lock }; |
| | 1398 | }; |
| | 1399 | defer if (add_lock) |lock| self.register_manager.unlockReg(lock); |
| | 1400 | |
| | 1401 | _ = try self.addInst(.{ |
| | 1402 | .tag = .andi, |
| | 1403 | .data = .{ .i_type = .{ |
| | 1404 | .rd = overflow_reg, |
| | 1405 | .rs1 = add_reg, |
| | 1406 | .imm12 = @intCast(max_val), |
| | 1407 | } }, |
| | 1408 | }); |
| | 1409 | |
| | 1410 | const overflow_mcv = try self.binOp( |
| | 1411 | .cmp_neq, |
| | 1412 | null, |
| | 1413 | .{ .register = overflow_reg }, |
| | 1414 | .{ .register = add_reg }, |
| | 1415 | lhs_ty, |
| | 1416 | lhs_ty, |
| | 1417 | ); |
| | 1418 | |
| | 1419 | const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset; |
| | 1420 | try self.genSetStack(Type.u1, @intCast(overflow_offset), overflow_mcv); |
| | 1421 | |
| | 1422 | break :result result_mcv; |
| | 1423 | }, |
| 1299 | | 1424 | |
| 1300 | break :result result_mcv; | 1425 | else => return self.fail("TODO: addWithOverflow check for size {d}", .{int_info.bits}), |
| | 1426 | } |
| 1301 | } else { | 1427 | } else { |
| 1302 | return self.fail("TODO: airAddWithOverFlow calculate carry for signed addition", .{}); | 1428 | return self.fail("TODO: airAddWithOverFlow calculate carry for signed addition", .{}); |
| 1303 | } | 1429 | } |
| ... | @@ -1367,6 +1493,7 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1367,6 +1493,7 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void { |
| 1367 | const rhs = try self.resolveInst(bin_op.rhs); | 1493 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1368 | const lhs_ty = self.typeOf(bin_op.lhs); | 1494 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 1369 | const rhs_ty = self.typeOf(bin_op.rhs); | 1495 | const rhs_ty = self.typeOf(bin_op.rhs); |
| | 1496 | |
| 1370 | break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty); | 1497 | break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1371 | }; | 1498 | }; |
| 1372 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1499 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| ... | @@ -1506,7 +1633,19 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { | ... | @@ -1506,7 +1633,19 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { |
| 1506 | | 1633 | |
| 1507 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { | 1634 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1508 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 1635 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1509 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSliceLen for {}", .{self.target.cpu.arch}); | 1636 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 1637 | const ptr_bits = 64; |
| | 1638 | const ptr_bytes = @divExact(ptr_bits, 8); |
| | 1639 | const mcv = try self.resolveInst(ty_op.operand); |
| | 1640 | switch (mcv) { |
| | 1641 | .dead, .unreach, .none => unreachable, |
| | 1642 | .register => unreachable, // a slice doesn't fit in one register |
| | 1643 | .stack_offset => |off| { |
| | 1644 | break :result MCValue{ .stack_offset = off + ptr_bytes }; |
| | 1645 | }, |
| | 1646 | else => return self.fail("TODO airSliceLen for {}", .{mcv}), |
| | 1647 | } |
| | 1648 | }; |
| 1510 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1649 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1511 | } | 1650 | } |
| 1512 | | 1651 | |
| ... | @@ -1598,10 +1737,60 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1598,10 +1737,60 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void { |
| 1598 | | 1737 | |
| 1599 | fn airCtz(self: *Self, inst: Air.Inst.Index) !void { | 1738 | fn airCtz(self: *Self, inst: Air.Inst.Index) !void { |
| 1600 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 1739 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1601 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); | 1740 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 1741 | const operand = try self.resolveInst(ty_op.operand); |
| | 1742 | const operand_ty = self.typeOf(ty_op.operand); |
| | 1743 | |
| | 1744 | const dest_reg = try self.register_manager.allocReg(inst, gp); |
| | 1745 | |
| | 1746 | const source_reg, const source_lock = blk: { |
| | 1747 | if (operand == .register) break :blk .{ operand.register, null }; |
| | 1748 | |
| | 1749 | const source_reg, const source_lock = try self.allocReg(); |
| | 1750 | try self.genSetReg(operand_ty, source_reg, operand); |
| | 1751 | break :blk .{ source_reg, source_lock }; |
| | 1752 | }; |
| | 1753 | defer if (source_lock) |lock| self.register_manager.unlockReg(lock); |
| | 1754 | |
| | 1755 | // TODO: the B extension for RISCV should have the ctz instruction, and we should use it. |
| | 1756 | |
| | 1757 | try self.ctz(source_reg, dest_reg, operand_ty); |
| | 1758 | |
| | 1759 | break :result .{ .register = dest_reg }; |
| | 1760 | }; |
| 1602 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1761 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1603 | } | 1762 | } |
| 1604 | | 1763 | |
| | 1764 | fn ctz(self: *Self, src: Register, dst: Register, ty: Type) !void { |
| | 1765 | const mod = self.bin_file.comp.module.?; |
| | 1766 | const length = (ty.abiSize(mod) * 8) - 1; |
| | 1767 | |
| | 1768 | const count_reg, const count_lock = try self.allocReg(); |
| | 1769 | defer self.register_manager.unlockReg(count_lock); |
| | 1770 | |
| | 1771 | const len_reg, const len_lock = try self.allocReg(); |
| | 1772 | defer self.register_manager.unlockReg(len_lock); |
| | 1773 | |
| | 1774 | try self.genSetReg(Type.usize, count_reg, .{ .immediate = 0 }); |
| | 1775 | try self.genSetReg(Type.usize, len_reg, .{ .immediate = length }); |
| | 1776 | |
| | 1777 | _ = try self.addInst(.{ |
| | 1778 | .tag = .beq, |
| | 1779 | .data = .{ |
| | 1780 | .b_type = .{ |
| | 1781 | .rs1 = count_reg, |
| | 1782 | .rs2 = len_reg, |
| | 1783 | .inst = @intCast(self.mir_instructions.len + 0), |
| | 1784 | }, |
| | 1785 | }, |
| | 1786 | }); |
| | 1787 | |
| | 1788 | _ = src; |
| | 1789 | _ = dst; |
| | 1790 | |
| | 1791 | return self.fail("TODO: finish ctz", .{}); |
| | 1792 | } |
| | 1793 | |
| 1605 | fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { | 1794 | fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { |
| 1606 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 1795 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1607 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); | 1796 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); |
| ... | @@ -1750,12 +1939,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1750,12 +1939,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 1750 | const elem_ty = self.typeOfIndex(inst); | 1939 | const elem_ty = self.typeOfIndex(inst); |
| 1751 | const result: MCValue = result: { | 1940 | const result: MCValue = result: { |
| 1752 | if (!elem_ty.hasRuntimeBits(mod)) | 1941 | if (!elem_ty.hasRuntimeBits(mod)) |
| 1753 | break :result MCValue.none; | 1942 | break :result .none; |
| 1754 | | 1943 | |
| 1755 | const ptr = try self.resolveInst(ty_op.operand); | 1944 | const ptr = try self.resolveInst(ty_op.operand); |
| 1756 | const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod); | 1945 | const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod); |
| 1757 | if (self.liveness.isUnused(inst) and !is_volatile) | 1946 | if (self.liveness.isUnused(inst) and !is_volatile) |
| 1758 | break :result MCValue.dead; | 1947 | break :result .dead; |
| 1759 | | 1948 | |
| 1760 | const dst_mcv: MCValue = blk: { | 1949 | const dst_mcv: MCValue = blk: { |
| 1761 | if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { | 1950 | if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { |
| ... | @@ -1771,27 +1960,38 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1771,27 +1960,38 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 1771 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1960 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1772 | } | 1961 | } |
| 1773 | | 1962 | |
| 1774 | fn load(self: *Self, dst_mcv: MCValue, src_ptr: MCValue, ptr_ty: Type) InnerError!void { | 1963 | fn load(self: *Self, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerError!void { |
| 1775 | const mod = self.bin_file.comp.module.?; | 1964 | const mod = self.bin_file.comp.module.?; |
| 1776 | const elem_ty = ptr_ty.childType(mod); | 1965 | const dst_ty = ptr_ty.childType(mod); |
| 1777 | | 1966 | |
| 1778 | switch (src_ptr) { | 1967 | log.debug("loading {}:{} into {}", .{ ptr_mcv, ptr_ty.fmt(mod), dst_mcv }); |
| 1779 | .none => unreachable, | | |
| 1780 | .undef => unreachable, | | |
| 1781 | .unreach => unreachable, | | |
| 1782 | .dead => unreachable, | | |
| 1783 | .immediate => |imm| try self.setValue(elem_ty, dst_mcv, .{ .memory = imm }), | | |
| 1784 | .ptr_stack_offset => |off| try self.setValue(elem_ty, dst_mcv, .{ .stack_offset = off }), | | |
| 1785 | | 1968 | |
| 1786 | .stack_offset, | 1969 | switch (ptr_mcv) { |
| | 1970 | .none, |
| | 1971 | .undef, |
| | 1972 | .unreach, |
| | 1973 | .dead, |
| | 1974 | .register_pair, |
| | 1975 | => unreachable, // not a valid pointer |
| | 1976 | |
| | 1977 | .immediate, |
| 1787 | .register, | 1978 | .register, |
| | 1979 | .register_offset, |
| | 1980 | .ptr_stack_offset, |
| | 1981 | => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()), |
| | 1982 | |
| 1788 | .memory, | 1983 | .memory, |
| 1789 | => try self.setValue(elem_ty, dst_mcv, src_ptr), | 1984 | .indirect, |
| | 1985 | .load_symbol, |
| | 1986 | .stack_offset, |
| | 1987 | => { |
| | 1988 | const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); |
| | 1989 | const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); |
| | 1990 | defer self.register_manager.unlockReg(addr_lock); |
| 1790 | | 1991 | |
| 1791 | .load_symbol => { | 1992 | try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } }); |
| 1792 | const reg = try self.copyToTmpRegister(ptr_ty, src_ptr); | | |
| 1793 | try self.load(dst_mcv, .{ .register = reg }, ptr_ty); | | |
| 1794 | }, | 1993 | }, |
| | 1994 | .air_ref => |ptr_ref| try self.load(dst_mcv, try self.resolveInst(ptr_ref), ptr_ty), |
| 1795 | } | 1995 | } |
| 1796 | } | 1996 | } |
| 1797 | | 1997 | |
| ... | @@ -1817,7 +2017,12 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty: | ... | @@ -1817,7 +2017,12 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty: |
| 1817 | const mod = self.bin_file.comp.module.?; | 2017 | const mod = self.bin_file.comp.module.?; |
| 1818 | const value_abi_size = value_ty.abiSize(mod); | 2018 | const value_abi_size = value_ty.abiSize(mod); |
| 1819 | | 2019 | |
| 1820 | log.debug("storing {s}", .{@tagName(pointer)}); | 2020 | log.debug("storing {}:{} in {}:{}", .{ value, value_ty.fmt(mod), pointer, ptr_ty.fmt(mod) }); |
| | 2021 | |
| | 2022 | if (value_ty.isSlice(mod)) { |
| | 2023 | // cheat a bit by loading in two parts |
| | 2024 | |
| | 2025 | } |
| 1821 | | 2026 | |
| 1822 | switch (pointer) { | 2027 | switch (pointer) { |
| 1823 | .none => unreachable, | 2028 | .none => unreachable, |
| ... | @@ -1976,7 +2181,6 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { | ... | @@ -1976,7 +2181,6 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1976 | } | 2181 | } |
| 1977 | | 2182 | |
| 1978 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { | 2183 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1979 | const mod = self.bin_file.comp.module.?; | | |
| 1980 | var arg_index = self.arg_index; | 2184 | var arg_index = self.arg_index; |
| 1981 | | 2185 | |
| 1982 | // we skip over args that have no bits | 2186 | // we skip over args that have no bits |
| ... | @@ -1986,21 +2190,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1986,21 +2190,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1986 | const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { | 2190 | const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { |
| 1987 | const src_mcv = self.args[arg_index]; | 2191 | const src_mcv = self.args[arg_index]; |
| 1988 | | 2192 | |
| 1989 | // we want to move every arg onto the stack. | | |
| 1990 | // while it might no tbe the best solution right now, it simplifies | | |
| 1991 | // the spilling of args with multiple arg levels. | | |
| 1992 | const dst_mcv = switch (src_mcv) { | 2193 | const dst_mcv = switch (src_mcv) { |
| 1993 | .register => |src_reg| dst: { | 2194 | .register => |src_reg| dst: { |
| 1994 | // TODO: get the true type of the arg, and fit the spill to size. | 2195 | try self.register_manager.getReg(src_reg, null); |
| 1995 | const arg_size = Type.usize.abiSize(mod); | 2196 | break :dst src_mcv; |
| 1996 | const arg_align = Type.usize.abiAlignment(mod); | | |
| 1997 | const offset = try self.allocMem(inst, @intCast(arg_size), arg_align); | | |
| 1998 | try self.genSetStack(Type.usize, offset, .{ .register = src_reg }); | | |
| 1999 | | | |
| 2000 | // can go on to be reused in next function call | | |
| 2001 | self.register_manager.freeReg(src_reg); | | |
| 2002 | | | |
| 2003 | break :dst .{ .stack_offset = offset }; | | |
| 2004 | }, | 2197 | }, |
| 2005 | else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}), | 2198 | else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}), |
| 2006 | }; | 2199 | }; |
| ... | @@ -2044,87 +2237,122 @@ fn airFence(self: *Self) !void { | ... | @@ -2044,87 +2237,122 @@ fn airFence(self: *Self) !void { |
| 2044 | } | 2237 | } |
| 2045 | | 2238 | |
| 2046 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { | 2239 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| 2047 | const mod = self.bin_file.comp.module.?; | | |
| 2048 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{}); | 2240 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{}); |
| 2049 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 2241 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 2050 | const fn_ty = self.typeOf(pl_op.operand); | | |
| 2051 | const callee = pl_op.operand; | 2242 | const callee = pl_op.operand; |
| 2052 | const extra = self.air.extraData(Air.Call, pl_op.payload); | 2243 | const extra = self.air.extraData(Air.Call, pl_op.payload); |
| 2053 | const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); | 2244 | const arg_refs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); |
| | 2245 | |
| | 2246 | const expected_num_args = 8; |
| | 2247 | const ExpectedContents = extern struct { |
| | 2248 | vals: [expected_num_args][@sizeOf(MCValue)]u8 align(@alignOf(MCValue)), |
| | 2249 | }; |
| | 2250 | var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = |
| | 2251 | std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); |
| | 2252 | const allocator = stack.get(); |
| | 2253 | |
| | 2254 | const arg_tys = try allocator.alloc(Type, arg_refs.len); |
| | 2255 | defer allocator.free(arg_tys); |
| | 2256 | for (arg_tys, arg_refs) |*arg_ty, arg_ref| arg_ty.* = self.typeOf(arg_ref); |
| | 2257 | |
| | 2258 | const arg_vals = try allocator.alloc(MCValue, arg_refs.len); |
| | 2259 | defer allocator.free(arg_vals); |
| | 2260 | for (arg_vals, arg_refs) |*arg_val, arg_ref| arg_val.* = .{ .air_ref = arg_ref }; |
| | 2261 | |
| | 2262 | const call_ret = try self.genCall(.{ .air = callee }, arg_tys, arg_vals); |
| | 2263 | |
| | 2264 | var bt = self.liveness.iterateBigTomb(inst); |
| | 2265 | try self.feed(&bt, pl_op.operand); |
| | 2266 | for (arg_refs) |arg_ref| try self.feed(&bt, arg_ref); |
| | 2267 | |
| | 2268 | const result = if (self.liveness.isUnused(inst)) .unreach else call_ret; |
| | 2269 | return self.finishAirResult(inst, result); |
| | 2270 | } |
| | 2271 | |
| | 2272 | fn genCall( |
| | 2273 | self: *Self, |
| | 2274 | info: union(enum) { |
| | 2275 | air: Air.Inst.Ref, |
| | 2276 | lib: struct { |
| | 2277 | return_type: InternPool.Index, |
| | 2278 | param_types: []const InternPool.Index, |
| | 2279 | lib: ?[]const u8 = null, |
| | 2280 | callee: []const u8, |
| | 2281 | }, |
| | 2282 | }, |
| | 2283 | arg_tys: []const Type, |
| | 2284 | args: []const MCValue, |
| | 2285 | ) !MCValue { |
| | 2286 | const mod = self.bin_file.comp.module.?; |
| | 2287 | |
| | 2288 | const fn_ty = switch (info) { |
| | 2289 | .air => |callee| fn_info: { |
| | 2290 | const callee_ty = self.typeOf(callee); |
| | 2291 | break :fn_info switch (callee_ty.zigTypeTag(mod)) { |
| | 2292 | .Fn => callee_ty, |
| | 2293 | .Pointer => callee_ty.childType(mod), |
| | 2294 | else => unreachable, |
| | 2295 | }; |
| | 2296 | }, |
| | 2297 | .lib => |lib| try mod.funcType(.{ |
| | 2298 | .param_types = lib.param_types, |
| | 2299 | .return_type = lib.return_type, |
| | 2300 | .cc = .C, |
| | 2301 | }), |
| | 2302 | }; |
| 2054 | | 2303 | |
| 2055 | var info = try self.resolveCallingConventionValues(fn_ty); | 2304 | var call_info = try self.resolveCallingConventionValues(fn_ty, .caller); |
| 2056 | defer info.deinit(self); | 2305 | defer call_info.deinit(self); |
| | 2306 | |
| | 2307 | for (call_info.args, 0..) |mc_arg, arg_i| try self.genCopy(arg_tys[arg_i], mc_arg, args[arg_i]); |
| 2057 | | 2308 | |
| 2058 | // Due to incremental compilation, how function calls are generated depends | 2309 | // Due to incremental compilation, how function calls are generated depends |
| 2059 | // on linking. | 2310 | // on linking. |
| 2060 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 2311 | switch (info) { |
| 2061 | for (info.args, 0..) |mc_arg, arg_i| { | 2312 | .air => |callee| if (try self.air.value(callee, mod)) |func_value| { |
| 2062 | const arg = args[arg_i]; | 2313 | const func_key = mod.intern_pool.indexToKey(func_value.ip_index); |
| 2063 | const arg_ty = self.typeOf(arg); | 2314 | switch (switch (func_key) { |
| 2064 | const arg_mcv = try self.resolveInst(args[arg_i]); | 2315 | else => func_key, |
| 2065 | try self.setValue(arg_ty, mc_arg, arg_mcv); | 2316 | .ptr => |ptr| switch (ptr.addr) { |
| 2066 | } | 2317 | .decl => |decl| mod.intern_pool.indexToKey(mod.declPtr(decl).val.toIntern()), |
| 2067 | | 2318 | else => func_key, |
| 2068 | if (try self.air.value(callee, mod)) |func_value| { | 2319 | }, |
| 2069 | switch (mod.intern_pool.indexToKey(func_value.ip_index)) { | 2320 | }) { |
| 2070 | .func => |func| { | 2321 | .func => |func| { |
| 2071 | const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); | 2322 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 2072 | const sym = elf_file.symbol(sym_index); | 2323 | const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); |
| 2073 | _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); | 2324 | const sym = elf_file.symbol(sym_index); |
| 2074 | const got_addr = sym.zigGotAddress(elf_file); | 2325 | _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); |
| 2075 | try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); | 2326 | const got_addr = sym.zigGotAddress(elf_file); |
| 2076 | _ = try self.addInst(.{ | 2327 | try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); |
| 2077 | .tag = .jalr, | 2328 | _ = try self.addInst(.{ |
| 2078 | .data = .{ .i_type = .{ | 2329 | .tag = .jalr, |
| 2079 | .rd = .ra, | 2330 | .data = .{ .i_type = .{ |
| 2080 | .rs1 = .ra, | 2331 | .rd = .ra, |
| 2081 | .imm12 = 0, | 2332 | .rs1 = .ra, |
| 2082 | } }, | 2333 | .imm12 = 0, |
| 2083 | }); | 2334 | } }, |
| | 2335 | }); |
| | 2336 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| | 2337 | return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch}); |
| | 2338 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| | 2339 | unreachable; // unsupported architecture for MachO |
| | 2340 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { |
| | 2341 | return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}); |
| | 2342 | } else unreachable; |
| 2084 | }, | 2343 | }, |
| 2085 | .extern_func => { | 2344 | .extern_func => { |
| 2086 | return self.fail("TODO implement calling extern functions", .{}); | 2345 | return self.fail("TODO: extern func calls", .{}); |
| 2087 | }, | | |
| 2088 | else => { | | |
| 2089 | return self.fail("TODO implement calling bitcasted functions", .{}); | | |
| 2090 | }, | 2346 | }, |
| | 2347 | else => return self.fail("TODO implement calling bitcasted functions", .{}), |
| 2091 | } | 2348 | } |
| 2092 | } else { | 2349 | } else { |
| 2093 | return self.fail("TODO implement calling runtime-known function pointer", .{}); | 2350 | return self.fail("TODO: call function pointers", .{}); |
| 2094 | } | 2351 | }, |
| 2095 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | 2352 | .lib => return self.fail("TODO: lib func calls", .{}), |
| 2096 | return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch}); | | |
| 2097 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | | |
| 2098 | unreachable; // unsupported architecture for MachO | | |
| 2099 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { | | |
| 2100 | return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}); | | |
| 2101 | } else unreachable; | | |
| 2102 | | | |
| 2103 | const result: MCValue = result: { | | |
| 2104 | switch (info.return_value) { | | |
| 2105 | .register => |reg| { | | |
| 2106 | if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { | | |
| 2107 | // Save function return value in a callee saved register | | |
| 2108 | break :result try self.copyToNewRegister(inst, info.return_value); | | |
| 2109 | } | | |
| 2110 | }, | | |
| 2111 | else => {}, | | |
| 2112 | } | | |
| 2113 | break :result info.return_value; | | |
| 2114 | }; | | |
| 2115 | | | |
| 2116 | if (args.len <= Liveness.bpi - 2) { | | |
| 2117 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); | | |
| 2118 | buf[0] = callee; | | |
| 2119 | @memcpy(buf[1..][0..args.len], args); | | |
| 2120 | return self.finishAir(inst, result, buf); | | |
| 2121 | } | | |
| 2122 | var bt = try self.iterateBigTomb(inst, 1 + args.len); | | |
| 2123 | bt.feed(callee); | | |
| 2124 | for (args) |arg| { | | |
| 2125 | bt.feed(arg); | | |
| 2126 | } | 2353 | } |
| 2127 | return bt.finishAir(result); | 2354 | |
| | 2355 | return call_info.return_value; |
| 2128 | } | 2356 | } |
| 2129 | | 2357 | |
| 2130 | fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void { | 2358 | fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| ... | @@ -2151,7 +2379,7 @@ fn ret(self: *Self, mcv: MCValue) !void { | ... | @@ -2151,7 +2379,7 @@ fn ret(self: *Self, mcv: MCValue) !void { |
| 2151 | const mod = self.bin_file.comp.module.?; | 2379 | const mod = self.bin_file.comp.module.?; |
| 2152 | | 2380 | |
| 2153 | const ret_ty = self.fn_type.fnReturnType(mod); | 2381 | const ret_ty = self.fn_type.fnReturnType(mod); |
| 2154 | try self.setValue(ret_ty, self.ret_mcv, mcv); | 2382 | try self.genCopy(ret_ty, self.ret_mcv, mcv); |
| 2155 | | 2383 | |
| 2156 | _ = try self.addInst(.{ | 2384 | _ = try self.addInst(.{ |
| 2157 | .tag = .psuedo_epilogue, | 2385 | .tag = .psuedo_epilogue, |
| ... | @@ -2183,6 +2411,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2183,6 +2411,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index) !void { |
| 2183 | const ty = self.typeOf(bin_op.lhs); | 2411 | const ty = self.typeOf(bin_op.lhs); |
| 2184 | const mod = self.bin_file.comp.module.?; | 2412 | const mod = self.bin_file.comp.module.?; |
| 2185 | assert(ty.eql(self.typeOf(bin_op.rhs), mod)); | 2413 | assert(ty.eql(self.typeOf(bin_op.rhs), mod)); |
| | 2414 | |
| 2186 | if (ty.zigTypeTag(mod) == .ErrorSet) | 2415 | if (ty.zigTypeTag(mod) == .ErrorSet) |
| 2187 | return self.fail("TODO implement cmp for errors", .{}); | 2416 | return self.fail("TODO implement cmp for errors", .{}); |
| 2188 | | 2417 | |
| ... | @@ -2233,11 +2462,54 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2233,11 +2462,54 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 2233 | | 2462 | |
| 2234 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { | 2463 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| 2235 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 2464 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 2236 | const name = self.air.nullTerminatedString(pl_op.payload); | | |
| 2237 | const operand = pl_op.operand; | 2465 | const operand = pl_op.operand; |
| 2238 | // TODO emit debug info for this variable | 2466 | const ty = self.typeOf(operand); |
| 2239 | _ = name; | 2467 | const mcv = try self.resolveInst(operand); |
| 2240 | return self.finishAir(inst, .dead, .{ operand, .none, .none }); | 2468 | |
| | 2469 | const name = self.air.nullTerminatedString(pl_op.payload); |
| | 2470 | |
| | 2471 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| | 2472 | try self.genVarDbgInfo(tag, ty, mcv, name); |
| | 2473 | |
| | 2474 | return self.finishAir(inst, .unreach, .{ operand, .none, .none }); |
| | 2475 | } |
| | 2476 | |
| | 2477 | fn genVarDbgInfo( |
| | 2478 | self: Self, |
| | 2479 | tag: Air.Inst.Tag, |
| | 2480 | ty: Type, |
| | 2481 | mcv: MCValue, |
| | 2482 | name: [:0]const u8, |
| | 2483 | ) !void { |
| | 2484 | const mod = self.bin_file.comp.module.?; |
| | 2485 | const is_ptr = switch (tag) { |
| | 2486 | .dbg_var_ptr => true, |
| | 2487 | .dbg_var_val => false, |
| | 2488 | else => unreachable, |
| | 2489 | }; |
| | 2490 | |
| | 2491 | switch (self.debug_output) { |
| | 2492 | .dwarf => |dw| { |
| | 2493 | const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) { |
| | 2494 | .register => |reg| .{ .register = reg.dwarfLocOp() }, |
| | 2495 | .memory => |address| .{ .memory = address }, |
| | 2496 | .load_symbol => |sym_off| loc: { |
| | 2497 | assert(sym_off.off == 0); |
| | 2498 | break :loc .{ .linker_load = .{ .type = .direct, .sym_index = sym_off.sym } }; |
| | 2499 | }, |
| | 2500 | .immediate => |x| .{ .immediate = x }, |
| | 2501 | .undef => .undef, |
| | 2502 | .none => .none, |
| | 2503 | else => blk: { |
| | 2504 | log.debug("TODO generate debug info for {}", .{mcv}); |
| | 2505 | break :blk .nop; |
| | 2506 | }, |
| | 2507 | }; |
| | 2508 | try dw.genVarDbgInfo(name, ty, mod.funcOwnerDeclIndex(self.func_index), is_ptr, loc); |
| | 2509 | }, |
| | 2510 | .plan9 => {}, |
| | 2511 | .none => {}, |
| | 2512 | } |
| 2241 | } | 2513 | } |
| 2242 | | 2514 | |
| 2243 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | 2515 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2348,7 +2620,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2348,7 +2620,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2348 | log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); | 2620 | log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); |
| 2349 | // TODO make sure the destination stack offset / register does not already have something | 2621 | // TODO make sure the destination stack offset / register does not already have something |
| 2350 | // going on there. | 2622 | // going on there. |
| 2351 | try self.setValue(self.typeOfIndex(else_key), canon_mcv, else_value); | 2623 | try self.genCopy(self.typeOfIndex(else_key), canon_mcv, else_value); |
| 2352 | // TODO track the new register / stack allocation | 2624 | // TODO track the new register / stack allocation |
| 2353 | } | 2625 | } |
| 2354 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); | 2626 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); |
| ... | @@ -2375,7 +2647,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2375,7 +2647,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2375 | log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); | 2647 | log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); |
| 2376 | // TODO make sure the destination stack offset / register does not already have something | 2648 | // TODO make sure the destination stack offset / register does not already have something |
| 2377 | // going on there. | 2649 | // going on there. |
| 2378 | try self.setValue(self.typeOfIndex(then_key), parent_mcv, then_value); | 2650 | try self.genCopy(self.typeOfIndex(then_key), parent_mcv, then_value); |
| 2379 | // TODO track the new register / stack allocation | 2651 | // TODO track the new register / stack allocation |
| 2380 | } | 2652 | } |
| 2381 | | 2653 | |
| ... | @@ -2638,7 +2910,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { | ... | @@ -2638,7 +2910,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 2638 | if (block_mcv == .none) { | 2910 | if (block_mcv == .none) { |
| 2639 | block_data.mcv = operand_mcv; | 2911 | block_data.mcv = operand_mcv; |
| 2640 | } else { | 2912 | } else { |
| 2641 | try self.setValue(self.typeOfIndex(block), block_mcv, operand_mcv); | 2913 | try self.genCopy(self.typeOfIndex(block), block_mcv, operand_mcv); |
| 2642 | } | 2914 | } |
| 2643 | } | 2915 | } |
| 2644 | return self.brVoid(block); | 2916 | return self.brVoid(block); |
| ... | @@ -2783,29 +3055,45 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT | ... | @@ -2783,29 +3055,45 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT |
| 2783 | } | 3055 | } |
| 2784 | | 3056 | |
| 2785 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. | 3057 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. |
| 2786 | fn setValue(self: *Self, ty: Type, dst_val: MCValue, src_val: MCValue) !void { | 3058 | fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 2787 | // There isn't anything to store | 3059 | // There isn't anything to store |
| 2788 | if (dst_val == .none) return; | 3060 | if (dst_mcv == .none) return; |
| 2789 | | 3061 | |
| 2790 | if (!dst_val.isMutable()) { | 3062 | if (!dst_mcv.isMutable()) { |
| 2791 | // panic so we can see the trace | 3063 | // panic so we can see the trace |
| 2792 | return std.debug.panic("tried to setValue immutable: {s}", .{@tagName(dst_val)}); | 3064 | return std.debug.panic("tried to genCopy immutable: {s}", .{@tagName(dst_mcv)}); |
| 2793 | } | 3065 | } |
| 2794 | | 3066 | |
| 2795 | switch (dst_val) { | 3067 | switch (dst_mcv) { |
| 2796 | .register => |reg| return self.genSetReg(ty, reg, src_val), | 3068 | .register => |reg| return self.genSetReg(ty, reg, src_mcv), |
| 2797 | .stack_offset => |off| return self.genSetStack(ty, off, src_val), | 3069 | .register_pair => |pair| return self.genSetRegPair(ty, pair, src_mcv), |
| 2798 | .memory => |addr| return self.genSetMem(ty, addr, src_val), | 3070 | .register_offset => |dst_reg_off| try self.genSetReg(ty, dst_reg_off.reg, switch (src_mcv) { |
| 2799 | else => return self.fail("TODO: setValue {s}", .{@tagName(dst_val)}), | 3071 | .none, |
| | 3072 | .unreach, |
| | 3073 | .dead, |
| | 3074 | .undef, |
| | 3075 | => unreachable, |
| | 3076 | .immediate, |
| | 3077 | .register, |
| | 3078 | .register_offset, |
| | 3079 | => src_mcv.offset(-dst_reg_off.off), |
| | 3080 | else => .{ .register_offset = .{ |
| | 3081 | .reg = try self.copyToTmpRegister(ty, src_mcv), |
| | 3082 | .off = -dst_reg_off.off, |
| | 3083 | } }, |
| | 3084 | }), |
| | 3085 | .stack_offset => |off| return self.genSetStack(ty, off, src_mcv), |
| | 3086 | .memory => |addr| return self.genSetMem(ty, addr, src_mcv), |
| | 3087 | else => return self.fail("TODO: genCopy {s} with {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }), |
| 2800 | } | 3088 | } |
| 2801 | } | 3089 | } |
| 2802 | | 3090 | |
| 2803 | /// Sets the value of `src_val` into stack memory at `stack_offset`. | 3091 | /// Sets the value of `src_mcv` into stack memory at `stack_offset`. |
| 2804 | fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) InnerError!void { | 3092 | fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) InnerError!void { |
| 2805 | const mod = self.bin_file.comp.module.?; | 3093 | const mod = self.bin_file.comp.module.?; |
| 2806 | const abi_size: u32 = @intCast(ty.abiSize(mod)); | 3094 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 2807 | | 3095 | |
| 2808 | switch (src_val) { | 3096 | switch (src_mcv) { |
| 2809 | .none => return, | 3097 | .none => return, |
| 2810 | .dead => unreachable, | 3098 | .dead => unreachable, |
| 2811 | .undef => { | 3099 | .undef => { |
| ... | @@ -2820,7 +3108,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner | ... | @@ -2820,7 +3108,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2820 | const reg, const reg_lock = try self.allocReg(); | 3108 | const reg, const reg_lock = try self.allocReg(); |
| 2821 | defer self.register_manager.unlockReg(reg_lock); | 3109 | defer self.register_manager.unlockReg(reg_lock); |
| 2822 | | 3110 | |
| 2823 | try self.genSetReg(ty, reg, src_val); | 3111 | try self.genSetReg(ty, reg, src_mcv); |
| 2824 | | 3112 | |
| 2825 | return self.genSetStack(ty, stack_offset, .{ .register = reg }); | 3113 | return self.genSetStack(ty, stack_offset, .{ .register = reg }); |
| 2826 | }, | 3114 | }, |
| ... | @@ -2839,24 +3127,24 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner | ... | @@ -2839,24 +3127,24 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2839 | .tag = tag, | 3127 | .tag = tag, |
| 2840 | .data = .{ .i_type = .{ | 3128 | .data = .{ .i_type = .{ |
| 2841 | .rd = reg, | 3129 | .rd = reg, |
| 2842 | .rs1 = .s0, | 3130 | .rs1 = .sp, |
| 2843 | .imm12 = math.cast(i12, stack_offset) orelse { | 3131 | .imm12 = math.cast(i12, stack_offset) orelse { |
| 2844 | return self.fail("TODO: genSetStack bigger stack values", .{}); | 3132 | return self.fail("TODO: genSetStack bigger stack values", .{}); |
| 2845 | }, | 3133 | }, |
| 2846 | } }, | 3134 | } }, |
| 2847 | }); | 3135 | }); |
| 2848 | }, | 3136 | }, |
| 2849 | else => return self.fail("TODO: genSetStack for size={d}", .{abi_size}), | 3137 | else => unreachable, // register can hold a max of 8 bytes |
| 2850 | } | 3138 | } |
| 2851 | }, | 3139 | }, |
| 2852 | .stack_offset, .load_symbol => { | 3140 | .stack_offset, .load_symbol => { |
| 2853 | switch (src_val) { | 3141 | switch (src_mcv) { |
| 2854 | .stack_offset => |off| if (off == stack_offset) return, | 3142 | .stack_offset => |off| if (off == stack_offset) return, |
| 2855 | else => {}, | 3143 | else => {}, |
| 2856 | } | 3144 | } |
| 2857 | | 3145 | |
| 2858 | if (abi_size <= 8) { | 3146 | if (abi_size <= 8) { |
| 2859 | const reg = try self.copyToTmpRegister(ty, src_val); | 3147 | const reg = try self.copyToTmpRegister(ty, src_mcv); |
| 2860 | return self.genSetStack(ty, stack_offset, .{ .register = reg }); | 3148 | return self.genSetStack(ty, stack_offset, .{ .register = reg }); |
| 2861 | } | 3149 | } |
| 2862 | | 3150 | |
| ... | @@ -2875,7 +3163,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner | ... | @@ -2875,7 +3163,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2875 | const count_reg = regs[3]; | 3163 | const count_reg = regs[3]; |
| 2876 | const tmp_reg = regs[4]; | 3164 | const tmp_reg = regs[4]; |
| 2877 | | 3165 | |
| 2878 | switch (src_val) { | 3166 | switch (src_mcv) { |
| 2879 | .stack_offset => |offset| { | 3167 | .stack_offset => |offset| { |
| 2880 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset }); | 3168 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset }); |
| 2881 | }, | 3169 | }, |
| ... | @@ -2894,14 +3182,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner | ... | @@ -2894,14 +3182,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2894 | .tag = .load_symbol, | 3182 | .tag = .load_symbol, |
| 2895 | .data = .{ | 3183 | .data = .{ |
| 2896 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ | 3184 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ |
| 2897 | .register = @intFromEnum(src_reg), | 3185 | .register = src_reg.id(), |
| 2898 | .atom_index = atom_index, | 3186 | .atom_index = atom_index, |
| 2899 | .sym_index = sym_off.sym, | 3187 | .sym_index = sym_off.sym, |
| 2900 | }), | 3188 | }), |
| 2901 | }, | 3189 | }, |
| 2902 | }); | 3190 | }); |
| 2903 | }, | 3191 | }, |
| 2904 | else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_val)}), | 3192 | else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_mcv)}), |
| 2905 | } | 3193 | } |
| 2906 | | 3194 | |
| 2907 | try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); | 3195 | try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); |
| ... | @@ -2910,16 +3198,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner | ... | @@ -2910,16 +3198,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2910 | // memcpy(src, dst, len) | 3198 | // memcpy(src, dst, len) |
| 2911 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); | 3199 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); |
| 2912 | }, | 3200 | }, |
| 2913 | else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)}), | 3201 | .air_ref => |ref| try self.genSetStack(ty, stack_offset, try self.resolveInst(ref)), |
| | 3202 | else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_mcv)}), |
| 2914 | } | 3203 | } |
| 2915 | } | 3204 | } |
| 2916 | | 3205 | |
| 2917 | fn genSetMem(self: *Self, ty: Type, addr: u64, src_val: MCValue) InnerError!void { | 3206 | fn genSetMem(self: *Self, ty: Type, addr: u64, src_mcv: MCValue) InnerError!void { |
| 2918 | const mod = self.bin_file.comp.module.?; | 3207 | const mod = self.bin_file.comp.module.?; |
| 2919 | const abi_size: u32 = @intCast(ty.abiSize(mod)); | 3208 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 2920 | _ = abi_size; | 3209 | _ = abi_size; |
| 2921 | _ = addr; | 3210 | _ = addr; |
| 2922 | _ = src_val; | 3211 | _ = src_mcv; |
| 2923 | | 3212 | |
| 2924 | return self.fail("TODO: genSetMem", .{}); | 3213 | return self.fail("TODO: genSetMem", .{}); |
| 2925 | } | 3214 | } |
| ... | @@ -2932,51 +3221,101 @@ fn genInlineMemcpy( | ... | @@ -2932,51 +3221,101 @@ fn genInlineMemcpy( |
| 2932 | count: Register, | 3221 | count: Register, |
| 2933 | tmp: Register, | 3222 | tmp: Register, |
| 2934 | ) !void { | 3223 | ) !void { |
| 2935 | _ = src; | 3224 | try self.genSetReg(Type.usize, count, .{ .register = len }); |
| 2936 | _ = dst; | 3225 | |
| | 3226 | // lb tmp, 0(src) |
| | 3227 | const first_inst = try self.addInst(.{ |
| | 3228 | .tag = .lb, |
| | 3229 | .data = .{ |
| | 3230 | .i_type = .{ |
| | 3231 | .rd = tmp, |
| | 3232 | .rs1 = src, |
| | 3233 | .imm12 = 0, |
| | 3234 | }, |
| | 3235 | }, |
| | 3236 | }); |
| 2937 | | 3237 | |
| 2938 | // store 0 in the count | 3238 | // sb tmp, 0(dst) |
| 2939 | try self.genSetReg(Type.usize, count, .{ .immediate = 0 }); | 3239 | _ = try self.addInst(.{ |
| | 3240 | .tag = .sb, |
| | 3241 | .data = .{ |
| | 3242 | .i_type = .{ |
| | 3243 | .rd = tmp, |
| | 3244 | .rs1 = dst, |
| | 3245 | .imm12 = 0, |
| | 3246 | }, |
| | 3247 | }, |
| | 3248 | }); |
| 2940 | | 3249 | |
| 2941 | // compare count to length | 3250 | // dec count by 1 |
| 2942 | const compare_inst = try self.addInst(.{ | 3251 | _ = try self.addInst(.{ |
| 2943 | .tag = .cmp_eq, | 3252 | .tag = .addi, |
| 2944 | .data = .{ .r_type = .{ | 3253 | .data = .{ |
| 2945 | .rd = tmp, | 3254 | .i_type = .{ |
| 2946 | .rs1 = count, | 3255 | .rd = count, |
| 2947 | .rs2 = len, | 3256 | .rs1 = count, |
| 2948 | } }, | 3257 | .imm12 = -1, |
| | 3258 | }, |
| | 3259 | }, |
| 2949 | }); | 3260 | }); |
| 2950 | | 3261 | |
| 2951 | // end if true | 3262 | // branch if count is 0 |
| 2952 | _ = try self.addInst(.{ | 3263 | _ = try self.addInst(.{ |
| 2953 | .tag = .bne, | 3264 | .tag = .beq, |
| 2954 | .data = .{ | 3265 | .data = .{ |
| 2955 | .b_type = .{ | 3266 | .b_type = .{ |
| 2956 | .inst = @intCast(self.mir_instructions.len + 0), // points after the last inst | 3267 | .inst = @intCast(self.mir_instructions.len + 4), // points after the last inst |
| 2957 | .rs1 = .zero, | 3268 | .rs1 = count, |
| 2958 | .rs2 = tmp, | 3269 | .rs2 = .zero, |
| | 3270 | }, |
| | 3271 | }, |
| | 3272 | }); |
| | 3273 | |
| | 3274 | // increment the pointers |
| | 3275 | _ = try self.addInst(.{ |
| | 3276 | .tag = .addi, |
| | 3277 | .data = .{ |
| | 3278 | .i_type = .{ |
| | 3279 | .rd = src, |
| | 3280 | .rs1 = src, |
| | 3281 | .imm12 = 1, |
| | 3282 | }, |
| | 3283 | }, |
| | 3284 | }); |
| | 3285 | |
| | 3286 | _ = try self.addInst(.{ |
| | 3287 | .tag = .addi, |
| | 3288 | .data = .{ |
| | 3289 | .i_type = .{ |
| | 3290 | .rd = dst, |
| | 3291 | .rs1 = dst, |
| | 3292 | .imm12 = 1, |
| 2959 | }, | 3293 | }, |
| 2960 | }, | 3294 | }, |
| 2961 | }); | 3295 | }); |
| 2962 | _ = compare_inst; | | |
| 2963 | | 3296 | |
| 2964 | return self.fail("TODO: finish genInlineMemcpy", .{}); | 3297 | // jump back to start of loop |
| | 3298 | _ = try self.addInst(.{ |
| | 3299 | .tag = .j, |
| | 3300 | .data = .{ |
| | 3301 | .inst = first_inst, |
| | 3302 | }, |
| | 3303 | }); |
| 2965 | } | 3304 | } |
| 2966 | | 3305 | |
| 2967 | /// Sets the value of `src_val` into `reg`. Assumes you have a lock on it. | 3306 | /// Sets the value of `src_mcv` into `reg`. Assumes you have a lock on it. |
| 2968 | fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!void { | 3307 | fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!void { |
| 2969 | const mod = self.bin_file.comp.module.?; | 3308 | const mod = self.bin_file.comp.module.?; |
| 2970 | const abi_size: u32 = @intCast(ty.abiSize(mod)); | 3309 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 2971 | | 3310 | |
| 2972 | switch (src_val) { | 3311 | switch (src_mcv) { |
| 2973 | .dead => unreachable, | 3312 | .dead => unreachable, |
| 2974 | .ptr_stack_offset => |off| { | 3313 | .ptr_stack_offset => |off| { |
| 2975 | _ = try self.addInst(.{ | 3314 | _ = try self.addInst(.{ |
| 2976 | .tag = .addi, | 3315 | .tag = .addi, |
| 2977 | .data = .{ .i_type = .{ | 3316 | .data = .{ .i_type = .{ |
| 2978 | .rd = reg, | 3317 | .rd = reg, |
| 2979 | .rs1 = .s0, | 3318 | .rs1 = .sp, |
| 2980 | .imm12 = math.cast(i12, off) orelse { | 3319 | .imm12 = math.cast(i12, off) orelse { |
| 2981 | return self.fail("TODO: bigger stack sizes", .{}); | 3320 | return self.fail("TODO: bigger stack sizes", .{}); |
| 2982 | }, | 3321 | }, |
| ... | @@ -3006,7 +3345,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! | ... | @@ -3006,7 +3345,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! |
| 3006 | const carry: i32 = if (lo12 < 0) 1 else 0; | 3345 | const carry: i32 = if (lo12 < 0) 1 else 0; |
| 3007 | const hi20: i20 = @truncate((x >> 12) +% carry); | 3346 | const hi20: i20 = @truncate((x >> 12) +% carry); |
| 3008 | | 3347 | |
| 3009 | // TODO: add test case for 32-bit immediate | | |
| 3010 | _ = try self.addInst(.{ | 3348 | _ = try self.addInst(.{ |
| 3011 | .tag = .lui, | 3349 | .tag = .lui, |
| 3012 | .data = .{ .u_type = .{ | 3350 | .data = .{ .u_type = .{ |
| ... | @@ -3069,6 +3407,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! | ... | @@ -3069,6 +3407,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! |
| 3069 | } }, | 3407 | } }, |
| 3070 | }); | 3408 | }); |
| 3071 | }, | 3409 | }, |
| | 3410 | .register_pair => |pair| try self.genSetReg(ty, reg, .{ .register = pair[0] }), |
| 3072 | .memory => |addr| { | 3411 | .memory => |addr| { |
| 3073 | try self.genSetReg(ty, reg, .{ .immediate = addr }); | 3412 | try self.genSetReg(ty, reg, .{ .immediate = addr }); |
| 3074 | | 3413 | |
| ... | @@ -3080,8 +3419,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! | ... | @@ -3080,8 +3419,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! |
| 3080 | .imm12 = 0, | 3419 | .imm12 = 0, |
| 3081 | } }, | 3420 | } }, |
| 3082 | }); | 3421 | }); |
| 3083 | | | |
| 3084 | // LOAD imm=[i12 offset = 0], rs1 | | |
| 3085 | }, | 3422 | }, |
| 3086 | .stack_offset => |off| { | 3423 | .stack_offset => |off| { |
| 3087 | const tag: Mir.Inst.Tag = switch (abi_size) { | 3424 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| ... | @@ -3096,7 +3433,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! | ... | @@ -3096,7 +3433,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! |
| 3096 | .tag = tag, | 3433 | .tag = tag, |
| 3097 | .data = .{ .i_type = .{ | 3434 | .data = .{ .i_type = .{ |
| 3098 | .rd = reg, | 3435 | .rd = reg, |
| 3099 | .rs1 = .s0, | 3436 | .rs1 = .sp, |
| 3100 | .imm12 = math.cast(i12, off) orelse { | 3437 | .imm12 = math.cast(i12, off) orelse { |
| 3101 | return self.fail("TODO: genSetReg support larger stack sizes", .{}); | 3438 | return self.fail("TODO: genSetReg support larger stack sizes", .{}); |
| 3102 | }, | 3439 | }, |
| ... | @@ -3120,13 +3457,55 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! | ... | @@ -3120,13 +3457,55 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! |
| 3120 | .tag = .load_symbol, | 3457 | .tag = .load_symbol, |
| 3121 | .data = .{ | 3458 | .data = .{ |
| 3122 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ | 3459 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ |
| 3123 | .register = @intFromEnum(reg), | 3460 | .register = reg.id(), |
| 3124 | .atom_index = atom_index, | 3461 | .atom_index = atom_index, |
| 3125 | .sym_index = sym_off.sym, | 3462 | .sym_index = sym_off.sym, |
| 3126 | }), | 3463 | }), |
| 3127 | }, | 3464 | }, |
| 3128 | }); | 3465 | }); |
| 3129 | }, | 3466 | }, |
| | 3467 | .air_ref => |ref| try self.genSetReg(ty, reg, try self.resolveInst(ref)), |
| | 3468 | .indirect => |reg_off| { |
| | 3469 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| | 3470 | 1 => .lb, |
| | 3471 | 2 => .lh, |
| | 3472 | 4 => .lw, |
| | 3473 | 8 => .ld, |
| | 3474 | else => return self.fail("TODO: genSetReg for size {d}", .{abi_size}), |
| | 3475 | }; |
| | 3476 | |
| | 3477 | _ = try self.addInst(.{ |
| | 3478 | .tag = tag, |
| | 3479 | .data = .{ |
| | 3480 | .i_type = .{ |
| | 3481 | .rd = reg, |
| | 3482 | .rs1 = reg_off.reg, |
| | 3483 | .imm12 = @intCast(reg_off.off), |
| | 3484 | }, |
| | 3485 | }, |
| | 3486 | }); |
| | 3487 | }, |
| | 3488 | else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}), |
| | 3489 | } |
| | 3490 | } |
| | 3491 | |
| | 3492 | fn genSetRegPair(self: *Self, ty: Type, pair: [2]Register, src_mcv: MCValue) InnerError!void { |
| | 3493 | const mod = self.bin_file.comp.module.?; |
| | 3494 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| | 3495 | |
| | 3496 | assert(abi_size > 8 and abi_size <= 16); // must fit only fit into two registers |
| | 3497 | |
| | 3498 | switch (src_mcv) { |
| | 3499 | .air_ref => |ref| return self.genSetRegPair(ty, pair, try self.resolveInst(ref)), |
| | 3500 | .load_symbol => |sym_off| { |
| | 3501 | _ = sym_off; |
| | 3502 | // return self.fail("TODO: genSetRegPair load_symbol", .{}); |
| | 3503 | // commented out just for testing. |
| | 3504 | |
| | 3505 | // plan here is to load the address into a temporary register and |
| | 3506 | // copy into the pair. |
| | 3507 | }, |
| | 3508 | else => return self.fail("TODO: genSetRegPair {s}", .{@tagName(src_mcv)}), |
| 3130 | } | 3509 | } |
| 3131 | } | 3510 | } |
| 3132 | | 3511 | |
| ... | @@ -3138,7 +3517,7 @@ fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3138,7 +3517,7 @@ fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3138 | | 3517 | |
| 3139 | const dst_mcv = try self.allocRegOrMem(inst, true); | 3518 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 3140 | const dst_ty = self.typeOfIndex(inst); | 3519 | const dst_ty = self.typeOfIndex(inst); |
| 3141 | try self.setValue(dst_ty, dst_mcv, src_mcv); | 3520 | try self.genCopy(dst_ty, dst_mcv, src_mcv); |
| 3142 | break :result dst_mcv; | 3521 | break :result dst_mcv; |
| 3143 | }; | 3522 | }; |
| 3144 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3523 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| ... | @@ -3157,7 +3536,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3157,7 +3536,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 3157 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); | 3536 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| 3158 | | 3537 | |
| 3159 | const dest = try self.allocRegOrMem(inst, true); | 3538 | const dest = try self.allocRegOrMem(inst, true); |
| 3160 | try self.setValue(self.typeOfIndex(inst), dest, operand); | 3539 | try self.genCopy(self.typeOfIndex(inst), dest, operand); |
| 3161 | break :result dest; | 3540 | break :result dest; |
| 3162 | }; | 3541 | }; |
| 3163 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 3542 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | @@ -3385,8 +3764,12 @@ const CallMCValues = struct { | ... | @@ -3385,8 +3764,12 @@ const CallMCValues = struct { |
| 3385 | }; | 3764 | }; |
| 3386 | | 3765 | |
| 3387 | /// Caller must call `CallMCValues.deinit`. | 3766 | /// Caller must call `CallMCValues.deinit`. |
| 3388 | fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | 3767 | fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: CallView) !CallMCValues { |
| 3389 | const mod = self.bin_file.comp.module.?; | 3768 | const mod = self.bin_file.comp.module.?; |
| | 3769 | const ip = &mod.intern_pool; |
| | 3770 | |
| | 3771 | _ = role; |
| | 3772 | |
| 3390 | const fn_info = mod.typeToFunc(fn_ty).?; | 3773 | const fn_info = mod.typeToFunc(fn_ty).?; |
| 3391 | const cc = fn_info.cc; | 3774 | const cc = fn_info.cc; |
| 3392 | var result: CallMCValues = .{ | 3775 | var result: CallMCValues = .{ |
| ... | @@ -3413,26 +3796,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -3413,26 +3796,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 3413 | return self.fail("TODO: support more than 8 function args", .{}); | 3796 | return self.fail("TODO: support more than 8 function args", .{}); |
| 3414 | } | 3797 | } |
| 3415 | | 3798 | |
| 3416 | const locks = try self.gpa.alloc(RegisterLock, result.args.len); | 3799 | var fa_reg_i: u32 = 0; |
| 3417 | defer self.gpa.free(locks); | | |
| 3418 | | 3800 | |
| 3419 | for (0..result.args.len) |i| { | 3801 | // spill the needed argument registers |
| 3420 | const arg_reg = try self.register_manager.allocReg(null, fa); | 3802 | for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| { |
| 3421 | const lock = self.register_manager.lockRegAssumeUnused(arg_reg); | 3803 | const param_ty = Type.fromInterned(ty); |
| 3422 | locks[i] = lock; | 3804 | const param_size = param_ty.abiSize(mod); |
| 3423 | result.args[i] = .{ .register = arg_reg }; | | |
| 3424 | } | | |
| 3425 | | 3805 | |
| 3426 | // we can just free the locks now, as this should be the only place where the fa | 3806 | switch (param_size) { |
| 3427 | // arg set is used. | 3807 | 1...8 => { |
| 3428 | for (locks) |lock| { | 3808 | const arg_reg: Register = abi.function_arg_regs[fa_reg_i]; |
| 3429 | self.register_manager.unlockReg(lock); | 3809 | fa_reg_i += 1; |
| | 3810 | try self.register_manager.getReg(arg_reg, null); |
| | 3811 | result_arg.* = .{ .register = arg_reg }; |
| | 3812 | }, |
| | 3813 | 9...16 => { |
| | 3814 | const arg_regs: [2]Register = abi.function_arg_regs[fa_reg_i..][0..2].*; |
| | 3815 | fa_reg_i += 2; |
| | 3816 | for (arg_regs) |reg| try self.register_manager.getReg(reg, null); |
| | 3817 | result_arg.* = .{ .register_pair = arg_regs }; |
| | 3818 | }, |
| | 3819 | else => return self.fail("TODO: support args of size {}", .{param_size}), |
| | 3820 | } |
| 3430 | } | 3821 | } |
| 3431 | | 3822 | |
| 3432 | // stack_offset = num s registers spilled + local var space | 3823 | result.stack_byte_count = self.max_end_stack; |
| 3433 | // TODO: spill used s registers here | | |
| 3434 | | | |
| 3435 | result.stack_byte_count = 0; | | |
| 3436 | result.stack_align = .@"16"; | 3824 | result.stack_align = .@"16"; |
| 3437 | }, | 3825 | }, |
| 3438 | else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}), | 3826 | else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}), |