| ... | ... | @@ -11,8 +11,6 @@ const ErrorMsg = Module.ErrorMsg; |
| 11 | 11 | const Target = std.Target; |
| 12 | 12 | const Allocator = mem.Allocator; |
| 13 | 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 | 15 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. |
| 18 | 16 | pub const BlockData = struct { |
| ... | ... | @@ -232,7 +230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 232 | 230 | /// The constant was emitted into the code, at this offset. |
| 233 | 231 | embedded_in_code: usize, |
| 234 | 232 | /// The value is in a target-specific register. |
| 235 | | register: Reg, |
| 233 | register: Register, |
| 236 | 234 | /// The value is in memory at a hard-coded address. |
| 237 | 235 | memory: u64, |
| 238 | 236 | /// The value is one of the stack variables. |
| ... | ... | @@ -280,9 +278,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 280 | 278 | |
| 281 | 279 | const Branch = struct { |
| 282 | 280 | 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), |
| 286 | 283 | |
| 287 | 284 | /// Maps offset to what is stored there. |
| 288 | 285 | stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{}, |
| ... | ... | @@ -292,6 +289,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 292 | 289 | /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. |
| 293 | 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 | 306 | fn deinit(self: *Branch, gpa: *Allocator) void { |
| 296 | 307 | self.inst_table.deinit(gpa); |
| 297 | 308 | self.registers.deinit(gpa); |
| ... | ... | @@ -516,7 +527,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 516 | 527 | // Both operands cannot be memory. |
| 517 | 528 | src_inst = op_rhs; |
| 518 | 529 | if (lhs.isMemory() and rhs.isMemory()) { |
| 519 | | dst_mcv = try self.moveToNewRegister(op_lhs); |
| 530 | dst_mcv = try self.copyToNewRegister(op_lhs); |
| 520 | 531 | src_mcv = rhs; |
| 521 | 532 | } else { |
| 522 | 533 | dst_mcv = lhs; |
| ... | ... | @@ -527,7 +538,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 527 | 538 | // Both operands cannot be memory. |
| 528 | 539 | src_inst = op_lhs; |
| 529 | 540 | if (lhs.isMemory() and rhs.isMemory()) { |
| 530 | | dst_mcv = try self.moveToNewRegister(op_rhs); |
| 541 | dst_mcv = try self.copyToNewRegister(op_rhs); |
| 531 | 542 | src_mcv = lhs; |
| 532 | 543 | } else { |
| 533 | 544 | dst_mcv = rhs; |
| ... | ... | @@ -535,11 +546,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 535 | 546 | } |
| 536 | 547 | } else { |
| 537 | 548 | if (lhs.isMemory()) { |
| 538 | | dst_mcv = try self.moveToNewRegister(op_lhs); |
| 549 | dst_mcv = try self.copyToNewRegister(op_lhs); |
| 539 | 550 | src_mcv = rhs; |
| 540 | 551 | src_inst = op_rhs; |
| 541 | 552 | } else { |
| 542 | | dst_mcv = try self.moveToNewRegister(op_rhs); |
| 553 | dst_mcv = try self.copyToNewRegister(op_rhs); |
| 543 | 554 | src_mcv = lhs; |
| 544 | 555 | src_inst = op_lhs; |
| 545 | 556 | } |
| ... | ... | @@ -552,7 +563,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 552 | 563 | switch (src_mcv) { |
| 553 | 564 | .immediate => |imm| { |
| 554 | 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 | 569 | else => {}, |
| ... | ... | @@ -614,9 +625,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 614 | 625 | } |
| 615 | 626 | |
| 616 | 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 | 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 | 650 | fn genBreakpoint(self: *Self, src: usize) !MCValue { |
| ... | ... | @@ -737,7 +765,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 737 | 765 | // Either one, but not both, can be a memory operand. |
| 738 | 766 | // Source operand can be an immediate, 8 bits or 32 bits. |
| 739 | 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 | 769 | else |
| 742 | 770 | lhs; |
| 743 | 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 | 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 | 981 | switch (arch) { |
| 954 | 982 | .x86_64 => switch (mcv) { |
| 955 | 983 | .dead => unreachable, |
| ... | ... | @@ -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 | 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 | 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 | 1235 | }, |
| 1195 | 1236 | }); |
| 1196 | 1237 | if (imm >= std.math.maxInt(U)) { |
| 1197 | | return self.moveToNewRegister(inst); |
| 1238 | return self.copyToNewRegister(inst); |
| 1198 | 1239 | } |
| 1199 | 1240 | }, |
| 1200 | 1241 | else => {}, |
| ... | ... | @@ -1249,15 +1290,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1249 | 1290 | var next_int_reg: usize = 0; |
| 1250 | 1291 | var next_stack_offset: u32 = 0; |
| 1251 | 1292 | |
| 1252 | | const integer_registers = [_]Reg{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; |
| 1253 | 1293 | for (param_types) |ty, i| { |
| 1254 | 1294 | switch (ty.zigTypeTag()) { |
| 1255 | 1295 | .Bool, .Int => { |
| 1256 | | if (next_int_reg >= integer_registers.len) { |
| 1296 | if (next_int_reg >= c_abi_int_param_regs.len) { |
| 1257 | 1297 | results[i] = .{ .stack_offset = next_stack_offset }; |
| 1258 | 1298 | next_stack_offset += @intCast(u32, ty.abiSize(self.target.*)); |
| 1259 | 1299 | } else { |
| 1260 | | results[i] = .{ .register = integer_registers[next_int_reg] }; |
| 1300 | results[i] = .{ .register = c_abi_int_param_regs[next_int_reg] }; |
| 1261 | 1301 | next_int_reg += 1; |
| 1262 | 1302 | } |
| 1263 | 1303 | }, |
| ... | ... | @@ -1280,14 +1320,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1280 | 1320 | return error.CodegenFail; |
| 1281 | 1321 | } |
| 1282 | 1322 | |
| 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 | }, |
| 1287 | 1336 | }; |
| 1288 | 1337 | |
| 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); |
| 1291 | 1343 | } |
| 1292 | 1344 | }; |
| 1293 | 1345 | } |