mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-08-20 15:48:43 +02:00
Improve branch operations (#1442)
* Add Compare instruction * Add BranchIf instruction * Use test when BranchIf & Compare against 0 * Propagate Compare into BranchIfTrue/False use - Propagate Compare operations into their BranchIfTrue/False use and turn these into a BranchIf. - Clean up Comparison enum. * Replace BranchIfTrue/False with BranchIf * Use BranchIf in EmitPtPointerLoad - Using BranchIf early instead of BranchIfTrue/False improves LCQ and reduces the amount of work needed by the Optimizer. EmitPtPointerLoader was a/the big producer of BranchIfTrue/False. - Fix asserts firing when assembling BitwiseAnd because of type mismatch in EmitStoreExclusive. This is harmless and should not cause any diffs. * Increment PPTC interval version * Improve IRDumper for BranchIf & Compare * Use BranchIf in EmitNativeCall * Clean up * Do not emit test when immediately preceded by and
This commit is contained in:
parent
a33dc2f491
commit
ee22517d92
12 changed files with 311 additions and 145 deletions
|
@ -2,6 +2,8 @@ using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen.Optimizations
|
namespace ARMeilleure.CodeGen.Optimizations
|
||||||
{
|
{
|
||||||
static class Optimizer
|
static class Optimizer
|
||||||
|
@ -42,7 +44,18 @@ namespace ARMeilleure.CodeGen.Optimizations
|
||||||
|
|
||||||
Simplification.RunPass(operation);
|
Simplification.RunPass(operation);
|
||||||
|
|
||||||
if (DestIsLocalVar(operation) && IsPropagableCopy(operation))
|
if (DestIsLocalVar(operation))
|
||||||
|
{
|
||||||
|
if (IsPropagableCompare(operation))
|
||||||
|
{
|
||||||
|
modified |= PropagateCompare(operation);
|
||||||
|
|
||||||
|
if (modified && IsUnused(operation))
|
||||||
|
{
|
||||||
|
RemoveNode(block, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (IsPropagableCopy(operation))
|
||||||
{
|
{
|
||||||
PropagateCopy(operation);
|
PropagateCopy(operation);
|
||||||
|
|
||||||
|
@ -50,6 +63,7 @@ namespace ARMeilleure.CodeGen.Optimizations
|
||||||
|
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
node = nextNode;
|
node = nextNode;
|
||||||
}
|
}
|
||||||
|
@ -88,6 +102,91 @@ namespace ARMeilleure.CodeGen.Optimizations
|
||||||
while (modified);
|
while (modified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool PropagateCompare(Operation compOp)
|
||||||
|
{
|
||||||
|
// Try to propagate Compare operations into their BranchIf uses, when these BranchIf uses are in the form
|
||||||
|
// of:
|
||||||
|
//
|
||||||
|
// - BranchIf %x, 0x0, Equal ;; i.e BranchIfFalse %x
|
||||||
|
// - BranchIf %x, 0x0, NotEqual ;; i.e BranchIfTrue %x
|
||||||
|
//
|
||||||
|
// The commutative property of Equal and NotEqual is taken into consideration as well.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// %x = Compare %a, %b, comp
|
||||||
|
// BranchIf %x, 0x0, NotEqual
|
||||||
|
//
|
||||||
|
// =>
|
||||||
|
//
|
||||||
|
// BranchIf %a, %b, comp
|
||||||
|
|
||||||
|
static bool IsZeroBranch(Operation operation, out Comparison compType)
|
||||||
|
{
|
||||||
|
compType = Comparison.Equal;
|
||||||
|
|
||||||
|
if (operation.Instruction != Instruction.BranchIf)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand src1 = operation.GetSource(0);
|
||||||
|
Operand src2 = operation.GetSource(1);
|
||||||
|
Operand comp = operation.GetSource(2);
|
||||||
|
|
||||||
|
compType = (Comparison)comp.AsInt32();
|
||||||
|
|
||||||
|
return (src1.Kind == OperandKind.Constant && src1.Value == 0) ||
|
||||||
|
(src2.Kind == OperandKind.Constant && src2.Value == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
|
Operand dest = compOp.Destination;
|
||||||
|
Operand src1 = compOp.GetSource(0);
|
||||||
|
Operand src2 = compOp.GetSource(1);
|
||||||
|
Operand comp = compOp.GetSource(2);
|
||||||
|
|
||||||
|
Comparison compType = (Comparison)comp.AsInt32();
|
||||||
|
|
||||||
|
Node[] uses = dest.Uses.ToArray();
|
||||||
|
|
||||||
|
foreach (Node use in uses)
|
||||||
|
{
|
||||||
|
if (!(use is Operation operation))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If operation is a BranchIf and has a constant value 0 in its RHS or LHS source operands.
|
||||||
|
if (IsZeroBranch(operation, out Comparison otherCompType))
|
||||||
|
{
|
||||||
|
Comparison propCompType;
|
||||||
|
|
||||||
|
if (otherCompType == Comparison.NotEqual)
|
||||||
|
{
|
||||||
|
propCompType = compType;
|
||||||
|
}
|
||||||
|
else if (otherCompType == Comparison.Equal)
|
||||||
|
{
|
||||||
|
propCompType = compType.Invert();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
operation.SetSource(0, src1);
|
||||||
|
operation.SetSource(1, src2);
|
||||||
|
operation.SetSource(2, Const((int)propCompType));
|
||||||
|
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
private static void PropagateCopy(Operation copyOp)
|
private static void PropagateCopy(Operation copyOp)
|
||||||
{
|
{
|
||||||
// Propagate copy source operand to all uses of the destination operand.
|
// Propagate copy source operand to all uses of the destination operand.
|
||||||
|
@ -143,6 +242,11 @@ namespace ARMeilleure.CodeGen.Optimizations
|
||||||
|| operation.Instruction == Instruction.CompareAndSwap8);
|
|| operation.Instruction == Instruction.CompareAndSwap8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsPropagableCompare(Operation operation)
|
||||||
|
{
|
||||||
|
return operation.Instruction == Instruction.Compare;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsPropagableCopy(Operation operation)
|
private static bool IsPropagableCopy(Operation operation)
|
||||||
{
|
{
|
||||||
if (operation.Instruction != Instruction.Copy)
|
if (operation.Instruction != Instruction.Copy)
|
||||||
|
|
|
@ -33,24 +33,14 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
Add(Instruction.BitwiseNot, GenerateBitwiseNot);
|
Add(Instruction.BitwiseNot, GenerateBitwiseNot);
|
||||||
Add(Instruction.BitwiseOr, GenerateBitwiseOr);
|
Add(Instruction.BitwiseOr, GenerateBitwiseOr);
|
||||||
Add(Instruction.Branch, GenerateBranch);
|
Add(Instruction.Branch, GenerateBranch);
|
||||||
Add(Instruction.BranchIfFalse, GenerateBranchIfFalse);
|
Add(Instruction.BranchIf, GenerateBranchIf);
|
||||||
Add(Instruction.BranchIfTrue, GenerateBranchIfTrue);
|
|
||||||
Add(Instruction.ByteSwap, GenerateByteSwap);
|
Add(Instruction.ByteSwap, GenerateByteSwap);
|
||||||
Add(Instruction.Call, GenerateCall);
|
Add(Instruction.Call, GenerateCall);
|
||||||
Add(Instruction.Clobber, GenerateClobber);
|
Add(Instruction.Clobber, GenerateClobber);
|
||||||
|
Add(Instruction.Compare, GenerateCompare);
|
||||||
Add(Instruction.CompareAndSwap, GenerateCompareAndSwap);
|
Add(Instruction.CompareAndSwap, GenerateCompareAndSwap);
|
||||||
Add(Instruction.CompareAndSwap16, GenerateCompareAndSwap16);
|
Add(Instruction.CompareAndSwap16, GenerateCompareAndSwap16);
|
||||||
Add(Instruction.CompareAndSwap8, GenerateCompareAndSwap8);
|
Add(Instruction.CompareAndSwap8, GenerateCompareAndSwap8);
|
||||||
Add(Instruction.CompareEqual, GenerateCompareEqual);
|
|
||||||
Add(Instruction.CompareGreater, GenerateCompareGreater);
|
|
||||||
Add(Instruction.CompareGreaterOrEqual, GenerateCompareGreaterOrEqual);
|
|
||||||
Add(Instruction.CompareGreaterOrEqualUI, GenerateCompareGreaterOrEqualUI);
|
|
||||||
Add(Instruction.CompareGreaterUI, GenerateCompareGreaterUI);
|
|
||||||
Add(Instruction.CompareLess, GenerateCompareLess);
|
|
||||||
Add(Instruction.CompareLessOrEqual, GenerateCompareLessOrEqual);
|
|
||||||
Add(Instruction.CompareLessOrEqualUI, GenerateCompareLessOrEqualUI);
|
|
||||||
Add(Instruction.CompareLessUI, GenerateCompareLessUI);
|
|
||||||
Add(Instruction.CompareNotEqual, GenerateCompareNotEqual);
|
|
||||||
Add(Instruction.ConditionalSelect, GenerateConditionalSelect);
|
Add(Instruction.ConditionalSelect, GenerateConditionalSelect);
|
||||||
Add(Instruction.ConvertI64ToI32, GenerateConvertI64ToI32);
|
Add(Instruction.ConvertI64ToI32, GenerateConvertI64ToI32);
|
||||||
Add(Instruction.ConvertToFP, GenerateConvertToFP);
|
Add(Instruction.ConvertToFP, GenerateConvertToFP);
|
||||||
|
@ -474,6 +464,8 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
|
|
||||||
Debug.Assert(dest.Type.IsInteger());
|
Debug.Assert(dest.Type.IsInteger());
|
||||||
|
|
||||||
|
// Note: GenerateCompareCommon makes the assumption that BitwiseAnd will emit only a single `and`
|
||||||
|
// instruction.
|
||||||
context.Assembler.And(dest, src2, dest.Type);
|
context.Assembler.And(dest, src2, dest.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,22 +517,17 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
context.JumpTo(context.CurrBlock.Branch);
|
context.JumpTo(context.CurrBlock.Branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateBranchIfFalse(CodeGenContext context, Operation operation)
|
private static void GenerateBranchIf(CodeGenContext context, Operation operation)
|
||||||
{
|
{
|
||||||
Operand source = operation.GetSource(0);
|
Operand comp = operation.GetSource(2);
|
||||||
|
|
||||||
context.Assembler.Test(source, source, source.Type);
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
context.JumpTo(X86Condition.Equal, context.CurrBlock.Branch);
|
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateBranchIfTrue(CodeGenContext context, Operation operation)
|
GenerateCompareCommon(context, operation);
|
||||||
{
|
|
||||||
Operand source = operation.GetSource(0);
|
|
||||||
|
|
||||||
context.Assembler.Test(source, source, source.Type);
|
context.JumpTo(cond, context.CurrBlock.Branch);
|
||||||
|
|
||||||
context.JumpTo(X86Condition.NotEqual, context.CurrBlock.Branch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateByteSwap(CodeGenContext context, Operation operation)
|
private static void GenerateByteSwap(CodeGenContext context, Operation operation)
|
||||||
|
@ -566,6 +553,60 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
// register allocator, we don't need to produce any code.
|
// register allocator, we don't need to produce any code.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void GenerateCompare(CodeGenContext context, Operation operation)
|
||||||
|
{
|
||||||
|
Operand dest = operation.Destination;
|
||||||
|
Operand comp = operation.GetSource(2);
|
||||||
|
|
||||||
|
Debug.Assert(dest.Type == OperandType.I32);
|
||||||
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
|
var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
|
||||||
|
|
||||||
|
GenerateCompareCommon(context, operation);
|
||||||
|
|
||||||
|
context.Assembler.Setcc(dest, cond);
|
||||||
|
context.Assembler.Movzx8(dest, dest, OperandType.I32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateCompareCommon(CodeGenContext context, Operation operation)
|
||||||
|
{
|
||||||
|
Operand src1 = operation.GetSource(0);
|
||||||
|
Operand src2 = operation.GetSource(1);
|
||||||
|
|
||||||
|
EnsureSameType(src1, src2);
|
||||||
|
|
||||||
|
Debug.Assert(src1.Type.IsInteger());
|
||||||
|
|
||||||
|
if (src2.Kind == OperandKind.Constant && src2.Value == 0)
|
||||||
|
{
|
||||||
|
if (MatchOperation(operation.ListPrevious, Instruction.BitwiseAnd, src1.Type, src1.GetRegister()))
|
||||||
|
{
|
||||||
|
// Since the `test` and `and` instruction set the status flags in the same way, we can omit the
|
||||||
|
// `test r,r` instruction when it is immediately preceded by an `and r,*` instruction.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// and eax, 0x3
|
||||||
|
// test eax, eax
|
||||||
|
// jz .L0
|
||||||
|
//
|
||||||
|
// =>
|
||||||
|
//
|
||||||
|
// and eax, 0x3
|
||||||
|
// jz .L0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Assembler.Test(src1, src1, src1.Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Assembler.Cmp(src1, src2, src1.Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void GenerateCompareAndSwap(CodeGenContext context, Operation operation)
|
private static void GenerateCompareAndSwap(CodeGenContext context, Operation operation)
|
||||||
{
|
{
|
||||||
Operand src1 = operation.GetSource(0);
|
Operand src1 = operation.GetSource(0);
|
||||||
|
@ -615,71 +656,6 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
context.Assembler.Cmpxchg8(memOp, src3);
|
context.Assembler.Cmpxchg8(memOp, src3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateCompareEqual(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.Equal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareGreater(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.Greater);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareGreaterOrEqual(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.GreaterOrEqual);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareGreaterOrEqualUI(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.AboveOrEqual);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareGreaterUI(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.Above);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareLess(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.Less);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareLessOrEqual(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.LessOrEqual);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareLessOrEqualUI(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.BelowOrEqual);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareLessUI(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.Below);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompareNotEqual(CodeGenContext context, Operation operation)
|
|
||||||
{
|
|
||||||
GenerateCompare(context, operation, X86Condition.NotEqual);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateCompare(CodeGenContext context, Operation operation, X86Condition condition)
|
|
||||||
{
|
|
||||||
Operand dest = operation.Destination;
|
|
||||||
Operand src1 = operation.GetSource(0);
|
|
||||||
Operand src2 = operation.GetSource(1);
|
|
||||||
|
|
||||||
EnsureSameType(src1, src2);
|
|
||||||
|
|
||||||
Debug.Assert(dest.Type == OperandType.I32);
|
|
||||||
|
|
||||||
context.Assembler.Cmp(src1, src2, src1.Type);
|
|
||||||
context.Assembler.Setcc(dest, condition);
|
|
||||||
context.Assembler.Movzx8(dest, dest, OperandType.I32);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateConditionalSelect(CodeGenContext context, Operation operation)
|
private static void GenerateConditionalSelect(CodeGenContext context, Operation operation)
|
||||||
{
|
{
|
||||||
Operand dest = operation.Destination;
|
Operand dest = operation.Destination;
|
||||||
|
@ -1561,6 +1537,25 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
context.Assembler.Pshufd(dest, dest, 0xfc);
|
context.Assembler.Pshufd(dest, dest, 0xfc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool MatchOperation(Node node, Instruction inst, OperandType destType, Register destReg)
|
||||||
|
{
|
||||||
|
if (!(node is Operation operation) || node.DestinationsCount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.Instruction != inst)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand dest = operation.Destination;
|
||||||
|
|
||||||
|
return dest.Kind == OperandKind.Register &&
|
||||||
|
dest.Type == destType &&
|
||||||
|
dest.GetRegister() == destReg;
|
||||||
|
}
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
[Conditional("DEBUG")]
|
||||||
private static void ValidateUnOp(Operand dest, Operand source)
|
private static void ValidateUnOp(Operand dest, Operand source)
|
||||||
{
|
{
|
||||||
|
|
|
@ -154,7 +154,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
// -- Doing so may allow us to encode the constant as operand 2 and avoid a copy.
|
// -- Doing so may allow us to encode the constant as operand 2 and avoid a copy.
|
||||||
// - If the constant is on operand 2, we check if the instruction supports it,
|
// - If the constant is on operand 2, we check if the instruction supports it,
|
||||||
// if not, we also add a copy. 64-bits constants are usually not supported.
|
// if not, we also add a copy. 64-bits constants are usually not supported.
|
||||||
if (IsCommutative(inst))
|
if (IsCommutative(operation))
|
||||||
{
|
{
|
||||||
src2 = operation.GetSource(1);
|
src2 = operation.GetSource(1);
|
||||||
|
|
||||||
|
@ -1348,16 +1348,8 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
case Instruction.BitwiseAnd:
|
case Instruction.BitwiseAnd:
|
||||||
case Instruction.BitwiseExclusiveOr:
|
case Instruction.BitwiseExclusiveOr:
|
||||||
case Instruction.BitwiseOr:
|
case Instruction.BitwiseOr:
|
||||||
case Instruction.CompareEqual:
|
case Instruction.BranchIf:
|
||||||
case Instruction.CompareGreater:
|
case Instruction.Compare:
|
||||||
case Instruction.CompareGreaterOrEqual:
|
|
||||||
case Instruction.CompareGreaterOrEqualUI:
|
|
||||||
case Instruction.CompareGreaterUI:
|
|
||||||
case Instruction.CompareLess:
|
|
||||||
case Instruction.CompareLessOrEqual:
|
|
||||||
case Instruction.CompareLessOrEqualUI:
|
|
||||||
case Instruction.CompareLessUI:
|
|
||||||
case Instruction.CompareNotEqual:
|
|
||||||
case Instruction.Multiply:
|
case Instruction.Multiply:
|
||||||
case Instruction.RotateRight:
|
case Instruction.RotateRight:
|
||||||
case Instruction.ShiftLeft:
|
case Instruction.ShiftLeft:
|
||||||
|
@ -1376,18 +1368,28 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsCommutative(Instruction inst)
|
private static bool IsCommutative(Operation operation)
|
||||||
{
|
{
|
||||||
switch (inst)
|
switch (operation.Instruction)
|
||||||
{
|
{
|
||||||
case Instruction.Add:
|
case Instruction.Add:
|
||||||
case Instruction.BitwiseAnd:
|
case Instruction.BitwiseAnd:
|
||||||
case Instruction.BitwiseExclusiveOr:
|
case Instruction.BitwiseExclusiveOr:
|
||||||
case Instruction.BitwiseOr:
|
case Instruction.BitwiseOr:
|
||||||
case Instruction.CompareEqual:
|
|
||||||
case Instruction.CompareNotEqual:
|
|
||||||
case Instruction.Multiply:
|
case Instruction.Multiply:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case Instruction.BranchIf:
|
||||||
|
case Instruction.Compare:
|
||||||
|
{
|
||||||
|
Operand comp = operation.GetSource(2);
|
||||||
|
|
||||||
|
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||||
|
|
||||||
|
var compType = (Comparison)comp.AsInt32();
|
||||||
|
|
||||||
|
return compType == Comparison.Equal || compType == Comparison.NotEqual;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen.X86
|
namespace ARMeilleure.CodeGen.X86
|
||||||
{
|
{
|
||||||
enum X86Condition
|
enum X86Condition
|
||||||
|
@ -19,4 +22,26 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
LessOrEqual = 0xe,
|
LessOrEqual = 0xe,
|
||||||
Greater = 0xf
|
Greater = 0xf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ComparisonX86Extensions
|
||||||
|
{
|
||||||
|
public static X86Condition ToX86Condition(this Comparison comp)
|
||||||
|
{
|
||||||
|
return comp switch
|
||||||
|
{
|
||||||
|
Comparison.Equal => X86Condition.Equal,
|
||||||
|
Comparison.NotEqual => X86Condition.NotEqual,
|
||||||
|
Comparison.Greater => X86Condition.Greater,
|
||||||
|
Comparison.LessOrEqual => X86Condition.LessOrEqual,
|
||||||
|
Comparison.GreaterUI => X86Condition.Above,
|
||||||
|
Comparison.LessOrEqualUI => X86Condition.BelowOrEqual,
|
||||||
|
Comparison.GreaterOrEqual => X86Condition.GreaterOrEqual,
|
||||||
|
Comparison.Less => X86Condition.Less,
|
||||||
|
Comparison.GreaterOrEqualUI => X86Condition.AboveOrEqual,
|
||||||
|
Comparison.LessUI => X86Condition.Below,
|
||||||
|
|
||||||
|
_ => throw new ArgumentException(null, nameof(comp))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -198,6 +198,8 @@ namespace ARMeilleure.Diagnostics
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Operation operation:
|
case Operation operation:
|
||||||
|
bool comparison = false;
|
||||||
|
|
||||||
_builder.Append(operation.Instruction);
|
_builder.Append(operation.Instruction);
|
||||||
|
|
||||||
if (operation.Instruction == Instruction.Extended)
|
if (operation.Instruction == Instruction.Extended)
|
||||||
|
@ -206,17 +208,32 @@ namespace ARMeilleure.Diagnostics
|
||||||
|
|
||||||
_builder.Append('.').Append(intrinOp.Intrinsic);
|
_builder.Append('.').Append(intrinOp.Intrinsic);
|
||||||
}
|
}
|
||||||
|
else if (operation.Instruction == Instruction.BranchIf ||
|
||||||
|
operation.Instruction == Instruction.Compare)
|
||||||
|
{
|
||||||
|
comparison = true;
|
||||||
|
}
|
||||||
|
|
||||||
_builder.Append(' ');
|
_builder.Append(' ');
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
for (int index = 0; index < operation.SourcesCount; index++)
|
||||||
{
|
{
|
||||||
DumpOperand(operation.GetSource(index));
|
Operand source = operation.GetSource(index);
|
||||||
|
|
||||||
if (index < operation.SourcesCount - 1)
|
if (index < operation.SourcesCount - 1)
|
||||||
{
|
{
|
||||||
|
DumpOperand(source);
|
||||||
|
|
||||||
_builder.Append(", ");
|
_builder.Append(", ");
|
||||||
}
|
}
|
||||||
|
else if (comparison)
|
||||||
|
{
|
||||||
|
_builder.Append((Comparison)source.AsInt32());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DumpOperand(source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,17 +163,18 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
// Note: The return value of a translated function is always an Int64 with the
|
// Note: The return value of a translated function is always an Int64 with the address execution has
|
||||||
// address execution has returned to. We expect this address to be immediately after the
|
// returned to. We expect this address to be immediately after the current instruction, if it isn't we
|
||||||
// current instruction, if it isn't we keep returning until we reach the dispatcher.
|
// keep returning until we reach the dispatcher.
|
||||||
Operand nextAddr = Const((long)op.Address + op.OpCodeSizeInBytes);
|
Operand nextAddr = Const((long)op.Address + op.OpCodeSizeInBytes);
|
||||||
|
|
||||||
// Try to continue within this block.
|
// Try to continue within this block.
|
||||||
// If the return address isn't to our next instruction, we need to return so the JIT can figure out what to do.
|
// If the return address isn't to our next instruction, we need to return so the JIT can figure out
|
||||||
|
// what to do.
|
||||||
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
||||||
|
|
||||||
// We need to clear out the call flag for the return address before comparing it.
|
// We need to clear out the call flag for the return address before comparing it.
|
||||||
context.BranchIfTrue(lblContinue, context.ICompareEqual(context.BitwiseAnd(returnAddress, Const(~CallFlag)), nextAddr));
|
context.BranchIf(lblContinue, context.BitwiseAnd(returnAddress, Const(~CallFlag)), nextAddr, Comparison.Equal);
|
||||||
|
|
||||||
context.Return(returnAddress);
|
context.Return(returnAddress);
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace ARMeilleure.Instructions
|
||||||
Operand exAddr = context.Load(address.Type, exAddrPtr);
|
Operand exAddr = context.Load(address.Type, exAddrPtr);
|
||||||
|
|
||||||
// STEP 1: Check if we have exclusive access to this memory region. If not, fail and skip store.
|
// STEP 1: Check if we have exclusive access to this memory region. If not, fail and skip store.
|
||||||
Operand maskedAddress = context.BitwiseAnd(address, Const(GetExclusiveAddressMask()));
|
Operand maskedAddress = context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask()));
|
||||||
|
|
||||||
Operand exFailed = context.ICompareNotEqual(exAddr, maskedAddress);
|
Operand exFailed = context.ICompareNotEqual(exAddr, maskedAddress);
|
||||||
|
|
||||||
|
|
|
@ -403,7 +403,7 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
if (lblSlowPath != null)
|
if (lblSlowPath != null)
|
||||||
{
|
{
|
||||||
context.BranchIfTrue(lblSlowPath, context.ICompareLessOrEqual(pte, Const(0L)));
|
context.BranchIf(lblSlowPath, pte, Const(0L), Comparison.LessOrEqual);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -414,7 +414,7 @@ namespace ARMeilleure.Instructions
|
||||||
Operand lblNotWatched = Label();
|
Operand lblNotWatched = Label();
|
||||||
|
|
||||||
// Is the page currently being monitored for modifications? If so we need to call MarkRegionAsModified.
|
// Is the page currently being monitored for modifications? If so we need to call MarkRegionAsModified.
|
||||||
context.BranchIfTrue(lblNotWatched, context.ICompareGreaterOrEqual(pte, Const(0L)));
|
context.BranchIf(lblNotWatched, pte, Const(0L), Comparison.GreaterOrEqual);
|
||||||
|
|
||||||
// Mark the region as modified. Size here doesn't matter as address is assumed to be size aligned here.
|
// Mark the region as modified. Size here doesn't matter as address is assumed to be size aligned here.
|
||||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.MarkRegionAsModified)), address, Const(1UL));
|
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.MarkRegionAsModified)), address, Const(1UL));
|
||||||
|
|
24
ARMeilleure/IntermediateRepresentation/Comparison.cs
Normal file
24
ARMeilleure/IntermediateRepresentation/Comparison.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
namespace ARMeilleure.IntermediateRepresentation
|
||||||
|
{
|
||||||
|
enum Comparison
|
||||||
|
{
|
||||||
|
Equal = 0,
|
||||||
|
NotEqual = 1,
|
||||||
|
Greater = 2,
|
||||||
|
LessOrEqual = 3,
|
||||||
|
GreaterUI = 4,
|
||||||
|
LessOrEqualUI = 5,
|
||||||
|
GreaterOrEqual = 6,
|
||||||
|
Less = 7,
|
||||||
|
GreaterOrEqualUI = 8,
|
||||||
|
LessUI = 9
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ComparisonExtensions
|
||||||
|
{
|
||||||
|
public static Comparison Invert(this Comparison comp)
|
||||||
|
{
|
||||||
|
return (Comparison)((int)comp ^ 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,23 +8,13 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||||
BitwiseNot,
|
BitwiseNot,
|
||||||
BitwiseOr,
|
BitwiseOr,
|
||||||
Branch,
|
Branch,
|
||||||
BranchIfFalse,
|
BranchIf,
|
||||||
BranchIfTrue,
|
|
||||||
ByteSwap,
|
ByteSwap,
|
||||||
Call,
|
Call,
|
||||||
|
Compare,
|
||||||
CompareAndSwap,
|
CompareAndSwap,
|
||||||
CompareAndSwap16,
|
CompareAndSwap16,
|
||||||
CompareAndSwap8,
|
CompareAndSwap8,
|
||||||
CompareEqual,
|
|
||||||
CompareGreater,
|
|
||||||
CompareGreaterOrEqual,
|
|
||||||
CompareGreaterOrEqualUI,
|
|
||||||
CompareGreaterUI,
|
|
||||||
CompareLess,
|
|
||||||
CompareLessOrEqual,
|
|
||||||
CompareLessOrEqualUI,
|
|
||||||
CompareLessUI,
|
|
||||||
CompareNotEqual,
|
|
||||||
ConditionalSelect,
|
ConditionalSelect,
|
||||||
ConvertI64ToI32,
|
ConvertI64ToI32,
|
||||||
ConvertToFP,
|
ConvertToFP,
|
||||||
|
|
|
@ -62,18 +62,21 @@ namespace ARMeilleure.Translation
|
||||||
BranchToLabel(label);
|
BranchToLabel(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BranchIfFalse(Operand label, Operand op1)
|
public void BranchIf(Operand label, Operand op1, Operand op2, Comparison comp)
|
||||||
{
|
{
|
||||||
Add(Instruction.BranchIfFalse, null, op1);
|
Add(Instruction.BranchIf, null, op1, op2, Const((int)comp));
|
||||||
|
|
||||||
BranchToLabel(label);
|
BranchToLabel(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void BranchIfFalse(Operand label, Operand op1)
|
||||||
|
{
|
||||||
|
BranchIf(label, op1, Const(op1.Type, 0), Comparison.Equal);
|
||||||
|
}
|
||||||
|
|
||||||
public void BranchIfTrue(Operand label, Operand op1)
|
public void BranchIfTrue(Operand label, Operand op1)
|
||||||
{
|
{
|
||||||
Add(Instruction.BranchIfTrue, null, op1);
|
BranchIf(label, op1, Const(op1.Type, 0), Comparison.NotEqual);
|
||||||
|
|
||||||
BranchToLabel(label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ByteSwap(Operand op1)
|
public Operand ByteSwap(Operand op1)
|
||||||
|
@ -243,54 +246,59 @@ namespace ARMeilleure.Translation
|
||||||
return Add(Instruction.DivideUI, Local(op1.Type), op1, op2);
|
return Add(Instruction.DivideUI, Local(op1.Type), op1, op2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Operand ICompare(Operand op1, Operand op2, Comparison comp)
|
||||||
|
{
|
||||||
|
return Add(Instruction.Compare, Local(OperandType.I32), op1, op2, Const((int)comp));
|
||||||
|
}
|
||||||
|
|
||||||
public Operand ICompareEqual(Operand op1, Operand op2)
|
public Operand ICompareEqual(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareEqual, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.Equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareGreater(Operand op1, Operand op2)
|
public Operand ICompareGreater(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareGreater, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.Greater);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareGreaterOrEqual(Operand op1, Operand op2)
|
public Operand ICompareGreaterOrEqual(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareGreaterOrEqual, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.GreaterOrEqual);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareGreaterOrEqualUI(Operand op1, Operand op2)
|
public Operand ICompareGreaterOrEqualUI(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareGreaterOrEqualUI, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.GreaterOrEqualUI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareGreaterUI(Operand op1, Operand op2)
|
public Operand ICompareGreaterUI(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareGreaterUI, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.GreaterUI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareLess(Operand op1, Operand op2)
|
public Operand ICompareLess(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareLess, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.Less);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareLessOrEqual(Operand op1, Operand op2)
|
public Operand ICompareLessOrEqual(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareLessOrEqual, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.LessOrEqual);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareLessOrEqualUI(Operand op1, Operand op2)
|
public Operand ICompareLessOrEqualUI(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareLessOrEqualUI, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.LessOrEqualUI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareLessUI(Operand op1, Operand op2)
|
public Operand ICompareLessUI(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareLessUI, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.LessUI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand ICompareNotEqual(Operand op1, Operand op2)
|
public Operand ICompareNotEqual(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
return Add(Instruction.CompareNotEqual, Local(OperandType.I32), op1, op2);
|
return ICompare(op1, op2, Comparison.NotEqual);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand Load(OperandType type, Operand address)
|
public Operand Load(OperandType type, Operand address)
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
private const string HeaderMagic = "PTChd";
|
private const string HeaderMagic = "PTChd";
|
||||||
|
|
||||||
private const int InternalVersion = 18; //! To be incremented manually for each change to the ARMeilleure project.
|
private const int InternalVersion = 19; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string BaseDir = "Ryujinx";
|
private const string BaseDir = "Ryujinx";
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue