| ... | @@ -209,6 +209,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -209,6 +209,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 209 | err_msg: ?*ErrorMsg, | 209 | err_msg: ?*ErrorMsg, |
| 210 | args: []MCValue, | 210 | args: []MCValue, |
| 211 | ret_mcv: MCValue, | 211 | ret_mcv: MCValue, |
| | 212 | fn_type: Type, |
| 212 | arg_index: usize, | 213 | arg_index: usize, |
| 213 | src: usize, | 214 | src: usize, |
| 214 | stack_align: u32, | 215 | stack_align: u32, |
| ... | @@ -230,15 +231,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -230,15 +231,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 230 | /// No more references to this value remain. | 231 | /// No more references to this value remain. |
| 231 | dead, | 232 | dead, |
| 232 | /// A pointer-sized integer that fits in a register. | 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 | immediate: u64, | 235 | immediate: u64, |
| 234 | /// The constant was emitted into the code, at this offset. | 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 | embedded_in_code: usize, | 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 | /// The value is in a target-specific register. | 241 | /// The value is in a target-specific register. |
| 237 | register: Register, | 242 | register: Register, |
| 238 | /// The value is in memory at a hard-coded address. | 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 | memory: u64, | 245 | memory: u64, |
| 240 | /// The value is one of the stack variables. | 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 | /// The value is in the compare flags assuming an unsigned operation, | 251 | /// The value is in the compare flags assuming an unsigned operation, |
| 243 | /// with this operator applied on top of it. | 252 | /// with this operator applied on top of it. |
| 244 | compare_flags_unsigned: math.CompareOperator, | 253 | compare_flags_unsigned: math.CompareOperator, |
| ... | @@ -271,6 +280,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -271,6 +280,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 271 | .memory, | 280 | .memory, |
| 272 | .compare_flags_unsigned, | 281 | .compare_flags_unsigned, |
| 273 | .compare_flags_signed, | 282 | .compare_flags_signed, |
| | 283 | .ptr_stack_offset, |
| | 284 | .ptr_embedded_in_code, |
| 274 | => false, | 285 | => false, |
| 275 | | 286 | |
| 276 | .register, | 287 | .register, |
| ... | @@ -356,6 +367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -356,6 +367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 356 | .err_msg = null, | 367 | .err_msg = null, |
| 357 | .args = undefined, // populated after `resolveCallingConventionValues` | 368 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 358 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` | 369 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| | 370 | .fn_type = fn_type, |
| 359 | .arg_index = 0, | 371 | .arg_index = 0, |
| 360 | .branch_stack = &branch_stack, | 372 | .branch_stack = &branch_stack, |
| 361 | .src = src, | 373 | .src = src, |
| ... | @@ -459,26 +471,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -459,26 +471,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 459 | .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq), | 471 | .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq), |
| 460 | .condbr => return self.genCondBr(inst.castTag(.condbr).?), | 472 | .condbr => return self.genCondBr(inst.castTag(.condbr).?), |
| 461 | .constant => unreachable, // excluded from function bodies | 473 | .constant => unreachable, // excluded from function bodies |
| | 474 | .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?), |
| | 475 | .intcast => return self.genIntCast(inst.castTag(.intcast).?), |
| 462 | .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?), | 476 | .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?), |
| 463 | .isnull => return self.genIsNull(inst.castTag(.isnull).?), | 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 | .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), | 480 | .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), |
| | 481 | .ref => return self.genRef(inst.castTag(.ref).?), |
| 465 | .ret => return self.genRet(inst.castTag(.ret).?), | 482 | .ret => return self.genRet(inst.castTag(.ret).?), |
| 466 | .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?), | 483 | .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?), |
| | 484 | .store => return self.genStore(inst.castTag(.store).?), |
| 467 | .sub => return self.genSub(inst.castTag(.sub).?), | 485 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 468 | .unreach => return MCValue{ .unreach = {} }, | 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 { | 490 | fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 { |
| 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.*); | | |
| 482 | if (abi_align > self.stack_align) | 491 | if (abi_align > self.stack_align) |
| 483 | self.stack_align = abi_align; | 492 | self.stack_align = abi_align; |
| 484 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 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,10 +497,66 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 488 | if (branch.next_stack_offset > branch.max_end_stack) | 497 | if (branch.next_stack_offset > branch.max_end_stack) |
| 489 | branch.max_end_stack = branch.next_stack_offset; | 498 | branch.max_end_stack = branch.next_stack_offset; |
| 490 | try branch.stack.putNoClobber(self.gpa, offset, .{ | 499 | try branch.stack.putNoClobber(self.gpa, offset, .{ |
| 491 | .inst = &inst.base, | 500 | .inst = inst, |
| 492 | .size = abi_size, | 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 | fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | 562 | fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| ... | @@ -572,6 +637,85 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -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 | fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue { | 719 | fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue { |
| 576 | // No side effects, so if it's unreferenced, do nothing. | 720 | // No side effects, so if it's unreferenced, do nothing. |
| 577 | if (inst.base.isUnused()) | 721 | if (inst.base.isUnused()) |
| ... | @@ -657,10 +801,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -657,10 +801,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 657 | .dead, .unreach, .immediate => unreachable, | 801 | .dead, .unreach, .immediate => unreachable, |
| 658 | .compare_flags_unsigned => unreachable, | 802 | .compare_flags_unsigned => unreachable, |
| 659 | .compare_flags_signed => unreachable, | 803 | .compare_flags_signed => unreachable, |
| | 804 | .ptr_stack_offset => unreachable, |
| | 805 | .ptr_embedded_in_code => unreachable, |
| 660 | .register => |dst_reg| { | 806 | .register => |dst_reg| { |
| 661 | switch (src_mcv) { | 807 | switch (src_mcv) { |
| 662 | .none => unreachable, | 808 | .none => unreachable, |
| 663 | .dead, .unreach => unreachable, | 809 | .dead, .unreach => unreachable, |
| | 810 | .ptr_stack_offset => unreachable, |
| | 811 | .ptr_embedded_in_code => unreachable, |
| 664 | .register => |src_reg| { | 812 | .register => |src_reg| { |
| 665 | self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 }); | 813 | self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 }); |
| 666 | self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) }); | 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,6 +891,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 743 | for (info.args) |mc_arg, arg_i| { | 891 | for (info.args) |mc_arg, arg_i| { |
| 744 | const arg = inst.args[arg_i]; | 892 | const arg = inst.args[arg_i]; |
| 745 | const arg_mcv = try self.resolveInst(inst.args[arg_i]); | 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 | switch (mc_arg) { | 896 | switch (mc_arg) { |
| 747 | .none => continue, | 897 | .none => continue, |
| 748 | .register => |reg| { | 898 | .register => |reg| { |
| ... | @@ -754,6 +904,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -754,6 +904,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 754 | // mov qword ptr [rsp + stack_offset], x | 904 | // mov qword ptr [rsp + stack_offset], x |
| 755 | return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); | 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 | .immediate => unreachable, | 913 | .immediate => unreachable, |
| 758 | .unreach => unreachable, | 914 | .unreach => unreachable, |
| 759 | .dead => unreachable, | 915 | .dead => unreachable, |
| ... | @@ -788,8 +944,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -788,8 +944,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 788 | return info.return_value; | 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 | fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue { | 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 | switch (arch) { | 975 | switch (arch) { |
| 794 | .i386 => { | 976 | .i386 => { |
| 795 | try self.code.append(0xc3); // ret | 977 | try self.code.append(0xc3); // ret |
| ... | @@ -1042,21 +1224,74 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1042,21 +1224,74 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1042 | } | 1224 | } |
| 1043 | | 1225 | |
| 1044 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. | 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 | switch (loc) { | 1228 | switch (loc) { |
| 1047 | .none => return, | 1229 | .none => return, |
| 1048 | .register => |reg| return self.genSetReg(src, reg, val), | 1230 | .register => |reg| return self.genSetReg(src, reg, val), |
| 1049 | .stack_offset => { | 1231 | .stack_offset => |off| return self.genSetStack(src, ty, off, val), |
| 1050 | return self.fail(src, "TODO implement setRegOrStack for stack offset", .{}); | 1232 | .memory => { |
| | 1233 | return self.fail(src, "TODO implement setRegOrMem for memory", .{}); |
| 1051 | }, | 1234 | }, |
| 1052 | else => unreachable, | 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 | switch (arch) { | 1290 | switch (arch) { |
| 1058 | .x86_64 => switch (mcv) { | 1291 | .x86_64 => switch (mcv) { |
| 1059 | .dead => unreachable, | 1292 | .dead => unreachable, |
| | 1293 | .ptr_stack_offset => unreachable, |
| | 1294 | .ptr_embedded_in_code => unreachable, |
| 1060 | .unreach, .none => return, // Nothing to do. | 1295 | .unreach, .none => return, // Nothing to do. |
| 1061 | .compare_flags_unsigned => |op| { | 1296 | .compare_flags_unsigned => |op| { |
| 1062 | try self.code.ensureCapacity(self.code.items.len + 3); | 1297 | try self.code.ensureCapacity(self.code.items.len + 3); |
| ... | @@ -1279,24 +1514,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -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 | /// If the MCValue is an immediate, and it does not fit within this type, | 1517 | /// If the MCValue is an immediate, and it does not fit within this type, |
| 1301 | /// we put it in a register. | 1518 | /// we put it in a register. |
| 1302 | /// A potential opportunity for future optimization here would be keeping track | 1519 | /// A potential opportunity for future optimization here would be keeping track |