public LowInstruction(LowOp op, int dest, int left, int right, ulong data) { Op = op; Dest = dest; Left = left; Right = right; Data = data; }
public void Noncommutative_arithmetic_destination_is_not_same_as_right(LowOp op) { var method = new LowMethod <X64Register>(); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32, X64Register.Rax)); method.Blocks.Add(new LowBlock { Instructions = { new LowInstruction(LowOp.LoadInt, 0, 0, 0, 1), // Load 1 -> #0 new LowInstruction(LowOp.LoadInt, 1, 0, 0, 1), // Load 1 -> #1 new LowInstruction(op, 2, 0, 1, 0), // Subtract/Shift #0 - #1 -> #2 new LowInstruction(LowOp.Test, 0, 0, 0, 0), // Use #0 new LowInstruction(LowOp.Move, 3, 2, 0, 0), // Use #2 new LowInstruction(LowOp.Return, 0, 3, 0, 0) }, Predecessors = Array.Empty <int>(), Successors = Array.Empty <int>() }); var(rewritten, allocationMap) = X64RegisterAllocator.Allocate(method); AssertDump(rewritten, $@" LB_0: LoadInt 0 0 1 -> 0 LoadInt 0 0 1 -> 1 {op} 0 1 0 -> 2 Test 0 0 0 -> 0 Move 2 0 0 -> 3 Return 3 0 0 -> 0"); Assert.That(allocationMap.Get(0).localIndex, Is.EqualTo(0)); Assert.That(allocationMap.Get(1).localIndex, Is.EqualTo(1)); Assert.That(allocationMap.Get(2).localIndex, Is.EqualTo(2)); // It would be tempting to assign #1 and #2 the same register, but that // is not good for x64: we would have to emit "mov r1, r0; sub r1, r1" where // local #1 is stored in r1 but local #2 lives there up until the last instruction. Assert.That(allocationMap.Get(1).location.Register, Is.Not.EqualTo(allocationMap.Get(2).location.Register)); }
public void Load_right_and_arithmetic_are_folded(LowOp arithmeticOp, long constant) { // The right operand of shift has special location on x64 var isShift = arithmeticOp == LowOp.ShiftLeft || arithmeticOp == LowOp.ShiftArithmeticRight; var rightLocation = isShift ? X64Register.Rdx : X64Register.Invalid; var method = new LowMethod <X64Register>(); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32, requiredLocation: X64Register.Rcx)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32, requiredLocation: rightLocation)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32)); method.Blocks.Add(new LowBlock { Instructions = { new LowInstruction(LowOp.LoadInt, 1, 0, 0, (ulong)constant), // Load constant -> #1 new LowInstruction(arithmeticOp, 2, 0, 1, 0) // #0 op #1 -> #2 } }); var expected = @$ " ; #0 int32 [rcx]
public void Division_reserves_rdx(LowOp op) { var method = new LowMethod <X64Register>(); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32, X64Register.Rax)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Int32, X64Register.Rax)); method.Blocks.Add(new LowBlock { Instructions = { new LowInstruction(LowOp.LoadInt, 0, 0, 0, 1), // Load 1 -> #0 new LowInstruction(LowOp.LoadInt, 1, 0, 0, 1), // Load 1 -> #1 new LowInstruction(LowOp.LoadInt, 2, 0, 0, 1), // Load 1 -> #2 new LowInstruction(op, 3, 2, 2, 0), // Divide #2 / #2 -> #3 new LowInstruction(LowOp.Test, 0, 0, 0, 0), // Use #0 new LowInstruction(LowOp.Move, 0, 1, 0, 0), // Use #1 new LowInstruction(LowOp.Return, 0, 3, 0, 0) }, Predecessors = Array.Empty <int>(), Successors = Array.Empty <int>() }); var(_, allocationMap) = X64RegisterAllocator.Allocate(method); // RDX holds the upper part of dividend and therefore must be reserved for (var i = 0; i < allocationMap.IntervalCount; i++) { var(location, localIndex) = allocationMap.Get(i); if (localIndex >= 0) { Assert.That(location.Register, Is.Not.EqualTo(X64Register.Rdx)); } } }
public void Call_instruction_reserves_registers(LowOp callOp) { var method = new LowMethod <X64Register>(); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Bool)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Bool)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Bool)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Bool)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Bool)); method.Locals.Add(new LowLocal <X64Register>(SimpleType.Void, X64Register.Rax)); method.Blocks.Add(new LowBlock { Instructions = { new LowInstruction(LowOp.LoadInt, 0, 0, 0, 1), // Load 1 -> #0 new LowInstruction(LowOp.LoadInt, 1, 0, 0, 1), // Load 1 -> #1 new LowInstruction(LowOp.LoadInt, 2, 0, 0, 1), // Load 1 -> #2 new LowInstruction(LowOp.LoadInt, 3, 0, 0, 1), // Load 1 -> #3 new LowInstruction(LowOp.LoadInt, 4, 0, 0, 1), // Load 1 -> #4 new LowInstruction(callOp, 5, 0, 0, 1234), // Call - this trashes rax, rcx, rdx, r8 and r9 new LowInstruction(LowOp.Test, 0, 0, 0, 0), // Test #0 new LowInstruction(LowOp.Test, 0, 1, 0, 0), // Test #1 new LowInstruction(LowOp.Test, 0, 2, 0, 0), // Test #2 new LowInstruction(LowOp.Test, 0, 3, 0, 0), // Test #3 new LowInstruction(LowOp.Test, 0, 4, 0, 0), // Test #4 new LowInstruction(LowOp.Return, 5, 0, 0, 0) }, Predecessors = Array.Empty <int>(), Successors = Array.Empty <int>() }); var(_, allocationMap) = X64RegisterAllocator.Allocate(method); // No local variable should be assigned to a blocked register for (var i = 0; i < allocationMap.IntervalCount; i++) { var(location, localIndex) = allocationMap.Get(i); if (localIndex == -1) { continue; } if (localIndex == 5) { Assert.That(location.Register, Is.EqualTo(X64Register.Rax)); continue; } Assert.That(location.IsSet, Is.True); Assert.That(location.Register, Is.Not.EqualTo(X64Register.Rax)); Assert.That(location.Register, Is.Not.EqualTo(X64Register.Rcx)); Assert.That(location.Register, Is.Not.EqualTo(X64Register.Rdx)); Assert.That(location.Register, Is.Not.EqualTo(X64Register.R8)); Assert.That(location.Register, Is.Not.EqualTo(X64Register.R9)); Assert.That(location.Register, Is.Not.EqualTo(X64Register.R10)); Assert.That(location.Register, Is.Not.EqualTo(X64Register.R11)); // ..and as a general sanity check, do not allocate the stack pointer! Assert.That(location.Register, Is.Not.EqualTo(X64Register.Rsp)); } }