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:
Ficture Seven 2020-08-05 02:52:33 +04:00 committed by GitHub
parent a33dc2f491
commit ee22517d92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 311 additions and 145 deletions

View File

@ -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)

View File

@ -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)
{ {

View File

@ -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;

View File

@ -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))
};
}
}
} }

View File

@ -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;
} }

View File

@ -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);
} }

View File

@ -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);

View File

@ -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));

View 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);
}
}
}

View File

@ -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,

View File

@ -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)

View File

@ -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";