| ... | ... | @@ -209,6 +209,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 209 | 209 | err_msg: ?*ErrorMsg, |
| 210 | 210 | args: []MCValue, |
| 211 | 211 | ret_mcv: MCValue, |
| 212 | fn_type: Type, |
| 212 | 213 | arg_index: usize, |
| 213 | 214 | src: usize, |
| 214 | 215 | stack_align: u32, |
| ... | ... | @@ -230,15 +231,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 230 | 231 | /// No more references to this value remain. |
| 231 | 232 | dead, |
| 232 | 233 | /// A pointer-sized integer that fits in a register. |
| 234 | /// If the type is a pointer, this is the pointer address in virtual address space. |
| 233 | 235 | immediate: u64, |
| 234 | 236 | /// The constant was emitted into the code, at this offset. |
| 237 | /// If the type is a pointer, it means the pointer address is embedded in the code. |
| 235 | 238 | embedded_in_code: usize, |
| 239 | /// The value is a pointer to a constant which was emitted into the code, at this offset. |
| 240 | ptr_embedded_in_code: usize, |
| 236 | 241 | /// The value is in a target-specific register. |
| 237 | 242 | register: Register, |
| 238 | 243 | /// The value is in memory at a hard-coded address. |
| 244 | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 239 | 245 | memory: u64, |
| 240 | 246 | /// The value is one of the stack variables. |
| 241 | | stack_offset: u64, |
| 247 | /// If the type is a pointer, it means the pointer address is in the stack at this offset. |
| 248 | stack_offset: u32, |
| 249 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 250 | ptr_stack_offset: u32, |
| 242 | 251 | /// The value is in the compare flags assuming an unsigned operation, |
| 243 | 252 | /// with this operator applied on top of it. |
| 244 | 253 | compare_flags_unsigned: math.CompareOperator, |
| ... | ... | @@ -271,6 +280,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 271 | 280 | .memory, |
| 272 | 281 | .compare_flags_unsigned, |
| 273 | 282 | .compare_flags_signed, |
| 283 | .ptr_stack_offset, |
| 284 | .ptr_embedded_in_code, |
| 274 | 285 | => false, |
| 275 | 286 | |
| 276 | 287 | .register, |
| ... | ... | @@ -356,6 +367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 356 | 367 | .err_msg = null, |
| 357 | 368 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 358 | 369 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 370 | .fn_type = fn_type, |
| 359 | 371 | .arg_index = 0, |
| 360 | 372 | .branch_stack = &branch_stack, |
| 361 | 373 | .src = src, |
| ... | ... | @@ -459,26 +471,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 459 | 471 | .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq), |
| 460 | 472 | .condbr => return self.genCondBr(inst.castTag(.condbr).?), |
| 461 | 473 | .constant => unreachable, // excluded from function bodies |
| 474 | .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?), |
| 475 | .intcast => return self.genIntCast(inst.castTag(.intcast).?), |
| 462 | 476 | .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?), |
| 463 | 477 | .isnull => return self.genIsNull(inst.castTag(.isnull).?), |
| 478 | .load => return self.genLoad(inst.castTag(.load).?), |
| 479 | .not => return self.genNot(inst.castTag(.not).?), |
| 464 | 480 | .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), |
| 481 | .ref => return self.genRef(inst.castTag(.ref).?), |
| 465 | 482 | .ret => return self.genRet(inst.castTag(.ret).?), |
| 466 | 483 | .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?), |
| 484 | .store => return self.genStore(inst.castTag(.store).?), |
| 467 | 485 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 468 | 486 | .unreach => return MCValue{ .unreach = {} }, |
| 469 | | .not => return self.genNot(inst.castTag(.not).?), |
| 470 | | .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?), |
| 471 | | .intcast => return self.genIntCast(inst.castTag(.intcast).?), |
| 472 | 487 | } |
| 473 | 488 | } |
| 474 | 489 | |
| 475 | | fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue { |
| 476 | | const elem_ty = inst.base.ty.elemType(); |
| 477 | | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { |
| 478 | | return self.fail(inst.base.src, "type '{}' too big to fit into stack frame", .{elem_ty}); |
| 479 | | }; |
| 480 | | // TODO swap this for inst.base.ty.ptrAlign |
| 481 | | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 490 | fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 { |
| 482 | 491 | if (abi_align > self.stack_align) |
| 483 | 492 | self.stack_align = abi_align; |
| 484 | 493 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| ... | ... | @@ -488,10 +497,66 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 488 | 497 | if (branch.next_stack_offset > branch.max_end_stack) |
| 489 | 498 | branch.max_end_stack = branch.next_stack_offset; |
| 490 | 499 | try branch.stack.putNoClobber(self.gpa, offset, .{ |
| 491 | | .inst = &inst.base, |
| 500 | .inst = inst, |
| 492 | 501 | .size = abi_size, |
| 493 | 502 | }); |
| 494 | | return MCValue{ .stack_offset = offset }; |
| 503 | return offset; |
| 504 | } |
| 505 | |
| 506 | /// Use a pointer instruction as the basis for allocating stack memory. |
| 507 | fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 { |
| 508 | const elem_ty = inst.ty.elemType(); |
| 509 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { |
| 510 | return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty}); |
| 511 | }; |
| 512 | // TODO swap this for inst.ty.ptrAlign |
| 513 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 514 | return self.allocMem(inst, abi_size, abi_align); |
| 515 | } |
| 516 | |
| 517 | fn allocRegOrMem(self: *Self, inst: *ir.Inst) !MCValue { |
| 518 | const elem_ty = inst.ty; |
| 519 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { |
| 520 | return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty}); |
| 521 | }; |
| 522 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 523 | if (abi_align > self.stack_align) |
| 524 | self.stack_align = abi_align; |
| 525 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 526 | |
| 527 | // TODO Make sure the type can fit in a register before we try to allocate one. |
| 528 | const free_index = @ctz(FreeRegInt, branch.free_registers); |
| 529 | if (free_index >= callee_preserved_regs.len) { |
| 530 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 531 | return MCValue{ .stack_offset = stack_offset }; |
| 532 | } |
| 533 | branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index); |
| 534 | const reg = callee_preserved_regs[free_index]; |
| 535 | try branch.registers.putNoClobber(self.gpa, reg, .{ .inst = inst }); |
| 536 | return MCValue{ .register = reg }; |
| 537 | } |
| 538 | |
| 539 | /// Does not "move" the instruction. |
| 540 | fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue { |
| 541 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 542 | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); |
| 543 | try branch.inst_table.ensureCapacity(self.gpa, branch.inst_table.items().len + 1); |
| 544 | |
| 545 | const free_index = @ctz(FreeRegInt, branch.free_registers); |
| 546 | if (free_index >= callee_preserved_regs.len) |
| 547 | return self.fail(inst.src, "TODO implement spilling register to stack", .{}); |
| 548 | branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index); |
| 549 | const reg = callee_preserved_regs[free_index]; |
| 550 | branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst }); |
| 551 | const old_mcv = branch.inst_table.get(inst).?; |
| 552 | const new_mcv: MCValue = .{ .register = reg }; |
| 553 | try self.genSetReg(inst.src, reg, old_mcv); |
| 554 | return new_mcv; |
| 555 | } |
| 556 | |
| 557 | fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue { |
| 558 | const stack_offset = try self.allocMemPtr(&inst.base); |
| 559 | return MCValue{ .ptr_stack_offset = stack_offset }; |
| 495 | 560 | } |
| 496 | 561 | |
| 497 | 562 | fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| ... | ... | @@ -572,6 +637,85 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 572 | 637 | } |
| 573 | 638 | } |
| 574 | 639 | |
| 640 | fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 641 | const elem_ty = inst.base.ty; |
| 642 | if (!elem_ty.hasCodeGenBits()) |
| 643 | return MCValue.none; |
| 644 | const ptr = try self.resolveInst(inst.operand); |
| 645 | const is_volatile = inst.operand.ty.isVolatilePtr(); |
| 646 | if (inst.base.isUnused() and !is_volatile) |
| 647 | return MCValue.dead; |
| 648 | const dst_mcv: MCValue = blk: { |
| 649 | if (inst.base.operandDies(0) and ptr.isMutable()) { |
| 650 | // The MCValue that holds the pointer can be re-used as the value. |
| 651 | // TODO track this in the register/stack allocation metadata. |
| 652 | break :blk ptr; |
| 653 | } else { |
| 654 | break :blk try self.allocRegOrMem(&inst.base); |
| 655 | } |
| 656 | }; |
| 657 | switch (ptr) { |
| 658 | .none => unreachable, |
| 659 | .unreach => unreachable, |
| 660 | .dead => unreachable, |
| 661 | .compare_flags_unsigned => unreachable, |
| 662 | .compare_flags_signed => unreachable, |
| 663 | .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }), |
| 664 | .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 665 | .ptr_embedded_in_code => |off| { |
| 666 | try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .embedded_in_code = off }); |
| 667 | }, |
| 668 | .embedded_in_code => { |
| 669 | return self.fail(inst.base.src, "TODO implement loading from MCValue.embedded_in_code", .{}); |
| 670 | }, |
| 671 | .register => { |
| 672 | return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{}); |
| 673 | }, |
| 674 | .memory => { |
| 675 | return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{}); |
| 676 | }, |
| 677 | .stack_offset => { |
| 678 | return self.fail(inst.base.src, "TODO implement loading from MCValue.stack_offset", .{}); |
| 679 | }, |
| 680 | } |
| 681 | return dst_mcv; |
| 682 | } |
| 683 | |
| 684 | fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue { |
| 685 | const ptr = try self.resolveInst(inst.lhs); |
| 686 | const value = try self.resolveInst(inst.rhs); |
| 687 | const elem_ty = inst.rhs.ty; |
| 688 | switch (ptr) { |
| 689 | .none => unreachable, |
| 690 | .unreach => unreachable, |
| 691 | .dead => unreachable, |
| 692 | .compare_flags_unsigned => unreachable, |
| 693 | .compare_flags_signed => unreachable, |
| 694 | .immediate => |imm| { |
| 695 | try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value); |
| 696 | }, |
| 697 | .ptr_stack_offset => |off| { |
| 698 | try self.genSetStack(inst.base.src, elem_ty, off, value); |
| 699 | }, |
| 700 | .ptr_embedded_in_code => |off| { |
| 701 | try self.setRegOrMem(inst.base.src, elem_ty, .{ .embedded_in_code = off }, value); |
| 702 | }, |
| 703 | .embedded_in_code => { |
| 704 | return self.fail(inst.base.src, "TODO implement storing to MCValue.embedded_in_code", .{}); |
| 705 | }, |
| 706 | .register => { |
| 707 | return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{}); |
| 708 | }, |
| 709 | .memory => { |
| 710 | return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{}); |
| 711 | }, |
| 712 | .stack_offset => { |
| 713 | return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{}); |
| 714 | }, |
| 715 | } |
| 716 | return .none; |
| 717 | } |
| 718 | |
| 575 | 719 | fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue { |
| 576 | 720 | // No side effects, so if it's unreferenced, do nothing. |
| 577 | 721 | if (inst.base.isUnused()) |
| ... | ... | @@ -657,10 +801,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 657 | 801 | .dead, .unreach, .immediate => unreachable, |
| 658 | 802 | .compare_flags_unsigned => unreachable, |
| 659 | 803 | .compare_flags_signed => unreachable, |
| 804 | .ptr_stack_offset => unreachable, |
| 805 | .ptr_embedded_in_code => unreachable, |
| 660 | 806 | .register => |dst_reg| { |
| 661 | 807 | switch (src_mcv) { |
| 662 | 808 | .none => unreachable, |
| 663 | 809 | .dead, .unreach => unreachable, |
| 810 | .ptr_stack_offset => unreachable, |
| 811 | .ptr_embedded_in_code => unreachable, |
| 664 | 812 | .register => |src_reg| { |
| 665 | 813 | self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 }); |
| 666 | 814 | self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) }); |
| ... | ... | @@ -743,6 +891,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 743 | 891 | for (info.args) |mc_arg, arg_i| { |
| 744 | 892 | const arg = inst.args[arg_i]; |
| 745 | 893 | const arg_mcv = try self.resolveInst(inst.args[arg_i]); |
| 894 | // Here we do not use setRegOrMem even though the logic is similar, because |
| 895 | // the function call will move the stack pointer, so the offsets are different. |
| 746 | 896 | switch (mc_arg) { |
| 747 | 897 | .none => continue, |
| 748 | 898 | .register => |reg| { |
| ... | ... | @@ -754,6 +904,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 754 | 904 | // mov qword ptr [rsp + stack_offset], x |
| 755 | 905 | return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); |
| 756 | 906 | }, |
| 907 | .ptr_stack_offset => { |
| 908 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset", .{}); |
| 909 | }, |
| 910 | .ptr_embedded_in_code => { |
| 911 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code", .{}); |
| 912 | }, |
| 757 | 913 | .immediate => unreachable, |
| 758 | 914 | .unreach => unreachable, |
| 759 | 915 | .dead => unreachable, |
| ... | ... | @@ -788,8 +944,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 788 | 944 | return info.return_value; |
| 789 | 945 | } |
| 790 | 946 | |
| 947 | fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 948 | const operand = try self.resolveInst(inst.operand); |
| 949 | switch (operand) { |
| 950 | .unreach => unreachable, |
| 951 | .dead => unreachable, |
| 952 | .none => return .none, |
| 953 | |
| 954 | .immediate, |
| 955 | .register, |
| 956 | .ptr_stack_offset, |
| 957 | .ptr_embedded_in_code, |
| 958 | .compare_flags_unsigned, |
| 959 | .compare_flags_signed, |
| 960 | => { |
| 961 | const stack_offset = try self.allocMemPtr(&inst.base); |
| 962 | try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand); |
| 963 | return MCValue{ .ptr_stack_offset = stack_offset }; |
| 964 | }, |
| 965 | |
| 966 | .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset }, |
| 967 | .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset }, |
| 968 | .memory => |vaddr| return MCValue{ .immediate = vaddr }, |
| 969 | } |
| 970 | } |
| 971 | |
| 791 | 972 | fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue { |
| 792 | | try self.setRegOrStack(src, self.ret_mcv, mcv); |
| 973 | const ret_ty = self.fn_type.fnReturnType(); |
| 974 | try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv); |
| 793 | 975 | switch (arch) { |
| 794 | 976 | .i386 => { |
| 795 | 977 | try self.code.append(0xc3); // ret |
| ... | ... | @@ -1042,21 +1224,74 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1042 | 1224 | } |
| 1043 | 1225 | |
| 1044 | 1226 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. |
| 1045 | | fn setRegOrStack(self: *Self, src: usize, loc: MCValue, val: MCValue) !void { |
| 1227 | fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void { |
| 1046 | 1228 | switch (loc) { |
| 1047 | 1229 | .none => return, |
| 1048 | 1230 | .register => |reg| return self.genSetReg(src, reg, val), |
| 1049 | | .stack_offset => { |
| 1050 | | return self.fail(src, "TODO implement setRegOrStack for stack offset", .{}); |
| 1231 | .stack_offset => |off| return self.genSetStack(src, ty, off, val), |
| 1232 | .memory => { |
| 1233 | return self.fail(src, "TODO implement setRegOrMem for memory", .{}); |
| 1051 | 1234 | }, |
| 1052 | 1235 | else => unreachable, |
| 1053 | 1236 | } |
| 1054 | 1237 | } |
| 1055 | 1238 | |
| 1056 | | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void { |
| 1239 | fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { |
| 1240 | switch (arch) { |
| 1241 | .x86_64 => switch (mcv) { |
| 1242 | .dead => unreachable, |
| 1243 | .ptr_stack_offset => unreachable, |
| 1244 | .ptr_embedded_in_code => unreachable, |
| 1245 | .unreach, .none => return, // Nothing to do. |
| 1246 | .compare_flags_unsigned => |op| { |
| 1247 | return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{}); |
| 1248 | }, |
| 1249 | .compare_flags_signed => |op| { |
| 1250 | return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{}); |
| 1251 | }, |
| 1252 | .immediate => |x_big| { |
| 1253 | try self.code.ensureCapacity(self.code.items.len + 7); |
| 1254 | if (x_big <= math.maxInt(u32)) { |
| 1255 | const x = @intCast(u32, x_big); |
| 1256 | if (stack_offset > 128) { |
| 1257 | return self.fail(src, "TODO implement set stack variable with large stack offset", .{}); |
| 1258 | } |
| 1259 | // We have a positive stack offset value but we want a twos complement negative |
| 1260 | // offset from rbp, which is at the top of the stack frame. |
| 1261 | const negative_offset = @intCast(i8, -@intCast(i32, stack_offset)); |
| 1262 | const twos_comp = @bitCast(u8, negative_offset); |
| 1263 | // mov DWORD PTR [rbp+offset], immediate |
| 1264 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp }); |
| 1265 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x); |
| 1266 | } else { |
| 1267 | return self.fail(src, "TODO implement set stack variable with large immediate", .{}); |
| 1268 | } |
| 1269 | }, |
| 1270 | .embedded_in_code => |code_offset| { |
| 1271 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); |
| 1272 | }, |
| 1273 | .register => |reg| { |
| 1274 | return self.fail(src, "TODO implement set stack variable from register", .{}); |
| 1275 | }, |
| 1276 | .memory => |vaddr| { |
| 1277 | return self.fail(src, "TODO implement set stack variable from memory vaddr", .{}); |
| 1278 | }, |
| 1279 | .stack_offset => |off| { |
| 1280 | if (stack_offset == off) |
| 1281 | return; // Copy stack variable to itself; nothing to do. |
| 1282 | return self.fail(src, "TODO implement copy stack variable to stack variable", .{}); |
| 1283 | }, |
| 1284 | }, |
| 1285 | else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}), |
| 1286 | } |
| 1287 | } |
| 1288 | |
| 1289 | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { |
| 1057 | 1290 | switch (arch) { |
| 1058 | 1291 | .x86_64 => switch (mcv) { |
| 1059 | 1292 | .dead => unreachable, |
| 1293 | .ptr_stack_offset => unreachable, |
| 1294 | .ptr_embedded_in_code => unreachable, |
| 1060 | 1295 | .unreach, .none => return, // Nothing to do. |
| 1061 | 1296 | .compare_flags_unsigned => |op| { |
| 1062 | 1297 | try self.code.ensureCapacity(self.code.items.len + 3); |
| ... | ... | @@ -1279,24 +1514,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1279 | 1514 | } |
| 1280 | 1515 | } |
| 1281 | 1516 | |
| 1282 | | /// Does not "move" the instruction. |
| 1283 | | fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue { |
| 1284 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1285 | | try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1); |
| 1286 | | try branch.inst_table.ensureCapacity(self.gpa, branch.inst_table.items().len + 1); |
| 1287 | | |
| 1288 | | const free_index = @ctz(FreeRegInt, branch.free_registers); |
| 1289 | | if (free_index >= callee_preserved_regs.len) |
| 1290 | | return self.fail(inst.src, "TODO implement spilling register to stack", .{}); |
| 1291 | | branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index); |
| 1292 | | const reg = callee_preserved_regs[free_index]; |
| 1293 | | branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst }); |
| 1294 | | const old_mcv = branch.inst_table.get(inst).?; |
| 1295 | | const new_mcv: MCValue = .{ .register = reg }; |
| 1296 | | try self.genSetReg(inst.src, reg, old_mcv); |
| 1297 | | return new_mcv; |
| 1298 | | } |
| 1299 | | |
| 1300 | 1517 | /// If the MCValue is an immediate, and it does not fit within this type, |
| 1301 | 1518 | /// we put it in a register. |
| 1302 | 1519 | /// A potential opportunity for future optimization here would be keeping track |