authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-17 15:51:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-20 13:12:20-07:00
log896472c20e33c81a010b21a6f900e721a2cf0839
treeda48b73a29bb57001d6e0ab87fe7e9f0fcb01712
parentef9aeb6ac415348e16f04913839002929064c91e

stage2: implement register copying


4 files changed, 124 insertions(+), 36 deletions(-)

src-self-hosted/codegen.zig+79-27
......@@ -11,8 +11,6 @@ const ErrorMsg = Module.ErrorMsg;
1111const Target = std.Target;
1212const Allocator = mem.Allocator;
1313const trace = @import("tracy.zig").trace;
14const x86_64 = @import("codegen/x86_64.zig");
15const x86 = @import("codegen/x86.zig");
1614
1715/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
1816pub const BlockData = struct {
......@@ -232,7 +230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
232230 /// The constant was emitted into the code, at this offset.
233231 embedded_in_code: usize,
234232 /// The value is in a target-specific register.
235 register: Reg,
233 register: Register,
236234 /// The value is in memory at a hard-coded address.
237235 memory: u64,
238236 /// The value is one of the stack variables.
......@@ -280,9 +278,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
280278
281279 const Branch = struct {
282280 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
283
284 /// The key is an enum value of an arch-specific register.
285 registers: std.AutoHashMapUnmanaged(usize, RegisterAllocation) = .{},
281 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
282 free_registers: FreeRegInt = std.math.maxInt(FreeRegInt),
286283
287284 /// Maps offset to what is stored there.
288285 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
......@@ -292,6 +289,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
292289 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
293290 next_stack_offset: u32 = 0,
294291
292 fn markRegUsed(self: *Branch, reg: Register) void {
293 const index = reg.allocIndex() orelse return;
294 const ShiftInt = std.math.Log2Int(FreeRegInt);
295 const shift = @intCast(ShiftInt, index);
296 self.free_registers &= ~(@as(FreeRegInt, 1) << shift);
297 }
298
299 fn markRegFree(self: *Branch, reg: Register) void {
300 const index = reg.allocIndex() orelse return;
301 const ShiftInt = std.math.Log2Int(FreeRegInt);
302 const shift = @intCast(ShiftInt, index);
303 self.free_registers |= @as(FreeRegInt, 1) << shift;
304 }
305
295306 fn deinit(self: *Branch, gpa: *Allocator) void {
296307 self.inst_table.deinit(gpa);
297308 self.registers.deinit(gpa);
......@@ -516,7 +527,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
516527 // Both operands cannot be memory.
517528 src_inst = op_rhs;
518529 if (lhs.isMemory() and rhs.isMemory()) {
519 dst_mcv = try self.moveToNewRegister(op_lhs);
530 dst_mcv = try self.copyToNewRegister(op_lhs);
520531 src_mcv = rhs;
521532 } else {
522533 dst_mcv = lhs;
......@@ -527,7 +538,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
527538 // Both operands cannot be memory.
528539 src_inst = op_lhs;
529540 if (lhs.isMemory() and rhs.isMemory()) {
530 dst_mcv = try self.moveToNewRegister(op_rhs);
541 dst_mcv = try self.copyToNewRegister(op_rhs);
531542 src_mcv = lhs;
532543 } else {
533544 dst_mcv = rhs;
......@@ -535,11 +546,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
535546 }
536547 } else {
537548 if (lhs.isMemory()) {
538 dst_mcv = try self.moveToNewRegister(op_lhs);
549 dst_mcv = try self.copyToNewRegister(op_lhs);
539550 src_mcv = rhs;
540551 src_inst = op_rhs;
541552 } else {
542 dst_mcv = try self.moveToNewRegister(op_rhs);
553 dst_mcv = try self.copyToNewRegister(op_rhs);
543554 src_mcv = lhs;
544555 src_inst = op_lhs;
545556 }
......@@ -552,7 +563,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
552563 switch (src_mcv) {
553564 .immediate => |imm| {
554565 if (imm > std.math.maxInt(u31)) {
555 src_mcv = try self.moveToNewRegister(src_inst);
566 src_mcv = try self.copyToNewRegister(src_inst);
556567 }
557568 },
558569 else => {},
......@@ -614,9 +625,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
614625 }
615626
616627 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
617 const i = self.arg_index;
628 if (FreeRegInt == u0) {
629 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
630 }
631 if (inst.base.isUnused())
632 return MCValue.dead;
633
634 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
635 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
636
637 const result = self.args[self.arg_index];
618638 self.arg_index += 1;
619 return self.args[i];
639
640 switch (result) {
641 .register => |reg| {
642 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });
643 branch.markRegUsed(reg);
644 },
645 else => {},
646 }
647 return result;
620648 }
621649
622650 fn genBreakpoint(self: *Self, src: usize) !MCValue {
......@@ -737,7 +765,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
737765 // Either one, but not both, can be a memory operand.
738766 // Source operand can be an immediate, 8 bits or 32 bits.
739767 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
740 try self.moveToNewRegister(inst.args.lhs)
768 try self.copyToNewRegister(inst.args.lhs)
741769 else
742770 lhs;
743771 // This instruction supports only signed 32-bit immediates at most.
......@@ -949,7 +977,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
949977 }
950978 }
951979
952 fn genSetReg(self: *Self, src: usize, reg: Reg, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
980 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
953981 switch (arch) {
954982 .x86_64 => switch (mcv) {
955983 .dead => unreachable,
......@@ -1171,9 +1199,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11711199 }
11721200 }
11731201
1174 fn moveToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
1202 /// Does not "move" the instruction.
1203 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
11751204 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1176 return self.fail(inst.src, "TODO implement moveToNewRegister", .{});
1205 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
1206 try branch.inst_table.ensureCapacity(self.gpa, branch.inst_table.items().len + 1);
1207
1208 const free_index = @ctz(FreeRegInt, branch.free_registers);
1209 if (free_index >= callee_preserved_regs.len)
1210 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
1211 branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
1212 const reg = callee_preserved_regs[free_index];
1213 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
1214 const old_mcv = branch.inst_table.get(inst).?;
1215 const new_mcv: MCValue = .{ .register = reg };
1216 try self.genSetReg(inst.src, reg, old_mcv);
1217 return new_mcv;
11771218 }
11781219
11791220 /// If the MCValue is an immediate, and it does not fit within this type,
......@@ -1194,7 +1235,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11941235 },
11951236 });
11961237 if (imm >= std.math.maxInt(U)) {
1197 return self.moveToNewRegister(inst);
1238 return self.copyToNewRegister(inst);
11981239 }
11991240 },
12001241 else => {},
......@@ -1249,15 +1290,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12491290 var next_int_reg: usize = 0;
12501291 var next_stack_offset: u32 = 0;
12511292
1252 const integer_registers = [_]Reg{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
12531293 for (param_types) |ty, i| {
12541294 switch (ty.zigTypeTag()) {
12551295 .Bool, .Int => {
1256 if (next_int_reg >= integer_registers.len) {
1296 if (next_int_reg >= c_abi_int_param_regs.len) {
12571297 results[i] = .{ .stack_offset = next_stack_offset };
12581298 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
12591299 } else {
1260 results[i] = .{ .register = integer_registers[next_int_reg] };
1300 results[i] = .{ .register = c_abi_int_param_regs[next_int_reg] };
12611301 next_int_reg += 1;
12621302 }
12631303 },
......@@ -1280,14 +1320,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12801320 return error.CodegenFail;
12811321 }
12821322
1283 const Reg = switch (arch) {
1284 .i386 => x86.Register,
1285 .x86_64 => x86_64.Register,
1286 else => enum { dummy },
1323 usingnamespace switch (arch) {
1324 .i386 => @import("codegen/x86.zig"),
1325 .x86_64 => @import("codegen/x86_64.zig"),
1326 else => struct {
1327 pub const Register = enum {
1328 dummy,
1329
1330 pub fn allocIndex(self: Register) ?u4 {
1331 return null;
1332 }
1333 };
1334 pub const callee_preserved_regs = [_]Register{};
1335 },
12871336 };
12881337
1289 fn parseRegName(name: []const u8) ?Reg {
1290 return std.meta.stringToEnum(Reg, name);
1338 /// An integer whose bits represent all the registers and whether they are free.
1339 const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });
1340
1341 fn parseRegName(name: []const u8) ?Register {
1342 return std.meta.stringToEnum(Register, name);
12911343 }
12921344 };
12931345}
src-self-hosted/codegen/x86.zig+14
......@@ -25,6 +25,20 @@ pub const Register = enum(u8) {
2525 pub fn id(self: @This()) u3 {
2626 return @truncate(u3, @enumToInt(self));
2727 }
28
29 /// Returns the index into `callee_preserved_regs`.
30 pub fn allocIndex(self: Register) ?u4 {
31 return switch (self) {
32 .eax, .ax, .al => 0,
33 .ecx, .cx, .cl => 1,
34 .edx, .dx, .dl => 2,
35 .esi, .si => 3,
36 .edi, .di => 4,
37 else => null,
38 };
39 }
2840};
2941
3042// zig fmt: on
43
44pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };
src-self-hosted/codegen/x86_64.zig+21-4
......@@ -38,7 +38,7 @@ pub const Register = enum(u8) {
3838 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
3939
4040 /// Returns the bit-width of the register.
41 pub fn size(self: @This()) u7 {
41 pub fn size(self: Register) u7 {
4242 return switch (@enumToInt(self)) {
4343 0...15 => 64,
4444 16...31 => 32,
......@@ -53,7 +53,7 @@ pub const Register = enum(u8) {
5353 /// other variant of access to those registers, such as r8b, r15d, and so
5454 /// on. This is needed because access to these registers requires special
5555 /// handling via the REX prefix, via the B or R bits, depending on context.
56 pub fn isExtended(self: @This()) bool {
56 pub fn isExtended(self: Register) bool {
5757 return @enumToInt(self) & 0x08 != 0;
5858 }
5959
......@@ -62,12 +62,29 @@ pub const Register = enum(u8) {
6262 /// an instruction (@see isExtended), and requires special handling. The
6363 /// lower three bits are often embedded directly in instructions (such as
6464 /// the B8 variant of moves), or used in R/M bytes.
65 pub fn id(self: @This()) u4 {
65 pub fn id(self: Register) u4 {
6666 return @truncate(u4, @enumToInt(self));
6767 }
68
69 /// Returns the index into `callee_preserved_regs`.
70 pub fn allocIndex(self: Register) ?u4 {
71 return switch (self) {
72 .rax, .eax, .ax, .al => 0,
73 .rcx, .ecx, .cx, .cl => 1,
74 .rdx, .edx, .dx, .dl => 2,
75 .rsi, .esi, .si => 3,
76 .rdi, .edi, .di => 4,
77 .r8, .r8d, .r8w, .r8b => 5,
78 .r9, .r9d, .r9w, .r9b => 6,
79 .r10, .r10d, .r10w, .r10b => 7,
80 .r11, .r11d, .r11w, .r11b => 8,
81 else => null,
82 };
83 }
6884};
6985
7086// zig fmt: on
7187
7288/// These registers belong to the called function.
73pub const callee_preserved = [_]Register{ rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11 };
89pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
90pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
test/stage2/compare_output.zig+10-5
......@@ -169,9 +169,8 @@ pub fn addCases(ctx: *TestContext) !void {
169169 ,
170170 "",
171171 );
172 }
173 {
174 var case = ctx.exe("assert function", linux_x64);
172
173 // Tests the assert() function.
175174 case.addCompareOutput(
176175 \\export fn _start() noreturn {
177176 \\ add(3, 4);
......@@ -199,15 +198,21 @@ pub fn addCases(ctx: *TestContext) !void {
199198 ,
200199 "",
201200 );
201
202 // Tests copying a register. For the `c = a + b`, it has to
203 // preserve both a and b, because they are both used later.
202204 case.addCompareOutput(
203205 \\export fn _start() noreturn {
204 \\ add(100, 200);
206 \\ add(3, 4);
205207 \\
206208 \\ exit();
207209 \\}
208210 \\
209211 \\fn add(a: u32, b: u32) void {
210 \\ assert(a + b == 300);
212 \\ const c = a + b; // 7
213 \\ const d = a + c; // 10
214 \\ const e = d + b; // 14
215 \\ assert(e == 14);
211216 \\}
212217 \\
213218 \\pub fn assert(ok: bool) void {