authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-21 23:36:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-21 23:36:21-07:00
logf18b92ef3ab689749efd135968f0834478f9fffa
treec15a49a80dd35c40df2f8f0976d7462304f8c301
parentdad7af0b37f352c8c390438877ef1552a316ffde

stage2: implement spilling registers to the stack


2 files changed, 227 insertions(+), 60 deletions(-)

src-self-hosted/codegen.zig+175-60
...@@ -14,6 +14,7 @@ const Allocator = mem.Allocator;...@@ -14,6 +14,7 @@ const Allocator = mem.Allocator;
14const trace = @import("tracy.zig").trace;14const trace = @import("tracy.zig").trace;
15const DW = std.dwarf;15const DW = std.dwarf;
16const leb128 = std.debug.leb;16const leb128 = std.debug.leb;
17const log = std.log.scoped(.codegen);
1718
18// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.19// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
19// zig fmt: off20// zig fmt: off
...@@ -344,6 +345,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -344,6 +345,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
344345
345 const Branch = struct {346 const Branch = struct {
346 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},347 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
348 /// The key must be canonical register.
347 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},349 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
348 free_registers: FreeRegInt = math.maxInt(FreeRegInt),350 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
349351
...@@ -381,9 +383,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -381,9 +383,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
381 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);383 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
382 const reg = callee_preserved_regs[free_index];384 const reg = callee_preserved_regs[free_index];
383 self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });385 self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
386 log.debug("alloc {} => {*}", .{reg, inst});
384 return reg;387 return reg;
385 }388 }
386389
390 /// Does not track the register.
391 fn findUnusedReg(self: *Branch) ?Register {
392 const free_index = @ctz(FreeRegInt, self.free_registers);
393 if (free_index >= callee_preserved_regs.len) {
394 return null;
395 }
396 return callee_preserved_regs[free_index];
397 }
398
387 fn deinit(self: *Branch, gpa: *Allocator) void {399 fn deinit(self: *Branch, gpa: *Allocator) void {
388 self.inst_table.deinit(gpa);400 self.inst_table.deinit(gpa);
389 self.registers.deinit(gpa);401 self.registers.deinit(gpa);
...@@ -570,8 +582,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -570,8 +582,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
570 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];582 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
571 const inst_table = &branch.inst_table;583 const inst_table = &branch.inst_table;
572 for (body.instructions) |inst| {584 for (body.instructions) |inst| {
573 const new_inst = try self.genFuncInst(inst);585 const mcv = try self.genFuncInst(inst);
574 try inst_table.putNoClobber(self.gpa, inst, new_inst);586 log.debug("{*} => {}", .{inst, mcv});
587 // TODO don't put void or dead things in here
588 try inst_table.putNoClobber(self.gpa, inst, mcv);
575589
576 var i: ir.Inst.DeathsBitIndex = 0;590 var i: ir.Inst.DeathsBitIndex = 0;
577 while (inst.getOperand(i)) |operand| : (i += 1) {591 while (inst.getOperand(i)) |operand| : (i += 1) {
...@@ -714,7 +728,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -714,7 +728,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
714 return self.allocMem(inst, abi_size, abi_align);728 return self.allocMem(inst, abi_size, abi_align);
715 }729 }
716730
717 fn allocRegOrMem(self: *Self, inst: *ir.Inst) !MCValue {731 fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue {
718 const elem_ty = inst.ty;732 const elem_ty = inst.ty;
719 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {733 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
720 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});734 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
...@@ -724,30 +738,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -724,30 +738,73 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
724 self.stack_align = abi_align;738 self.stack_align = abi_align;
725 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];739 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
726740
727 // Make sure the type can fit in a register before we try to allocate one.741 if (reg_ok) {
728 const ptr_bits = arch.ptrBitWidth();742 // Make sure the type can fit in a register before we try to allocate one.
729 const ptr_bytes: u64 = @divExact(ptr_bits, 8);743 const ptr_bits = arch.ptrBitWidth();
730 if (abi_size <= ptr_bytes) {744 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
731 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);745 if (abi_size <= ptr_bytes) {
732 if (branch.allocReg(inst)) |reg| {746 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
733 return MCValue{ .register = registerAlias(reg, abi_size) };747 if (branch.allocReg(inst)) |reg| {
748 return MCValue{ .register = registerAlias(reg, abi_size) };
749 }
734 }750 }
735 }751 }
736 const stack_offset = try self.allocMem(inst, abi_size, abi_align);752 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
737 return MCValue{ .stack_offset = stack_offset };753 return MCValue{ .stack_offset = stack_offset };
738 }754 }
739755
740 /// Does not "move" the instruction.756 /// Copies a value to a register without tracking the register. The register is not considered
741 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {757 /// allocated. A second call to `copyToTmpRegister` may return the same register.
758 /// This can have a side effect of spilling instructions to the stack to free up a register.
759 fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
760 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
761
762 const reg = branch.findUnusedReg() orelse b: {
763 // We'll take over the first register. Move the instruction that was previously
764 // there to a stack allocation.
765 const reg = callee_preserved_regs[0];
766 const regs_entry = branch.registers.remove(reg).?;
767 const spilled_inst = regs_entry.value.inst;
768
769 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
770 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;
771 const reg_mcv = inst_entry.value;
772 assert(reg == toCanonicalReg(reg_mcv.register));
773 inst_entry.value = stack_mcv;
774 try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
775
776 break :b reg;
777 };
778 try self.genSetReg(src, reg, mcv);
779 return reg;
780 }
781
782 /// Allocates a new register and copies `mcv` into it.
783 /// `reg_owner` is the instruction that gets associated with the register in the register table.
784 /// This can have a side effect of spilling instructions to the stack to free up a register.
785 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
742 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];786 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
743 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);787 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
744788
745 const reg = branch.allocReg(inst) orelse789 const reg = branch.allocReg(reg_owner) orelse b: {
746 return self.fail(inst.src, "TODO implement spilling register to stack", .{});790 // We'll take over the first register. Move the instruction that was previously
747 const old_mcv = branch.inst_table.get(inst).?;791 // there to a stack allocation.
748 const new_mcv: MCValue = .{ .register = reg };792 const reg = callee_preserved_regs[0];
749 try self.genSetReg(inst.src, reg, old_mcv);793 const regs_entry = branch.registers.getEntry(reg).?;
750 return new_mcv;794 const spilled_inst = regs_entry.value.inst;
795 regs_entry.value = .{ .inst = reg_owner };
796
797 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
798 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;
799 const reg_mcv = inst_entry.value;
800 assert(reg == toCanonicalReg(reg_mcv.register));
801 inst_entry.value = stack_mcv;
802 try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
803
804 break :b reg;
805 };
806 try self.genSetReg(reg_owner.src, reg, mcv);
807 return MCValue{ .register = reg };
751 }808 }
752809
753 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {810 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
...@@ -868,13 +925,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -868,13 +925,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
868 }925 }
869 }926 }
870927
871 fn reuseOperand(inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {928 fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
872 if (!inst.operandDies(op_index) or !mcv.isMutable())929 if (!inst.operandDies(op_index))
873 return false;930 return false;
874931
875 // OK we're going to do it, but we need to clear the operand death bit so that932 switch (mcv) {
876 // it stays allocated.933 .register => |reg| {
934 // If it's in the registers table, need to associate the register with the
935 // new instruction.
936 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
937 const entry = branch.registers.getEntry(toCanonicalReg(reg)).?;
938 entry.value = .{ .inst = inst };
939 log.debug("reusing {} => {*}", .{reg, inst});
940 },
941 .stack_offset => |off| {
942 log.debug("reusing stack offset {} => {*}", .{off, inst});
943 return true;
944 },
945 else => return false,
946 }
947
948 // Prevent the operand deaths processing code from deallocating it.
877 inst.clearOperandDeath(op_index);949 inst.clearOperandDeath(op_index);
950
878 return true;951 return true;
879 }952 }
880953
...@@ -887,11 +960,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -887,11 +960,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
887 if (inst.base.isUnused() and !is_volatile)960 if (inst.base.isUnused() and !is_volatile)
888 return MCValue.dead;961 return MCValue.dead;
889 const dst_mcv: MCValue = blk: {962 const dst_mcv: MCValue = blk: {
890 if (reuseOperand(&inst.base, 0, ptr)) {963 if (self.reuseOperand(&inst.base, 0, ptr)) {
891 // The MCValue that holds the pointer can be re-used as the value.964 // The MCValue that holds the pointer can be re-used as the value.
892 break :blk ptr;965 break :blk ptr;
893 } else {966 } else {
894 break :blk try self.allocRegOrMem(&inst.base);967 break :blk try self.allocRegOrMem(&inst.base, true);
895 }968 }
896 };969 };
897 switch (ptr) {970 switch (ptr) {
...@@ -985,23 +1058,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -985,23 +1058,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
985 var dst_mcv: MCValue = undefined;1058 var dst_mcv: MCValue = undefined;
986 var src_mcv: MCValue = undefined;1059 var src_mcv: MCValue = undefined;
987 var src_inst: *ir.Inst = undefined;1060 var src_inst: *ir.Inst = undefined;
988 if (reuseOperand(inst, 0, lhs)) {1061 if (self.reuseOperand(inst, 0, lhs)) {
989 // LHS dies; use it as the destination.1062 // LHS dies; use it as the destination.
990 // Both operands cannot be memory.1063 // Both operands cannot be memory.
991 src_inst = op_rhs;1064 src_inst = op_rhs;
992 if (lhs.isMemory() and rhs.isMemory()) {1065 if (lhs.isMemory() and rhs.isMemory()) {
993 dst_mcv = try self.copyToNewRegister(op_lhs);1066 dst_mcv = try self.copyToNewRegister(inst, lhs);
994 src_mcv = rhs;1067 src_mcv = rhs;
995 } else {1068 } else {
996 dst_mcv = lhs;1069 dst_mcv = lhs;
997 src_mcv = rhs;1070 src_mcv = rhs;
998 }1071 }
999 } else if (reuseOperand(inst, 1, rhs)) {1072 } else if (self.reuseOperand(inst, 1, rhs)) {
1000 // RHS dies; use it as the destination.1073 // RHS dies; use it as the destination.
1001 // Both operands cannot be memory.1074 // Both operands cannot be memory.
1002 src_inst = op_lhs;1075 src_inst = op_lhs;
1003 if (lhs.isMemory() and rhs.isMemory()) {1076 if (lhs.isMemory() and rhs.isMemory()) {
1004 dst_mcv = try self.copyToNewRegister(op_rhs);1077 dst_mcv = try self.copyToNewRegister(inst, rhs);
1005 src_mcv = lhs;1078 src_mcv = lhs;
1006 } else {1079 } else {
1007 dst_mcv = rhs;1080 dst_mcv = rhs;
...@@ -1009,11 +1082,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1009,11 +1082,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1009 }1082 }
1010 } else {1083 } else {
1011 if (lhs.isMemory()) {1084 if (lhs.isMemory()) {
1012 dst_mcv = try self.copyToNewRegister(op_lhs);1085 dst_mcv = try self.copyToNewRegister(inst, lhs);
1013 src_mcv = rhs;1086 src_mcv = rhs;
1014 src_inst = op_rhs;1087 src_inst = op_rhs;
1015 } else {1088 } else {
1016 dst_mcv = try self.copyToNewRegister(op_rhs);1089 dst_mcv = try self.copyToNewRegister(inst, rhs);
1017 src_mcv = lhs;1090 src_mcv = lhs;
1018 src_inst = op_lhs;1091 src_inst = op_lhs;
1019 }1092 }
...@@ -1026,18 +1099,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1026,18 +1099,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1026 switch (src_mcv) {1099 switch (src_mcv) {
1027 .immediate => |imm| {1100 .immediate => |imm| {
1028 if (imm > math.maxInt(u31)) {1101 if (imm > math.maxInt(u31)) {
1029 src_mcv = try self.copyToNewRegister(src_inst);1102 src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) };
1030 }1103 }
1031 },1104 },
1032 else => {},1105 else => {},
1033 }1106 }
10341107
1035 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);1108 try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, opx, mr);
10361109
1037 return dst_mcv;1110 return dst_mcv;
1038 }1111 }
10391112
1040 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {1113 fn genX8664BinMathCode(
1114 self: *Self,
1115 src: usize,
1116 dst_ty: Type,
1117 dst_mcv: MCValue,
1118 src_mcv: MCValue,
1119 opx: u8,
1120 mr: u8,
1121 ) !void {
1041 switch (dst_mcv) {1122 switch (dst_mcv) {
1042 .none => unreachable,1123 .none => unreachable,
1043 .undef => unreachable,1124 .undef => unreachable,
...@@ -1087,12 +1168,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1087,12 +1168,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1087 },1168 },
1088 }1169 }
1089 },1170 },
1090 .embedded_in_code, .memory, .stack_offset => {1171 .stack_offset => |off| {
1172 switch (src_mcv) {
1173 .none => unreachable,
1174 .undef => return self.genSetStack(src, dst_ty, off, .undef),
1175 .dead, .unreach => unreachable,
1176 .ptr_stack_offset => unreachable,
1177 .ptr_embedded_in_code => unreachable,
1178 .register => |src_reg| {
1179 try self.genX8664ModRMRegToStack(src, dst_ty, off, src_reg, mr + 0x1);
1180 },
1181 .immediate => |imm| {
1182 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source immediate", .{});
1183 },
1184 .embedded_in_code, .memory, .stack_offset => {
1185 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
1186 },
1187 .compare_flags_unsigned => {
1188 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
1189 },
1190 .compare_flags_signed => {
1191 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
1192 },
1193 }
1194 },
1195 .embedded_in_code, .memory => {
1091 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});1196 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
1092 },1197 },
1093 }1198 }
1094 }1199 }
10951200
1201 fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void {
1202 const abi_size = ty.abiSize(self.target.*);
1203 const adj_off = off + abi_size;
1204 try self.code.ensureCapacity(self.code.items.len + 7);
1205 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
1206 const reg_id: u8 = @truncate(u3, reg.id());
1207 if (adj_off <= 128) {
1208 // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
1209 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1210 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1211 const twos_comp = @bitCast(u8, negative_offset);
1212 self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM, twos_comp });
1213 } else if (adj_off <= 2147483648) {
1214 // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
1215 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1216 const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
1217 const twos_comp = @bitCast(u32, negative_offset);
1218 self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM });
1219 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1220 } else {
1221 return self.fail(src, "stack offset too large", .{});
1222 }
1223 }
1224
1096 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {1225 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
1097 if (FreeRegInt == u0) {1226 if (FreeRegInt == u0) {
1098 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});1227 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
...@@ -1109,7 +1238,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1109,7 +1238,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1109 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];1238 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
1110 switch (result) {1239 switch (result) {
1111 .register => |reg| {1240 .register => |reg| {
1112 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });1241 branch.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), .{ .inst = &inst.base });
1113 branch.markRegUsed(reg);1242 branch.markRegUsed(reg);
11141243
1115 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);1244 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);
...@@ -1304,13 +1433,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1304,13 +1433,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1304 // Either one, but not both, can be a memory operand.1433 // Either one, but not both, can be a memory operand.
1305 // Source operand can be an immediate, 8 bits or 32 bits.1434 // Source operand can be an immediate, 8 bits or 32 bits.
1306 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))1435 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
1307 try self.copyToNewRegister(inst.lhs)1436 try self.copyToNewRegister(&inst.base, lhs)
1308 else1437 else
1309 lhs;1438 lhs;
1310 // This instruction supports only signed 32-bit immediates at most.1439 // This instruction supports only signed 32-bit immediates at most.
1311 const src_mcv = try self.limitImmediateType(inst.rhs, i32);1440 const src_mcv = try self.limitImmediateType(inst.rhs, i32);
13121441
1313 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);1442 try self.genX8664BinMathCode(inst.base.src, inst.base.ty, dst_mcv, src_mcv, 7, 0x38);
1314 const info = inst.lhs.ty.intInfo(self.target.*);1443 const info = inst.lhs.ty.intInfo(self.target.*);
1315 if (info.signed) {1444 if (info.signed) {
1316 return MCValue{ .compare_flags_signed = op };1445 return MCValue{ .compare_flags_signed = op };
...@@ -1584,6 +1713,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1584,6 +1713,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1584 /// resulting REX is meaningful, but will remain the same if it is not.1713 /// resulting REX is meaningful, but will remain the same if it is not.
1585 /// * Deliberately inserting a "meaningless REX" requires explicit usage of1714 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
1586 /// 0x40, and cannot be done via this function.1715 /// 0x40, and cannot be done via this function.
1716 /// W => 64 bit mode
1717 /// R => extension to the MODRM.reg field
1718 /// X => extension to the SIB.index field
1719 /// B => extension to the MODRM.rm field or the SIB.base field
1587 fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {1720 fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
1588 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.1721 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
1589 var value: u8 = 0x40;1722 var value: u8 = 0x40;
...@@ -1681,27 +1814,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1681,27 +1814,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1681 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});1814 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
1682 },1815 },
1683 .register => |reg| {1816 .register => |reg| {
1684 const abi_size = ty.abiSize(self.target.*);1817 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
1685 const adj_off = stack_offset + abi_size;
1686 try self.code.ensureCapacity(self.code.items.len + 7);
1687 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1688 const reg_id: u8 = @truncate(u3, reg.id());
1689 if (adj_off <= 128) {
1690 // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
1691 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1692 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1693 const twos_comp = @bitCast(u8, negative_offset);
1694 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp });
1695 } else if (adj_off <= 2147483648) {
1696 // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
1697 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1698 const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
1699 const twos_comp = @bitCast(u32, negative_offset);
1700 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM });
1701 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1702 } else {
1703 return self.fail(src, "stack offset too large", .{});
1704 }
1705 },1818 },
1706 .memory => |vaddr| {1819 .memory => |vaddr| {
1707 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});1820 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
...@@ -1709,7 +1822,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1709,7 +1822,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1709 .stack_offset => |off| {1822 .stack_offset => |off| {
1710 if (stack_offset == off)1823 if (stack_offset == off)
1711 return; // Copy stack variable to itself; nothing to do.1824 return; // Copy stack variable to itself; nothing to do.
1712 return self.fail(src, "TODO implement copy stack variable to stack variable", .{});1825
1826 const reg = try self.copyToTmpRegister(src, mcv);
1827 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
1713 },1828 },
1714 },1829 },
1715 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),1830 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),
...@@ -2027,7 +2142,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2027,7 +2142,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2027 },2142 },
2028 });2143 });
2029 if (imm >= math.maxInt(U)) {2144 if (imm >= math.maxInt(U)) {
2030 return self.copyToNewRegister(inst);2145 return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) };
2031 }2146 }
2032 },2147 },
2033 else => {},2148 else => {},
test/stage2/test.zig+52
...@@ -600,6 +600,58 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -600,6 +600,58 @@ pub fn addCases(ctx: *TestContext) !void {
600 "",600 "",
601 );601 );
602602
603 // Spilling registers to the stack.
604 case.addCompareOutput(
605 \\export fn _start() noreturn {
606 \\ assert(add(3, 4) == 791);
607 \\
608 \\ exit();
609 \\}
610 \\
611 \\fn add(a: u32, b: u32) u32 {
612 \\ const x: u32 = blk: {
613 \\ const c = a + b; // 7
614 \\ const d = a + c; // 10
615 \\ const e = d + b; // 14
616 \\ const f = d + e; // 24
617 \\ const g = e + f; // 38
618 \\ const h = f + g; // 62
619 \\ const i = g + h; // 100
620 \\ const j = i + d; // 110
621 \\ const k = i + j; // 210
622 \\ const l = k + c; // 217
623 \\ const m = l + d; // 227
624 \\ const n = m + e; // 241
625 \\ const o = n + f; // 265
626 \\ const p = o + g; // 303
627 \\ const q = p + h; // 365
628 \\ const r = q + i; // 465
629 \\ const s = r + j; // 575
630 \\ const t = s + k; // 785
631 \\ break :blk t;
632 \\ };
633 \\ const y = x + a; // 788
634 \\ const z = y + a; // 791
635 \\ return z;
636 \\}
637 \\
638 \\pub fn assert(ok: bool) void {
639 \\ if (!ok) unreachable; // assertion failure
640 \\}
641 \\
642 \\fn exit() noreturn {
643 \\ asm volatile ("syscall"
644 \\ :
645 \\ : [number] "{rax}" (231),
646 \\ [arg1] "{rdi}" (0)
647 \\ : "rcx", "r11", "memory"
648 \\ );
649 \\ unreachable;
650 \\}
651 ,
652 "",
653 );
654
603 // Character literals and multiline strings.655 // Character literals and multiline strings.
604 case.addCompareOutput(656 case.addCompareOutput(
605 \\export fn _start() noreturn {657 \\export fn _start() noreturn {