From 2aee2302515ba444999b82c2e40cbc35dee08baf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 14 May 2022 01:50:07 +0200 Subject: [PATCH] x64: add unordered cmp with EFLAGS --- src/arch/x86_64/CodeGen.zig | 133 ++++++++++++- src/arch/x86_64/Emit.zig | 217 +++++++++++++++++++- src/arch/x86_64/Mir.zig | 11 +- src/register_manager.zig | 386 ++++++++++++++++++------------------ 4 files changed, 538 insertions(+), 209 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d0bed75fbd..e81f2d5435 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -202,6 +202,7 @@ pub const MCValue = union(enum) { fn isRegister(mcv: MCValue) bool { return switch (mcv) { .register => true, + .avx_register => true, else => false, }; } @@ -971,6 +972,7 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { .compare_flags_signed, .compare_flags_unsigned, => try self.allocRegOrMem(inst_to_save, true), + .avx_register => try self.allocRegOrMem(inst_to_save, false), else => unreachable, }; @@ -988,6 +990,7 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { .register_overflow_signed, .register_overflow_unsigned, => |reg| self.register_manager.freeReg(reg), + .avx_register => |reg| self.avx_register_manager.freeReg(reg), else => {}, } } @@ -2497,7 +2500,6 @@ fn reuseOperand( fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { const elem_ty = ptr_ty.elemType(); const abi_size = elem_ty.abiSize(self.target.*); - std.log.warn("{} => {}, {}", .{ ptr_ty.fmtDebug(), ptr, dst_mcv }); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -2627,7 +2629,6 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { const abi_size = value_ty.abiSize(self.target.*); - std.log.warn("{} => {}, {} => {}", .{ ptr_ty.fmtDebug(), ptr, value_ty.fmtDebug(), value }); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -3375,13 +3376,39 @@ fn genBinOp( const rhs = try self.resolveInst(rhs_air); const lhs_ty = self.air.typeOf(lhs_air); const rhs_ty = self.air.typeOf(rhs_air); - if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { + if (lhs_ty.zigTypeTag() == .Vector) { return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } if (lhs_ty.abiSize(self.target.*) > 8) { return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } + if (lhs_ty.zigTypeTag() == .Float) { + switch (tag) { + .add => { + const dst_reg: AvxRegister = blk: { + const reg = try self.avx_register_manager.allocReg(null); + try self.genSetAvxReg(lhs_ty, reg, lhs); + break :blk reg.to128(); + }; + const dst_lock = self.avx_register_manager.lockRegAssumeUnused(dst_reg); + defer self.avx_register_manager.unlockReg(dst_lock); + + const src_reg: AvxRegister = blk: { + const reg = try self.avx_register_manager.allocReg(null); + try self.genSetAvxReg(lhs_ty, reg, rhs); + break :blk reg.to128(); + }; + const src_lock = self.avx_register_manager.lockRegAssumeUnused(src_reg); + defer self.avx_register_manager.unlockReg(src_lock); + + try self.genBinOpMir(.add_f64, lhs_ty, .{ .avx_register = dst_reg }, .{ .avx_register = src_reg }); + return MCValue{ .avx_register = dst_reg }; + }, + else => unreachable, + } + } + const is_commutative: bool = switch (tag) { .add, .addwrap, @@ -3550,8 +3577,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }, } }, - .avx_register => { - return self.fail("TODO genBinOp for AVX register", .{}); + .avx_register => |dst_reg| { + switch (src_mcv) { + .avx_register => |src_reg| { + switch (dst_ty.zigTypeTag()) { + .Float => switch (dst_ty.tag()) { + .f64 => { + _ = try self.addInst(.{ + .tag = mir_tag, + .ops = (Mir.Ops(AvxRegister, AvxRegister){ + .reg1 = dst_reg.to128(), + .reg2 = src_reg.to128(), + }).encode(), + .data = undefined, + }); + }, + else => return self.fail("TODO genBinOp for AVX register and type {}", .{dst_ty.fmtDebug()}), + }, + else => return self.fail("TODO genBinOp for AVX register and type {}", .{dst_ty.fmtDebug()}), + } + }, + else => return self.fail("TODO genBinOp for AVX register", .{}), + } }, .ptr_stack_offset, .stack_offset => |off| { if (off > math.maxInt(i32)) { @@ -4209,6 +4256,37 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { self.compare_flags_inst = inst; const result: MCValue = result: { + if (ty.zigTypeTag() == .Float) { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const dst_reg: AvxRegister = blk: { + const reg = try self.avx_register_manager.allocReg(null); + try self.genSetAvxReg(ty, reg, lhs); + break :blk reg.to128(); + }; + const dst_lock = self.avx_register_manager.lockRegAssumeUnused(dst_reg); + defer self.avx_register_manager.unlockReg(dst_lock); + + const src_reg: AvxRegister = blk: { + const reg = try self.avx_register_manager.allocReg(null); + try self.genSetAvxReg(ty, reg, rhs); + break :blk reg.to128(); + }; + const src_lock = self.avx_register_manager.lockRegAssumeUnused(src_reg); + defer self.avx_register_manager.unlockReg(src_lock); + + _ = try self.addInst(.{ + .tag = .cmp_f64, + .ops = (Mir.Ops(AvxRegister, AvxRegister){ + .reg1 = dst_reg, + .reg2 = src_reg, + }).encode(), + .data = undefined, + }); + + break :result MCValue{ .compare_flags_unsigned = op }; + } // There are 2 operands, destination and source. // Either one, but not both, can be a memory operand. // Source operand can be an immediate, 8 bits or 32 bits. @@ -5962,6 +6040,51 @@ fn genSetAvxReg(self: *Self, ty: Type, reg: AvxRegister, mcv: MCValue) InnerErro else => return self.fail("TODO genSetAvxReg from stack offset for type {}", .{ty.fmtDebug()}), } }, + .avx_register => |src_reg| { + switch (ty.zigTypeTag()) { + .Float => { + switch (ty.tag()) { + .f32 => return self.fail("TODO genSetAvxReg from register for f32", .{}), + .f64 => { + _ = try self.addInst(.{ + .tag = .mov_f64, + .ops = (Mir.Ops(AvxRegister, AvxRegister){ + .reg1 = reg.to128(), + .reg2 = src_reg.to128(), + .flags = 0b10, + }).encode(), + .data = undefined, + }); + }, + else => return self.fail("TODO genSetAvxReg from register for {}", .{ty.fmtDebug()}), + } + }, + else => return self.fail("TODO genSetAvxReg from register for type {}", .{ty.fmtDebug()}), + } + }, + .memory => { + switch (ty.zigTypeTag()) { + .Float => { + switch (ty.tag()) { + .f32 => return self.fail("TODO genSetAvxReg from memory for f32", .{}), + .f64 => { + const base_reg = try self.register_manager.allocReg(null); + try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); + _ = try self.addInst(.{ + .tag = .mov_f64, + .ops = (Mir.Ops(AvxRegister, Register){ + .reg1 = reg.to128(), + .reg2 = base_reg.to64(), + }).encode(), + .data = .{ .imm = 0 }, + }); + }, + else => return self.fail("TODO genSetAvxReg from memory for {}", .{ty.fmtDebug()}), + } + }, + else => return self.fail("TODO genSetAvxReg from memory for type {}", .{ty.fmtDebug()}), + } + }, else => |other| { return self.fail("TODO genSetAvxReg from {}", .{other}); }, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 80034ab536..79341df8cd 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -186,6 +186,10 @@ pub fn lowerMir(emit: *Emit) InnerError!void { // AVX instructions .mov_f64 => try emit.mirMovF64(inst), + .add_f64 => try emit.mirAddF64(inst), + + .cmp_f64 => try emit.mirCmpF64(inst), + // Pseudo-instructions .call_extern => try emit.mirCallExtern(inst), @@ -960,11 +964,11 @@ fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { assert(tag == .mov_f64); const ops = emit.mir.instructions.items(.ops)[inst]; const flags = @truncate(u2, ops); - const imm = emit.mir.instructions.items(.data)[inst].imm; switch (flags) { 0b00 => { const decoded = Mir.Ops(AvxRegister, GpRegister).decode(ops); + const imm = emit.mir.instructions.items(.data)[inst].imm; return lowerToRmEnc(.vmovsd, Register.avxReg(decoded.reg1), RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, .base = decoded.reg2, @@ -972,11 +976,63 @@ fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { }, 0b01 => { const decoded = Mir.Ops(GpRegister, AvxRegister).decode(ops); + const imm = emit.mir.instructions.items(.data)[inst].imm; return lowerToMrEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, .base = decoded.reg1, }), Register.avxReg(decoded.reg2), emit.code); }, + 0b10 => { + const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); + return lowerToRvmEnc( + .vmovsd, + Register.avxReg(decoded.reg1), + Register.avxReg(decoded.reg1), + RegisterOrMemory.avxReg(decoded.reg2), + emit.code, + ); + }, + else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), + } +} + +fn mirAddF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .add_f64); + const ops = emit.mir.instructions.items(.ops)[inst]; + const flags = @truncate(u2, ops); + + switch (flags) { + 0b00 => { + const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); + return lowerToRvmEnc( + .vaddsd, + Register.avxReg(decoded.reg1), + Register.avxReg(decoded.reg1), + RegisterOrMemory.avxReg(decoded.reg2), + emit.code, + ); + }, + else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), + } +} + +fn mirCmpF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .cmp_f64); + const ops = emit.mir.instructions.items(.ops)[inst]; + const flags = @truncate(u2, ops); + + switch (flags) { + 0b00 => { + const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); + return lowerToRmEnc( + .vucomisd, + Register.avxReg(decoded.reg1), + RegisterOrMemory.avxReg(decoded.reg2), + emit.code, + ); + }, else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), } } @@ -1217,6 +1273,9 @@ const Tag = enum { cmovb, cmovnae, vmovsd, + vaddsd, + vcmpsd, + vucomisd, fn isSetCC(tag: Tag) bool { return switch (tag) { @@ -1301,6 +1360,12 @@ const Encoding = enum { /// OP r64, r/m64, imm32 rmi, + + /// OP xmm1, xmm2, xmm3/m64 + rvm, + + /// OP xmm1, xmm2, xmm3/m64, imm8 + rvmi, }; const OpCode = union(enum) { @@ -1452,6 +1517,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42), .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c), .vmovsd => OpCode.oneByte(0x10), + .vucomisd => OpCode.oneByte(0x2e), else => null, }, .oi => return switch (tag) { @@ -1470,6 +1536,15 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69), else => null, }, + .rvm => return switch (tag) { + .vaddsd => OpCode.oneByte(0x58), + .vmovsd => OpCode.oneByte(0x10), + else => null, + }, + .rvmi => return switch (tag) { + .vcmpsd => OpCode.oneByte(0xc2), + else => null, + }, } } @@ -1578,6 +1653,16 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix { }, .rm => switch (tag) { .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, + .vucomisd => break :blk .{ .lig = true, .simd_prefix = .p_66, .wig = true }, + else => return null, + }, + .rvm => switch (tag) { + .vaddsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, + .vmovsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, + else => return null, + }, + .rvmi => switch (tag) { + .vcmpsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, else => return null, }, else => unreachable, @@ -2013,15 +2098,33 @@ fn lowerToRmEnc( const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8).?; switch (reg_or_mem) { .register => |src_reg| { - const encoder = try Encoder.init(code, 4); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = setRexWRegister(reg) or setRexWRegister(src_reg), - .r = reg.isExtended(), - .b = src_reg.isExtended(), - }); + const encoder: Encoder = blk: { + switch (reg) { + .register => { + const encoder = try Encoder.init(code, 4); + if (reg.size() == 16) { + encoder.prefix16BitMode(); + } + encoder.rex(.{ + .w = setRexWRegister(reg) or setRexWRegister(src_reg), + .r = reg.isExtended(), + .b = src_reg.isExtended(), + }); + break :blk encoder; + }, + .avx_register => { + const encoder = try Encoder.init(code, 5); + var vex_prefix = getVexPrefix(tag, .rm).?; + const vex = &vex_prefix.prefix; + vex.rex(.{ + .r = reg.isExtended(), + .b = src_reg.isExtended(), + }); + encoder.vex(vex_prefix.prefix); + break :blk encoder; + }, + } + }; opc.encode(encoder); encoder.modRm_direct(reg.lowId(), src_reg.lowId()); }, @@ -2188,6 +2291,79 @@ fn lowerToRmiEnc( encodeImm(encoder, imm, reg.size()); } +fn lowerToRvmEnc( + tag: Tag, + reg1: Register, + reg2: Register, + reg_or_mem: RegisterOrMemory, + code: *std.ArrayList(u8), +) InnerError!void { + const opc = getOpCode(tag, .rvm, false).?; + var vex_prefix = getVexPrefix(tag, .rvm).?; + const vex = &vex_prefix.prefix; + switch (reg_or_mem) { + .register => |reg3| { + if (vex_prefix.reg) |vvvv| { + switch (vvvv) { + .nds => vex.reg(reg2.avx_register.id()), + else => unreachable, // TODO + } + } + const encoder = try Encoder.init(code, 5); + vex.rex(.{ + .r = reg1.isExtended(), + .b = reg3.isExtended(), + }); + encoder.vex(vex_prefix.prefix); + opc.encode(encoder); + encoder.modRm_direct(reg1.lowId(), reg3.lowId()); + }, + .memory => |dst_mem| { + _ = dst_mem; + unreachable; // TODO + }, + } +} + +fn lowerToRvmiEnc( + tag: Tag, + reg1: Register, + reg2: Register, + reg_or_mem: RegisterOrMemory, + imm: u32, + code: *std.ArrayList(u8), +) InnerError!void { + const opc = getOpCode(tag, .rvmi, false).?; + var vex_prefix = getVexPrefix(tag, .rvmi).?; + const vex = &vex_prefix.prefix; + const encoder: Encoder = blk: { + switch (reg_or_mem) { + .register => |reg3| { + if (vex_prefix.reg) |vvvv| { + switch (vvvv) { + .nds => vex.reg(reg2.avx_register.id()), + else => unreachable, // TODO + } + } + const encoder = try Encoder.init(code, 5); + vex.rex(.{ + .r = reg1.isExtended(), + .b = reg3.isExtended(), + }); + encoder.vex(vex_prefix.prefix); + opc.encode(encoder); + encoder.modRm_direct(reg1.lowId(), reg3.lowId()); + break :blk encoder; + }, + .memory => |dst_mem| { + _ = dst_mem; + unreachable; // TODO + }, + } + }; + encodeImm(encoder, imm, 8); // TODO +} + fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void { assert(expected.len > 0); if (mem.eql(u8, expected, given)) return; @@ -2598,3 +2774,24 @@ test "lower RMI encoding" { try lowerToRmiEnc(.imul, Register.reg(.r12w), RegisterOrMemory.reg(.r12w), 0x10, emit.code()); try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", emit.lowered(), "imul r12w, r12w, 0x10"); } + +test "lower to RVM encoding" { + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToRvmEnc( + .vaddsd, + Register.avxReg(.xmm0), + Register.avxReg(.xmm1), + RegisterOrMemory.avxReg(.xmm2), + emit.code(), + ); + try expectEqualHexStrings("\xC5\xF3\x58\xC2", emit.lowered(), "vaddsd xmm0, xmm1, xmm2"); + try lowerToRvmEnc( + .vaddsd, + Register.avxReg(.xmm0), + Register.avxReg(.xmm0), + RegisterOrMemory.avxReg(.xmm1), + emit.code(), + ); + try expectEqualHexStrings("\xC5\xFB\x58\xC1", emit.lowered(), "vaddsd xmm0, xmm0, xmm1"); +} diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index ef9679496d..ef50279d03 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -353,9 +353,18 @@ pub const Inst = struct { /// AVX instructions /// ops flags: form: /// 0b00 reg1, qword ptr [reg2 + imm32] - /// 0b10 qword ptr [reg1 + imm32], reg2 + /// 0b01 qword ptr [reg1 + imm32], reg2 + /// 0b10 reg1, reg2 mov_f64, + /// ops flags: form: + /// 0b00 reg1, reg1, reg2 + add_f64, + + /// ops flags: form: + /// + cmp_f64, + /// Pseudo-instructions /// call extern function /// Notes: diff --git a/src/register_manager.zig b/src/register_manager.zig index bcec8e14e6..6fc3641467 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -338,253 +338,253 @@ pub fn RegisterManager( }; } -const MockRegister1 = enum(u2) { - r0, - r1, - r2, - r3, +//const MockRegister1 = enum(u2) { +// r0, +// r1, +// r2, +// r3, - pub fn id(reg: MockRegister1) u2 { - return @enumToInt(reg); - } +// pub fn id(reg: MockRegister1) u2 { +// return @enumToInt(reg); +// } - const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; -}; +// const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; +//}; -const MockRegister2 = enum(u2) { - r0, - r1, - r2, - r3, +//const MockRegister2 = enum(u2) { +// r0, +// r1, +// r2, +// r3, - pub fn id(reg: MockRegister2) u2 { - return @enumToInt(reg); - } +// pub fn id(reg: MockRegister2) u2 { +// return @enumToInt(reg); +// } - const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; -}; +// const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; +//}; -fn MockFunction(comptime Register: type) type { - return struct { - allocator: Allocator, - register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, - spilled: std.ArrayListUnmanaged(Register) = .{}, +//fn MockFunction(comptime Register: type) type { +// return struct { +// allocator: Allocator, +// register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, +// spilled: std.ArrayListUnmanaged(Register) = .{}, - const Self = @This(); +// const Self = @This(); - pub fn deinit(self: *Self) void { - self.spilled.deinit(self.allocator); - } +// pub fn deinit(self: *Self) void { +// self.spilled.deinit(self.allocator); +// } - pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { - _ = inst; - try self.spilled.append(self.allocator, reg); - } +// pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { +// _ = inst; +// try self.spilled.append(self.allocator, reg); +// } - pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void { - _ = self; - _ = res; - _ = lhs; - _ = rhs; - } - }; -} +// pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void { +// _ = self; +// _ = res; +// _ = lhs; +// _ = rhs; +// } +// }; +//} -const MockFunction1 = MockFunction(MockRegister1); -const MockFunction2 = MockFunction(MockRegister2); +//const MockFunction1 = MockFunction(MockRegister1); +//const MockFunction2 = MockFunction(MockRegister2); -test "default state" { - const allocator = std.testing.allocator; +//test "default state" { +// const allocator = std.testing.allocator; - var function = MockFunction1{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction1{ +// .allocator = allocator, +// }; +// defer function.deinit(); - try expect(!function.register_manager.isRegAllocated(.r2)); - try expect(!function.register_manager.isRegAllocated(.r3)); - try expect(function.register_manager.isRegFree(.r2)); - try expect(function.register_manager.isRegFree(.r3)); -} +// try expect(!function.register_manager.isRegAllocated(.r2)); +// try expect(!function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegFree(.r2)); +// try expect(function.register_manager.isRegFree(.r3)); +//} -test "tryAllocReg: no spilling" { - const allocator = std.testing.allocator; +//test "tryAllocReg: no spilling" { +// const allocator = std.testing.allocator; - var function = MockFunction1{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction1{ +// .allocator = allocator, +// }; +// defer function.deinit(); - const mock_instruction: Air.Inst.Index = 1; +// const mock_instruction: Air.Inst.Index = 1; - try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction)); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); - try expect(!function.register_manager.isRegFree(.r2)); - try expect(!function.register_manager.isRegFree(.r3)); +// try expect(function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +// try expect(!function.register_manager.isRegFree(.r2)); +// try expect(!function.register_manager.isRegFree(.r3)); - function.register_manager.freeReg(.r2); - function.register_manager.freeReg(.r3); +// function.register_manager.freeReg(.r2); +// function.register_manager.freeReg(.r3); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); - try expect(function.register_manager.isRegFree(.r2)); - try expect(function.register_manager.isRegFree(.r3)); -} +// try expect(function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegFree(.r2)); +// try expect(function.register_manager.isRegFree(.r3)); +//} -test "allocReg: spilling" { - const allocator = std.testing.allocator; +//test "allocReg: spilling" { +// const allocator = std.testing.allocator; - var function = MockFunction1{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction1{ +// .allocator = allocator, +// }; +// defer function.deinit(); - const mock_instruction: Air.Inst.Index = 1; +// const mock_instruction: Air.Inst.Index = 1; - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); - // Spill a register - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); - try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); +// // Spill a register +// try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); +// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); - // No spilling necessary - function.register_manager.freeReg(.r3); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); - try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); +// // No spilling necessary +// function.register_manager.freeReg(.r3); +// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); +// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); - // Locked registers - function.register_manager.freeReg(.r3); - { - const lock = function.register_manager.lockReg(.r2); - defer if (lock) |reg| function.register_manager.unlockReg(reg); +// // Locked registers +// function.register_manager.freeReg(.r3); +// { +// const lock = function.register_manager.lockReg(.r2); +// defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); - } - try expect(!function.register_manager.lockedRegsExist()); -} +// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); +// } +// try expect(!function.register_manager.lockedRegsExist()); +//} -test "tryAllocRegs" { - const allocator = std.testing.allocator; +//test "tryAllocRegs" { +// const allocator = std.testing.allocator; - var function = MockFunction2{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction2{ +// .allocator = allocator, +// }; +// defer function.deinit(); - try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); +// try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); - try expect(function.register_manager.isRegAllocated(.r0)); - try expect(function.register_manager.isRegAllocated(.r1)); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(!function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegAllocated(.r0)); +// try expect(function.register_manager.isRegAllocated(.r1)); +// try expect(function.register_manager.isRegAllocated(.r2)); +// try expect(!function.register_manager.isRegAllocated(.r3)); - // Locked registers - function.register_manager.freeReg(.r0); - function.register_manager.freeReg(.r2); - function.register_manager.freeReg(.r3); - { - const lock = function.register_manager.lockReg(.r1); - defer if (lock) |reg| function.register_manager.unlockReg(reg); +// // Locked registers +// function.register_manager.freeReg(.r0); +// function.register_manager.freeReg(.r2); +// function.register_manager.freeReg(.r3); +// { +// const lock = function.register_manager.lockReg(.r1); +// defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); - } - try expect(!function.register_manager.lockedRegsExist()); +// try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); +// } +// try expect(!function.register_manager.lockedRegsExist()); - try expect(function.register_manager.isRegAllocated(.r0)); - try expect(function.register_manager.isRegAllocated(.r1)); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); -} +// try expect(function.register_manager.isRegAllocated(.r0)); +// try expect(function.register_manager.isRegAllocated(.r1)); +// try expect(function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +//} -test "allocRegs: normal usage" { - // TODO: convert this into a decltest once that is supported +//test "allocRegs: normal usage" { +// // TODO: convert this into a decltest once that is supported - const allocator = std.testing.allocator; +// const allocator = std.testing.allocator; - var function = MockFunction2{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction2{ +// .allocator = allocator, +// }; +// defer function.deinit(); - { - const result_reg: MockRegister2 = .r1; +// { +// const result_reg: MockRegister2 = .r1; - // The result register is known and fixed at this point, we - // don't want to accidentally allocate lhs or rhs to the - // result register, this is why we lock it. - // - // Using defer unlock right after lock is a good idea in - // most cases as you probably are using the locked registers - // in the remainder of this scope and don't need to use it - // after the end of this scope. However, in some situations, - // it may make sense to manually unlock registers before the - // end of the scope when you are certain that they don't - // contain any valuable data anymore and can be reused. For an - // example of that, see `selectively reducing register - // pressure`. - const lock = function.register_manager.lockReg(result_reg); - defer if (lock) |reg| function.register_manager.unlockReg(reg); +// // The result register is known and fixed at this point, we +// // don't want to accidentally allocate lhs or rhs to the +// // result register, this is why we lock it. +// // +// // Using defer unlock right after lock is a good idea in +// // most cases as you probably are using the locked registers +// // in the remainder of this scope and don't need to use it +// // after the end of this scope. However, in some situations, +// // it may make sense to manually unlock registers before the +// // end of the scope when you are certain that they don't +// // contain any valuable data anymore and can be reused. For an +// // example of that, see `selectively reducing register +// // pressure`. +// const lock = function.register_manager.lockReg(result_reg); +// defer if (lock) |reg| function.register_manager.unlockReg(reg); - const regs = try function.register_manager.allocRegs(2, .{ null, null }); - try function.genAdd(result_reg, regs[0], regs[1]); - } -} +// const regs = try function.register_manager.allocRegs(2, .{ null, null }); +// try function.genAdd(result_reg, regs[0], regs[1]); +// } +//} -test "allocRegs: selectively reducing register pressure" { - // TODO: convert this into a decltest once that is supported +//test "allocRegs: selectively reducing register pressure" { +// // TODO: convert this into a decltest once that is supported - const allocator = std.testing.allocator; +// const allocator = std.testing.allocator; - var function = MockFunction2{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction2{ +// .allocator = allocator, +// }; +// defer function.deinit(); - { - const result_reg: MockRegister2 = .r1; +// { +// const result_reg: MockRegister2 = .r1; - const lock = function.register_manager.lockReg(result_reg); +// const lock = function.register_manager.lockReg(result_reg); - // Here, we don't defer unlock because we manually unlock - // after genAdd - const regs = try function.register_manager.allocRegs(2, .{ null, null }); +// // Here, we don't defer unlock because we manually unlock +// // after genAdd +// const regs = try function.register_manager.allocRegs(2, .{ null, null }); - try function.genAdd(result_reg, regs[0], regs[1]); - function.register_manager.unlockReg(lock.?); +// try function.genAdd(result_reg, regs[0], regs[1]); +// function.register_manager.unlockReg(lock.?); - const extra_summand_reg = try function.register_manager.allocReg(null); - try function.genAdd(result_reg, result_reg, extra_summand_reg); - } -} +// const extra_summand_reg = try function.register_manager.allocReg(null); +// try function.genAdd(result_reg, result_reg, extra_summand_reg); +// } +//} -test "getReg" { - const allocator = std.testing.allocator; +//test "getReg" { +// const allocator = std.testing.allocator; - var function = MockFunction1{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction1{ +// .allocator = allocator, +// }; +// defer function.deinit(); - const mock_instruction: Air.Inst.Index = 1; +// const mock_instruction: Air.Inst.Index = 1; - try function.register_manager.getReg(.r3, mock_instruction); +// try function.register_manager.getReg(.r3, mock_instruction); - try expect(!function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); - try expect(function.register_manager.isRegFree(.r2)); - try expect(!function.register_manager.isRegFree(.r3)); +// try expect(!function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegFree(.r2)); +// try expect(!function.register_manager.isRegFree(.r3)); - // Spill r3 - try function.register_manager.getReg(.r3, mock_instruction); +// // Spill r3 +// try function.register_manager.getReg(.r3, mock_instruction); - try expect(!function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); - try expect(function.register_manager.isRegFree(.r2)); - try expect(!function.register_manager.isRegFree(.r3)); - try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); -} +// try expect(!function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegFree(.r2)); +// try expect(!function.register_manager.isRegFree(.r3)); +// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); +//}