| ... | @@ -11,8 +11,6 @@ const ErrorMsg = Module.ErrorMsg; | ... | @@ -11,8 +11,6 @@ const ErrorMsg = Module.ErrorMsg; |
| 11 | const Target = std.Target; | 11 | const Target = std.Target; |
| 12 | const Allocator = mem.Allocator; | 12 | const Allocator = mem.Allocator; |
| 13 | const trace = @import("tracy.zig").trace; | 13 | const trace = @import("tracy.zig").trace; |
| 14 | const x86_64 = @import("codegen/x86_64.zig"); | | |
| 15 | const x86 = @import("codegen/x86.zig"); | | |
| 16 | | 14 | |
| 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. |
| 18 | pub const BlockData = struct { | 16 | pub 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 { |
| 280 | | 278 | |
| 281 | const Branch = struct { | 279 | const Branch = struct { |
| 282 | inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{}, | 280 | inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{}, |
| 283 | | 281 | 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) = .{}, | | |
| 286 | | 283 | |
| 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, |
| 294 | | 291 | |
| | 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 | } |
| 615 | | 626 | |
| 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 | } |
| 621 | | 649 | |
| 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 | else | 769 | 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 | } |
| 951 | | 979 | |
| 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 | } |
| 1173 | | 1201 | |
| 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 | } |
| 1178 | | 1219 | |
| 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; |
| 1251 | | 1292 | |
| 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 | } |
| 1282 | | 1322 | |
| 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 | }; |
| 1288 | | 1337 | |
| 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 | } |