| ... | ... | @@ -38,9 +38,16 @@ const callee_preserved_regs = abi.callee_preserved_regs; |
| 38 | 38 | const gp = abi.RegisterClass.gp; |
| 39 | 39 | /// Function Args |
| 40 | 40 | const fa = abi.RegisterClass.fa; |
| 41 | /// Temporary Use |
| 42 | const tp = abi.RegisterClass.tp; |
| 41 | 43 | |
| 42 | 44 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 43 | 45 | |
| 46 | const RegisterView = enum(u1) { |
| 47 | caller, |
| 48 | callee, |
| 49 | }; |
| 50 | |
| 44 | 51 | gpa: Allocator, |
| 45 | 52 | air: Air, |
| 46 | 53 | liveness: Liveness, |
| ... | ... | @@ -82,8 +89,8 @@ branch_stack: *std.ArrayList(Branch), |
| 82 | 89 | |
| 83 | 90 | // Key is the block instruction |
| 84 | 91 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 85 | | |
| 86 | 92 | register_manager: RegisterManager = .{}, |
| 93 | |
| 87 | 94 | /// Maps offset to what is stored there. |
| 88 | 95 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 89 | 96 | |
| ... | ... | @@ -99,6 +106,7 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 99 | 106 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 100 | 107 | |
| 101 | 108 | const SymbolOffset = struct { sym: u32, off: i32 = 0 }; |
| 109 | const RegisterOffset = struct { reg: Register, off: i32 = 0 }; |
| 102 | 110 | |
| 103 | 111 | const MCValue = union(enum) { |
| 104 | 112 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. |
| ... | ... | @@ -119,6 +127,8 @@ const MCValue = union(enum) { |
| 119 | 127 | load_symbol: SymbolOffset, |
| 120 | 128 | /// The value is in a target-specific register. |
| 121 | 129 | register: Register, |
| 130 | /// The value is split across two registers |
| 131 | register_pair: [2]Register, |
| 122 | 132 | /// The value is in memory at a hard-coded address. |
| 123 | 133 | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 124 | 134 | memory: u64, |
| ... | ... | @@ -127,10 +137,15 @@ const MCValue = union(enum) { |
| 127 | 137 | stack_offset: u32, |
| 128 | 138 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 129 | 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 | 146 | fn isMemory(mcv: MCValue) bool { |
| 132 | 147 | return switch (mcv) { |
| 133 | | .memory, .stack_offset => true, |
| 148 | .memory, .indirect, .load_frame => true, |
| 134 | 149 | else => false, |
| 135 | 150 | }; |
| 136 | 151 | } |
| ... | ... | @@ -151,15 +166,85 @@ const MCValue = union(enum) { |
| 151 | 166 | .immediate, |
| 152 | 167 | .memory, |
| 153 | 168 | .ptr_stack_offset, |
| 169 | .indirect, |
| 154 | 170 | .undef, |
| 155 | 171 | .load_symbol, |
| 172 | .air_ref, |
| 156 | 173 | => false, |
| 157 | 174 | |
| 158 | 175 | .register, |
| 176 | .register_pair, |
| 177 | .register_offset, |
| 159 | 178 | .stack_offset, |
| 160 | 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 | 250 | const Branch = struct { |
| ... | ... | @@ -211,6 +296,11 @@ const BigTomb = struct { |
| 211 | 296 | |
| 212 | 297 | const Self = @This(); |
| 213 | 298 | |
| 299 | const CallView = enum(u1) { |
| 300 | callee, |
| 301 | caller, |
| 302 | }; |
| 303 | |
| 214 | 304 | pub fn generate( |
| 215 | 305 | lf: *link.File, |
| 216 | 306 | src_loc: Module.SrcLoc, |
| ... | ... | @@ -261,7 +351,7 @@ pub fn generate( |
| 261 | 351 | defer function.blocks.deinit(gpa); |
| 262 | 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 | 355 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 266 | 356 | error.OutOfRegisters => return Result{ |
| 267 | 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 | 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 | 385 | var mir = Mir{ |
| 288 | 386 | .instructions = function.mir_instructions.toOwnedSlice(), |
| 289 | 387 | .extra = try function.mir_extra.toOwnedSlice(gpa), |
| ... | ... | @@ -300,8 +398,10 @@ pub fn generate( |
| 300 | 398 | .prev_di_pc = 0, |
| 301 | 399 | .prev_di_line = func.lbrace_line, |
| 302 | 400 | .prev_di_column = func.lbrace_column, |
| 303 | | .stack_size = @max(32, function.max_end_stack), |
| 304 | 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 | 406 | defer emit.deinit(); |
| 307 | 407 | |
| ... | ... | @@ -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 | 736 | /// Asserts there is already capacity to insert into top branch inst_table. |
| 633 | 737 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 634 | 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 | 743 | .register => |reg| { |
| 640 | 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 | 754 | } |
| 651 | 755 | } |
| 652 | 756 | |
| 653 | | fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { |
| 654 | | var tomb_bits = self.liveness.getTombBits(inst); |
| 655 | | for (operands) |op| { |
| 656 | | const dies = @as(u1, @truncate(tomb_bits)) != 0; |
| 657 | | tomb_bits >>= 1; |
| 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) { |
| 757 | fn finishAirResult(self: *Self, inst: Air.Inst.Index, result: MCValue) void { |
| 758 | if (self.liveness.isUnused(inst)) switch (result) { |
| 759 | .none, .dead, .unreach => {}, |
| 760 | else => unreachable, // Why didn't the result die? |
| 761 | } else { |
| 664 | 762 | log.debug("%{d} => {}", .{ inst, result }); |
| 665 | 763 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 666 | 764 | branch.inst_table.putAssumeCapacityNoClobber(inst, result); |
| ... | ... | @@ -682,6 +780,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 682 | 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 | 799 | fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { |
| 686 | 800 | const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; |
| 687 | 801 | try table.ensureUnusedCapacity(self.gpa, additional_count); |
| ... | ... | @@ -716,6 +830,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 716 | 830 | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 717 | 831 | const mod = self.bin_file.comp.module.?; |
| 718 | 832 | const elem_ty = self.typeOfIndex(inst); |
| 833 | |
| 719 | 834 | const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { |
| 720 | 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 | 843 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 729 | 844 | if (abi_size <= ptr_bytes) { |
| 730 | 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 | 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 | 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 | 861 | } |
| 747 | 862 | |
| 748 | 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 | 870 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 750 | 871 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 751 | 872 | const reg_mcv = self.getResolvedInstValue(inst); |
| ... | ... | @@ -759,7 +880,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 759 | 880 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 760 | 881 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 761 | 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 | 884 | try self.genSetReg(ty, reg, mcv); |
| 764 | 885 | return reg; |
| 765 | 886 | } |
| ... | ... | @@ -830,7 +951,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 830 | 951 | math.divCeil(u32, src_storage_bits, 64) catch unreachable and |
| 831 | 952 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { |
| 832 | 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 | 955 | break :dst dst_mcv; |
| 835 | 956 | }; |
| 836 | 957 | |
| ... | ... | @@ -1261,43 +1382,48 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1261 | 1382 | |
| 1262 | 1383 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { |
| 1263 | 1384 | if (int_info.signedness == .unsigned) { |
| 1264 | | const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset; |
| 1265 | | |
| 1266 | | 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); |
| 1385 | switch (int_info.bits) { |
| 1386 | 1...8 => { |
| 1387 | const max_val = std.math.pow(u16, 2, int_info.bits) - 1; |
| 1270 | 1388 | |
| 1271 | | const add_reg, const add_lock = blk: { |
| 1272 | | if (add_result_mcv == .register) break :blk .{ add_result_mcv.register, null }; |
| 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 | | }); |
| 1389 | const overflow_reg, const overflow_lock = try self.allocReg(); |
| 1390 | defer self.register_manager.unlockReg(overflow_lock); |
| 1288 | 1391 | |
| 1289 | | const overflow_mcv = try self.binOp( |
| 1290 | | .cmp_neq, |
| 1291 | | null, |
| 1292 | | .{ .register = overflow_reg }, |
| 1293 | | .{ .register = add_reg }, |
| 1294 | | lhs_ty, |
| 1295 | | lhs_ty, |
| 1296 | | ); |
| 1392 | const add_reg, const add_lock = blk: { |
| 1393 | if (add_result_mcv == .register) break :blk .{ add_result_mcv.register, null }; |
| 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 | 1427 | } else { |
| 1302 | 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 | 1493 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1368 | 1494 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 1369 | 1495 | const rhs_ty = self.typeOf(bin_op.rhs); |
| 1496 | |
| 1370 | 1497 | break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1371 | 1498 | }; |
| 1372 | 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 | 1633 | |
| 1507 | 1634 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1508 | 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 | 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 | 1737 | |
| 1599 | 1738 | fn airCtz(self: *Self, inst: Air.Inst.Index) !void { |
| 1600 | 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 | 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 | 1794 | fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { |
| 1606 | 1795 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1607 | 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 | 1939 | const elem_ty = self.typeOfIndex(inst); |
| 1751 | 1940 | const result: MCValue = result: { |
| 1752 | 1941 | if (!elem_ty.hasRuntimeBits(mod)) |
| 1753 | | break :result MCValue.none; |
| 1942 | break :result .none; |
| 1754 | 1943 | |
| 1755 | 1944 | const ptr = try self.resolveInst(ty_op.operand); |
| 1756 | 1945 | const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod); |
| 1757 | 1946 | if (self.liveness.isUnused(inst) and !is_volatile) |
| 1758 | | break :result MCValue.dead; |
| 1947 | break :result .dead; |
| 1759 | 1948 | |
| 1760 | 1949 | const dst_mcv: MCValue = blk: { |
| 1761 | 1950 | if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { |
| ... | ... | @@ -1771,27 +1960,38 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 1771 | 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 | 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) { |
| 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 }), |
| 1967 | log.debug("loading {}:{} into {}", .{ ptr_mcv, ptr_ty.fmt(mod), dst_mcv }); |
| 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 | 1978 | .register, |
| 1979 | .register_offset, |
| 1980 | .ptr_stack_offset, |
| 1981 | => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()), |
| 1982 | |
| 1788 | 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 => { |
| 1792 | | const reg = try self.copyToTmpRegister(ptr_ty, src_ptr); |
| 1793 | | try self.load(dst_mcv, .{ .register = reg }, ptr_ty); |
| 1992 | try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } }); |
| 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 | 2017 | const mod = self.bin_file.comp.module.?; |
| 1818 | 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 | 2027 | switch (pointer) { |
| 1823 | 2028 | .none => unreachable, |
| ... | ... | @@ -1976,7 +2181,6 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1976 | 2181 | } |
| 1977 | 2182 | |
| 1978 | 2183 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1979 | | const mod = self.bin_file.comp.module.?; |
| 1980 | 2184 | var arg_index = self.arg_index; |
| 1981 | 2185 | |
| 1982 | 2186 | // we skip over args that have no bits |
| ... | ... | @@ -1986,21 +2190,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1986 | 2190 | const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { |
| 1987 | 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 | 2193 | const dst_mcv = switch (src_mcv) { |
| 1993 | 2194 | .register => |src_reg| dst: { |
| 1994 | | // TODO: get the true type of the arg, and fit the spill to size. |
| 1995 | | const arg_size = Type.usize.abiSize(mod); |
| 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 }; |
| 2195 | try self.register_manager.getReg(src_reg, null); |
| 2196 | break :dst src_mcv; |
| 2004 | 2197 | }, |
| 2005 | 2198 | else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}), |
| 2006 | 2199 | }; |
| ... | ... | @@ -2044,87 +2237,122 @@ fn airFence(self: *Self) !void { |
| 2044 | 2237 | } |
| 2045 | 2238 | |
| 2046 | 2239 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| 2047 | | const mod = self.bin_file.comp.module.?; |
| 2048 | 2240 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{}); |
| 2049 | 2241 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 2050 | | const fn_ty = self.typeOf(pl_op.operand); |
| 2051 | 2242 | const callee = pl_op.operand; |
| 2052 | 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); |
| 2056 | | defer info.deinit(self); |
| 2304 | var call_info = try self.resolveCallingConventionValues(fn_ty, .caller); |
| 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 | 2309 | // Due to incremental compilation, how function calls are generated depends |
| 2059 | 2310 | // on linking. |
| 2060 | | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 2061 | | for (info.args, 0..) |mc_arg, arg_i| { |
| 2062 | | const arg = args[arg_i]; |
| 2063 | | const arg_ty = self.typeOf(arg); |
| 2064 | | const arg_mcv = try self.resolveInst(args[arg_i]); |
| 2065 | | try self.setValue(arg_ty, mc_arg, arg_mcv); |
| 2066 | | } |
| 2067 | | |
| 2068 | | if (try self.air.value(callee, mod)) |func_value| { |
| 2069 | | switch (mod.intern_pool.indexToKey(func_value.ip_index)) { |
| 2311 | switch (info) { |
| 2312 | .air => |callee| if (try self.air.value(callee, mod)) |func_value| { |
| 2313 | const func_key = mod.intern_pool.indexToKey(func_value.ip_index); |
| 2314 | switch (switch (func_key) { |
| 2315 | else => func_key, |
| 2316 | .ptr => |ptr| switch (ptr.addr) { |
| 2317 | .decl => |decl| mod.intern_pool.indexToKey(mod.declPtr(decl).val.toIntern()), |
| 2318 | else => func_key, |
| 2319 | }, |
| 2320 | }) { |
| 2070 | 2321 | .func => |func| { |
| 2071 | | const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); |
| 2072 | | const sym = elf_file.symbol(sym_index); |
| 2073 | | _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); |
| 2074 | | const got_addr = sym.zigGotAddress(elf_file); |
| 2075 | | try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); |
| 2076 | | _ = try self.addInst(.{ |
| 2077 | | .tag = .jalr, |
| 2078 | | .data = .{ .i_type = .{ |
| 2079 | | .rd = .ra, |
| 2080 | | .rs1 = .ra, |
| 2081 | | .imm12 = 0, |
| 2082 | | } }, |
| 2083 | | }); |
| 2322 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 2323 | const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); |
| 2324 | const sym = elf_file.symbol(sym_index); |
| 2325 | _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); |
| 2326 | const got_addr = sym.zigGotAddress(elf_file); |
| 2327 | try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); |
| 2328 | _ = try self.addInst(.{ |
| 2329 | .tag = .jalr, |
| 2330 | .data = .{ .i_type = .{ |
| 2331 | .rd = .ra, |
| 2332 | .rs1 = .ra, |
| 2333 | .imm12 = 0, |
| 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 | 2344 | .extern_func => { |
| 2086 | | return self.fail("TODO implement calling extern functions", .{}); |
| 2087 | | }, |
| 2088 | | else => { |
| 2089 | | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 2345 | return self.fail("TODO: extern func calls", .{}); |
| 2090 | 2346 | }, |
| 2347 | else => return self.fail("TODO implement calling bitcasted functions", .{}), |
| 2091 | 2348 | } |
| 2092 | 2349 | } else { |
| 2093 | | return self.fail("TODO implement calling runtime-known function pointer", .{}); |
| 2094 | | } |
| 2095 | | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 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); |
| 2350 | return self.fail("TODO: call function pointers", .{}); |
| 2351 | }, |
| 2352 | .lib => return self.fail("TODO: lib func calls", .{}), |
| 2126 | 2353 | } |
| 2127 | | return bt.finishAir(result); |
| 2354 | |
| 2355 | return call_info.return_value; |
| 2128 | 2356 | } |
| 2129 | 2357 | |
| 2130 | 2358 | fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| ... | ... | @@ -2151,7 +2379,7 @@ fn ret(self: *Self, mcv: MCValue) !void { |
| 2151 | 2379 | const mod = self.bin_file.comp.module.?; |
| 2152 | 2380 | |
| 2153 | 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 | 2384 | _ = try self.addInst(.{ |
| 2157 | 2385 | .tag = .psuedo_epilogue, |
| ... | ... | @@ -2183,6 +2411,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index) !void { |
| 2183 | 2411 | const ty = self.typeOf(bin_op.lhs); |
| 2184 | 2412 | const mod = self.bin_file.comp.module.?; |
| 2185 | 2413 | assert(ty.eql(self.typeOf(bin_op.rhs), mod)); |
| 2414 | |
| 2186 | 2415 | if (ty.zigTypeTag(mod) == .ErrorSet) |
| 2187 | 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 | 2462 | |
| 2234 | 2463 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| 2235 | 2464 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 2236 | | const name = self.air.nullTerminatedString(pl_op.payload); |
| 2237 | 2465 | const operand = pl_op.operand; |
| 2238 | | // TODO emit debug info for this variable |
| 2239 | | _ = name; |
| 2240 | | return self.finishAir(inst, .dead, .{ operand, .none, .none }); |
| 2466 | const ty = self.typeOf(operand); |
| 2467 | const mcv = try self.resolveInst(operand); |
| 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 | 2515 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2348,7 +2620,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2348 | 2620 | log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); |
| 2349 | 2621 | // TODO make sure the destination stack offset / register does not already have something |
| 2350 | 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 | 2624 | // TODO track the new register / stack allocation |
| 2353 | 2625 | } |
| 2354 | 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 | 2647 | log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); |
| 2376 | 2648 | // TODO make sure the destination stack offset / register does not already have something |
| 2377 | 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 | 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 | 2910 | if (block_mcv == .none) { |
| 2639 | 2911 | block_data.mcv = operand_mcv; |
| 2640 | 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 | 2916 | return self.brVoid(block); |
| ... | ... | @@ -2783,29 +3055,45 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT |
| 2783 | 3055 | } |
| 2784 | 3056 | |
| 2785 | 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 | 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 | 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) { |
| 2796 | | .register => |reg| return self.genSetReg(ty, reg, src_val), |
| 2797 | | .stack_offset => |off| return self.genSetStack(ty, off, src_val), |
| 2798 | | .memory => |addr| return self.genSetMem(ty, addr, src_val), |
| 2799 | | else => return self.fail("TODO: setValue {s}", .{@tagName(dst_val)}), |
| 3067 | switch (dst_mcv) { |
| 3068 | .register => |reg| return self.genSetReg(ty, reg, src_mcv), |
| 3069 | .register_pair => |pair| return self.genSetRegPair(ty, pair, src_mcv), |
| 3070 | .register_offset => |dst_reg_off| try self.genSetReg(ty, dst_reg_off.reg, switch (src_mcv) { |
| 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`. |
| 2804 | | fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) InnerError!void { |
| 3091 | /// Sets the value of `src_mcv` into stack memory at `stack_offset`. |
| 3092 | fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) InnerError!void { |
| 2805 | 3093 | const mod = self.bin_file.comp.module.?; |
| 2806 | 3094 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 2807 | 3095 | |
| 2808 | | switch (src_val) { |
| 3096 | switch (src_mcv) { |
| 2809 | 3097 | .none => return, |
| 2810 | 3098 | .dead => unreachable, |
| 2811 | 3099 | .undef => { |
| ... | ... | @@ -2820,7 +3108,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2820 | 3108 | const reg, const reg_lock = try self.allocReg(); |
| 2821 | 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 | 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 | 3127 | .tag = tag, |
| 2840 | 3128 | .data = .{ .i_type = .{ |
| 2841 | 3129 | .rd = reg, |
| 2842 | | .rs1 = .s0, |
| 3130 | .rs1 = .sp, |
| 2843 | 3131 | .imm12 = math.cast(i12, stack_offset) orelse { |
| 2844 | 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 | 3140 | .stack_offset, .load_symbol => { |
| 2853 | | switch (src_val) { |
| 3141 | switch (src_mcv) { |
| 2854 | 3142 | .stack_offset => |off| if (off == stack_offset) return, |
| 2855 | 3143 | else => {}, |
| 2856 | 3144 | } |
| 2857 | 3145 | |
| 2858 | 3146 | if (abi_size <= 8) { |
| 2859 | | const reg = try self.copyToTmpRegister(ty, src_val); |
| 3147 | const reg = try self.copyToTmpRegister(ty, src_mcv); |
| 2860 | 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 | 3163 | const count_reg = regs[3]; |
| 2876 | 3164 | const tmp_reg = regs[4]; |
| 2877 | 3165 | |
| 2878 | | switch (src_val) { |
| 3166 | switch (src_mcv) { |
| 2879 | 3167 | .stack_offset => |offset| { |
| 2880 | 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 | 3182 | .tag = .load_symbol, |
| 2895 | 3183 | .data = .{ |
| 2896 | 3184 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ |
| 2897 | | .register = @intFromEnum(src_reg), |
| 3185 | .register = src_reg.id(), |
| 2898 | 3186 | .atom_index = atom_index, |
| 2899 | 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 | 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 | 3198 | // memcpy(src, dst, len) |
| 2911 | 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 | 3207 | const mod = self.bin_file.comp.module.?; |
| 2919 | 3208 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 2920 | 3209 | _ = abi_size; |
| 2921 | 3210 | _ = addr; |
| 2922 | | _ = src_val; |
| 3211 | _ = src_mcv; |
| 2923 | 3212 | |
| 2924 | 3213 | return self.fail("TODO: genSetMem", .{}); |
| 2925 | 3214 | } |
| ... | ... | @@ -2932,51 +3221,101 @@ fn genInlineMemcpy( |
| 2932 | 3221 | count: Register, |
| 2933 | 3222 | tmp: Register, |
| 2934 | 3223 | ) !void { |
| 2935 | | _ = src; |
| 2936 | | _ = dst; |
| 3224 | try self.genSetReg(Type.usize, count, .{ .register = len }); |
| 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 |
| 2939 | | try self.genSetReg(Type.usize, count, .{ .immediate = 0 }); |
| 3238 | // sb tmp, 0(dst) |
| 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 |
| 2942 | | const compare_inst = try self.addInst(.{ |
| 2943 | | .tag = .cmp_eq, |
| 2944 | | .data = .{ .r_type = .{ |
| 2945 | | .rd = tmp, |
| 2946 | | .rs1 = count, |
| 2947 | | .rs2 = len, |
| 2948 | | } }, |
| 3250 | // dec count by 1 |
| 3251 | _ = try self.addInst(.{ |
| 3252 | .tag = .addi, |
| 3253 | .data = .{ |
| 3254 | .i_type = .{ |
| 3255 | .rd = count, |
| 3256 | .rs1 = count, |
| 3257 | .imm12 = -1, |
| 3258 | }, |
| 3259 | }, |
| 2949 | 3260 | }); |
| 2950 | 3261 | |
| 2951 | | // end if true |
| 3262 | // branch if count is 0 |
| 2952 | 3263 | _ = try self.addInst(.{ |
| 2953 | | .tag = .bne, |
| 3264 | .tag = .beq, |
| 2954 | 3265 | .data = .{ |
| 2955 | 3266 | .b_type = .{ |
| 2956 | | .inst = @intCast(self.mir_instructions.len + 0), // points after the last inst |
| 2957 | | .rs1 = .zero, |
| 2958 | | .rs2 = tmp, |
| 3267 | .inst = @intCast(self.mir_instructions.len + 4), // points after the last inst |
| 3268 | .rs1 = count, |
| 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. |
| 2968 | | fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!void { |
| 3306 | /// Sets the value of `src_mcv` into `reg`. Assumes you have a lock on it. |
| 3307 | fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!void { |
| 2969 | 3308 | const mod = self.bin_file.comp.module.?; |
| 2970 | 3309 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 2971 | 3310 | |
| 2972 | | switch (src_val) { |
| 3311 | switch (src_mcv) { |
| 2973 | 3312 | .dead => unreachable, |
| 2974 | 3313 | .ptr_stack_offset => |off| { |
| 2975 | 3314 | _ = try self.addInst(.{ |
| 2976 | 3315 | .tag = .addi, |
| 2977 | 3316 | .data = .{ .i_type = .{ |
| 2978 | 3317 | .rd = reg, |
| 2979 | | .rs1 = .s0, |
| 3318 | .rs1 = .sp, |
| 2980 | 3319 | .imm12 = math.cast(i12, off) orelse { |
| 2981 | 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 | 3345 | const carry: i32 = if (lo12 < 0) 1 else 0; |
| 3007 | 3346 | const hi20: i20 = @truncate((x >> 12) +% carry); |
| 3008 | 3347 | |
| 3009 | | // TODO: add test case for 32-bit immediate |
| 3010 | 3348 | _ = try self.addInst(.{ |
| 3011 | 3349 | .tag = .lui, |
| 3012 | 3350 | .data = .{ .u_type = .{ |
| ... | ... | @@ -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 | 3411 | .memory => |addr| { |
| 3073 | 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 | 3419 | .imm12 = 0, |
| 3081 | 3420 | } }, |
| 3082 | 3421 | }); |
| 3083 | | |
| 3084 | | // LOAD imm=[i12 offset = 0], rs1 |
| 3085 | 3422 | }, |
| 3086 | 3423 | .stack_offset => |off| { |
| 3087 | 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 | 3433 | .tag = tag, |
| 3097 | 3434 | .data = .{ .i_type = .{ |
| 3098 | 3435 | .rd = reg, |
| 3099 | | .rs1 = .s0, |
| 3436 | .rs1 = .sp, |
| 3100 | 3437 | .imm12 = math.cast(i12, off) orelse { |
| 3101 | 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 | 3457 | .tag = .load_symbol, |
| 3121 | 3458 | .data = .{ |
| 3122 | 3459 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ |
| 3123 | | .register = @intFromEnum(reg), |
| 3460 | .register = reg.id(), |
| 3124 | 3461 | .atom_index = atom_index, |
| 3125 | 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 | 3517 | |
| 3139 | 3518 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 3140 | 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 | 3521 | break :result dst_mcv; |
| 3143 | 3522 | }; |
| 3144 | 3523 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| ... | ... | @@ -3157,7 +3536,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 3157 | 3536 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| 3158 | 3537 | |
| 3159 | 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 | 3540 | break :result dest; |
| 3162 | 3541 | }; |
| 3163 | 3542 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -3385,8 +3764,12 @@ const CallMCValues = struct { |
| 3385 | 3764 | }; |
| 3386 | 3765 | |
| 3387 | 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 | 3768 | const mod = self.bin_file.comp.module.?; |
| 3769 | const ip = &mod.intern_pool; |
| 3770 | |
| 3771 | _ = role; |
| 3772 | |
| 3390 | 3773 | const fn_info = mod.typeToFunc(fn_ty).?; |
| 3391 | 3774 | const cc = fn_info.cc; |
| 3392 | 3775 | var result: CallMCValues = .{ |
| ... | ... | @@ -3413,26 +3796,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 3413 | 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); |
| 3417 | | defer self.gpa.free(locks); |
| 3799 | var fa_reg_i: u32 = 0; |
| 3418 | 3800 | |
| 3419 | | for (0..result.args.len) |i| { |
| 3420 | | const arg_reg = try self.register_manager.allocReg(null, fa); |
| 3421 | | const lock = self.register_manager.lockRegAssumeUnused(arg_reg); |
| 3422 | | locks[i] = lock; |
| 3423 | | result.args[i] = .{ .register = arg_reg }; |
| 3424 | | } |
| 3801 | // spill the needed argument registers |
| 3802 | for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| { |
| 3803 | const param_ty = Type.fromInterned(ty); |
| 3804 | const param_size = param_ty.abiSize(mod); |
| 3425 | 3805 | |
| 3426 | | // we can just free the locks now, as this should be the only place where the fa |
| 3427 | | // arg set is used. |
| 3428 | | for (locks) |lock| { |
| 3429 | | self.register_manager.unlockReg(lock); |
| 3806 | switch (param_size) { |
| 3807 | 1...8 => { |
| 3808 | const arg_reg: Register = abi.function_arg_regs[fa_reg_i]; |
| 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 |
| 3433 | | // TODO: spill used s registers here |
| 3434 | | |
| 3435 | | result.stack_byte_count = 0; |
| 3823 | result.stack_byte_count = self.max_end_stack; |
| 3436 | 3824 | result.stack_align = .@"16"; |
| 3437 | 3825 | }, |
| 3438 | 3826 | else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}), |