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;...@@ -11,8 +11,6 @@ const ErrorMsg = Module.ErrorMsg;
11const Target = std.Target;11const Target = std.Target;
12const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
13const trace = @import("tracy.zig").trace;13const trace = @import("tracy.zig").trace;
14const x86_64 = @import("codegen/x86_64.zig");
15const x86 = @import("codegen/x86.zig");
1614
17/// The codegen-related data that is stored in `ir.Inst.Block` instructions.15/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
18pub const BlockData = struct {16pub const BlockData = struct {
...@@ -232,7 +230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -232,7 +230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
232 /// The constant was emitted into the code, at this offset.230 /// The constant was emitted into the code, at this offset.
233 embedded_in_code: usize,231 embedded_in_code: usize,
234 /// The value is in a target-specific register.232 /// The value is in a target-specific register.
235 register: Reg,233 register: Register,
236 /// The value is in memory at a hard-coded address.234 /// The value is in memory at a hard-coded address.
237 memory: u64,235 memory: u64,
238 /// The value is one of the stack variables.236 /// The value is one of the stack variables.
...@@ -280,9 +278,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -280,9 +278,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
280278
281 const Branch = struct {279 const Branch = struct {
282 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},280 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
283281 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
284 /// The key is an enum value of an arch-specific register.282 free_registers: FreeRegInt = std.math.maxInt(FreeRegInt),
285 registers: std.AutoHashMapUnmanaged(usize, RegisterAllocation) = .{},
286283
287 /// Maps offset to what is stored there.284 /// Maps offset to what is stored there.
288 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},285 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
...@@ -292,6 +289,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -292,6 +289,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
292 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.289 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
293 next_stack_offset: u32 = 0,290 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
295 fn deinit(self: *Branch, gpa: *Allocator) void {306 fn deinit(self: *Branch, gpa: *Allocator) void {
296 self.inst_table.deinit(gpa);307 self.inst_table.deinit(gpa);
297 self.registers.deinit(gpa);308 self.registers.deinit(gpa);
...@@ -516,7 +527,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -516,7 +527,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
516 // Both operands cannot be memory.527 // Both operands cannot be memory.
517 src_inst = op_rhs;528 src_inst = op_rhs;
518 if (lhs.isMemory() and rhs.isMemory()) {529 if (lhs.isMemory() and rhs.isMemory()) {
519 dst_mcv = try self.moveToNewRegister(op_lhs);530 dst_mcv = try self.copyToNewRegister(op_lhs);
520 src_mcv = rhs;531 src_mcv = rhs;
521 } else {532 } else {
522 dst_mcv = lhs;533 dst_mcv = lhs;
...@@ -527,7 +538,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -527,7 +538,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
527 // Both operands cannot be memory.538 // Both operands cannot be memory.
528 src_inst = op_lhs;539 src_inst = op_lhs;
529 if (lhs.isMemory() and rhs.isMemory()) {540 if (lhs.isMemory() and rhs.isMemory()) {
530 dst_mcv = try self.moveToNewRegister(op_rhs);541 dst_mcv = try self.copyToNewRegister(op_rhs);
531 src_mcv = lhs;542 src_mcv = lhs;
532 } else {543 } else {
533 dst_mcv = rhs;544 dst_mcv = rhs;
...@@ -535,11 +546,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -535,11 +546,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
535 }546 }
536 } else {547 } else {
537 if (lhs.isMemory()) {548 if (lhs.isMemory()) {
538 dst_mcv = try self.moveToNewRegister(op_lhs);549 dst_mcv = try self.copyToNewRegister(op_lhs);
539 src_mcv = rhs;550 src_mcv = rhs;
540 src_inst = op_rhs;551 src_inst = op_rhs;
541 } else {552 } else {
542 dst_mcv = try self.moveToNewRegister(op_rhs);553 dst_mcv = try self.copyToNewRegister(op_rhs);
543 src_mcv = lhs;554 src_mcv = lhs;
544 src_inst = op_lhs;555 src_inst = op_lhs;
545 }556 }
...@@ -552,7 +563,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -552,7 +563,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
552 switch (src_mcv) {563 switch (src_mcv) {
553 .immediate => |imm| {564 .immediate => |imm| {
554 if (imm > std.math.maxInt(u31)) {565 if (imm > std.math.maxInt(u31)) {
555 src_mcv = try self.moveToNewRegister(src_inst);566 src_mcv = try self.copyToNewRegister(src_inst);
556 }567 }
557 },568 },
558 else => {},569 else => {},
...@@ -614,9 +625,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -614,9 +625,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
614 }625 }
615626
616 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {627 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];
618 self.arg_index += 1;638 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;
620 }648 }
621649
622 fn genBreakpoint(self: *Self, src: usize) !MCValue {650 fn genBreakpoint(self: *Self, src: usize) !MCValue {
...@@ -737,7 +765,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -737,7 +765,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
737 // Either one, but not both, can be a memory operand.765 // Either one, but not both, can be a memory operand.
738 // Source operand can be an immediate, 8 bits or 32 bits.766 // Source operand can be an immediate, 8 bits or 32 bits.
739 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))767 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)
741 else769 else
742 lhs;770 lhs;
743 // This instruction supports only signed 32-bit immediates at most.771 // This instruction supports only signed 32-bit immediates at most.
...@@ -949,7 +977,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -949,7 +977,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
949 }977 }
950 }978 }
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 {
953 switch (arch) {981 switch (arch) {
954 .x86_64 => switch (mcv) {982 .x86_64 => switch (mcv) {
955 .dead => unreachable,983 .dead => unreachable,
...@@ -1171,9 +1199,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1171,9 +1199,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1171 }1199 }
1172 }1200 }
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 {
1175 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1204 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;
1177 }1218 }
11781219
1179 /// If the MCValue is an immediate, and it does not fit within this type,1220 /// 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 {...@@ -1194,7 +1235,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1194 },1235 },
1195 });1236 });
1196 if (imm >= std.math.maxInt(U)) {1237 if (imm >= std.math.maxInt(U)) {
1197 return self.moveToNewRegister(inst);1238 return self.copyToNewRegister(inst);
1198 }1239 }
1199 },1240 },
1200 else => {},1241 else => {},
...@@ -1249,15 +1290,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1249,15 +1290,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1249 var next_int_reg: usize = 0;1290 var next_int_reg: usize = 0;
1250 var next_stack_offset: u32 = 0;1291 var next_stack_offset: u32 = 0;
12511292
1252 const integer_registers = [_]Reg{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
1253 for (param_types) |ty, i| {1293 for (param_types) |ty, i| {
1254 switch (ty.zigTypeTag()) {1294 switch (ty.zigTypeTag()) {
1255 .Bool, .Int => {1295 .Bool, .Int => {
1256 if (next_int_reg >= integer_registers.len) {1296 if (next_int_reg >= c_abi_int_param_regs.len) {
1257 results[i] = .{ .stack_offset = next_stack_offset };1297 results[i] = .{ .stack_offset = next_stack_offset };
1258 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));1298 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1259 } else {1299 } else {
1260 results[i] = .{ .register = integer_registers[next_int_reg] };1300 results[i] = .{ .register = c_abi_int_param_regs[next_int_reg] };
1261 next_int_reg += 1;1301 next_int_reg += 1;
1262 }1302 }
1263 },1303 },
...@@ -1280,14 +1320,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1280,14 +1320,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1280 return error.CodegenFail;1320 return error.CodegenFail;
1281 }1321 }
12821322
1283 const Reg = switch (arch) {1323 usingnamespace switch (arch) {
1284 .i386 => x86.Register,1324 .i386 => @import("codegen/x86.zig"),
1285 .x86_64 => x86_64.Register,1325 .x86_64 => @import("codegen/x86_64.zig"),
1286 else => enum { dummy },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 },
1287 };1336 };
12881337
1289 fn parseRegName(name: []const u8) ?Reg {1338 /// An integer whose bits represent all the registers and whether they are free.
1290 return std.meta.stringToEnum(Reg, name);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);
1291 }1343 }
1292 };1344 };
1293}1345}
src-self-hosted/codegen/x86.zig+14
...@@ -25,6 +25,20 @@ pub const Register = enum(u8) {...@@ -25,6 +25,20 @@ pub const Register = enum(u8) {
25 pub fn id(self: @This()) u3 {25 pub fn id(self: @This()) u3 {
26 return @truncate(u3, @enumToInt(self));26 return @truncate(u3, @enumToInt(self));
27 }27 }
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 }
28};40};
2941
30// zig fmt: on42// 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) {...@@ -38,7 +38,7 @@ pub const Register = enum(u8) {
38 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,38 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
3939
40 /// Returns the bit-width of the register.40 /// Returns the bit-width of the register.
41 pub fn size(self: @This()) u7 {41 pub fn size(self: Register) u7 {
42 return switch (@enumToInt(self)) {42 return switch (@enumToInt(self)) {
43 0...15 => 64,43 0...15 => 64,
44 16...31 => 32,44 16...31 => 32,
...@@ -53,7 +53,7 @@ pub const Register = enum(u8) {...@@ -53,7 +53,7 @@ pub const Register = enum(u8) {
53 /// other variant of access to those registers, such as r8b, r15d, and so53 /// other variant of access to those registers, such as r8b, r15d, and so
54 /// on. This is needed because access to these registers requires special54 /// on. This is needed because access to these registers requires special
55 /// handling via the REX prefix, via the B or R bits, depending on context.55 /// 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 {
57 return @enumToInt(self) & 0x08 != 0;57 return @enumToInt(self) & 0x08 != 0;
58 }58 }
5959
...@@ -62,12 +62,29 @@ pub const Register = enum(u8) {...@@ -62,12 +62,29 @@ pub const Register = enum(u8) {
62 /// an instruction (@see isExtended), and requires special handling. The62 /// an instruction (@see isExtended), and requires special handling. The
63 /// lower three bits are often embedded directly in instructions (such as63 /// lower three bits are often embedded directly in instructions (such as
64 /// the B8 variant of moves), or used in R/M bytes.64 /// 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 {
66 return @truncate(u4, @enumToInt(self));66 return @truncate(u4, @enumToInt(self));
67 }67 }
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 }
68};84};
6985
70// zig fmt: on86// zig fmt: on
7187
72/// These registers belong to the called function.88/// 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 {...@@ -169,9 +169,8 @@ pub fn addCases(ctx: *TestContext) !void {
169 ,169 ,
170 "",170 "",
171 );171 );
172 }172
173 {173 // Tests the assert() function.
174 var case = ctx.exe("assert function", linux_x64);
175 case.addCompareOutput(174 case.addCompareOutput(
176 \\export fn _start() noreturn {175 \\export fn _start() noreturn {
177 \\ add(3, 4);176 \\ add(3, 4);
...@@ -199,15 +198,21 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -199,15 +198,21 @@ pub fn addCases(ctx: *TestContext) !void {
199 ,198 ,
200 "",199 "",
201 );200 );
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.
202 case.addCompareOutput(204 case.addCompareOutput(
203 \\export fn _start() noreturn {205 \\export fn _start() noreturn {
204 \\ add(100, 200);206 \\ add(3, 4);
205 \\207 \\
206 \\ exit();208 \\ exit();
207 \\}209 \\}
208 \\210 \\
209 \\fn add(a: u32, b: u32) void {211 \\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);
211 \\}216 \\}
212 \\217 \\
213 \\pub fn assert(ok: bool) void {218 \\pub fn assert(ok: bool) void {