aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChocolArm64/Instructions/InstEmitAlu.cs2
-rw-r--r--ChocolArm64/Translation/ILEmitterCtx.cs54
2 files changed, 48 insertions, 8 deletions
diff --git a/ChocolArm64/Instructions/InstEmitAlu.cs b/ChocolArm64/Instructions/InstEmitAlu.cs
index d5d9cd65..bd49124e 100644
--- a/ChocolArm64/Instructions/InstEmitAlu.cs
+++ b/ChocolArm64/Instructions/InstEmitAlu.cs
@@ -51,6 +51,8 @@ namespace ChocolArm64.Instructions
public static void Adds(ILEmitterCtx context)
{
+ context.TryOptMarkCondWithoutCmp();
+
EmitAluLoadOpers(context);
context.Emit(OpCodes.Add);
diff --git a/ChocolArm64/Translation/ILEmitterCtx.cs b/ChocolArm64/Translation/ILEmitterCtx.cs
index ef63e60c..fa65bbf9 100644
--- a/ChocolArm64/Translation/ILEmitterCtx.cs
+++ b/ChocolArm64/Translation/ILEmitterCtx.cs
@@ -312,19 +312,57 @@ namespace ChocolArm64.Translation
public void EmitCondBranch(ILLabel target, Condition cond)
{
- OpCode ilOp;
-
- int intCond = (int)cond;
-
if (_optOpLastCompare != null &&
_optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
{
- Ldloc(CmpOptTmp1Index, IoType.Int, _optOpLastCompare.RegisterSize);
- Ldloc(CmpOptTmp2Index, IoType.Int, _optOpLastCompare.RegisterSize);
+ if (_optOpLastCompare.Emitter == InstEmit.Subs)
+ {
+ Ldloc(CmpOptTmp1Index, IoType.Int, _optOpLastCompare.RegisterSize);
+ Ldloc(CmpOptTmp2Index, IoType.Int, _optOpLastCompare.RegisterSize);
+
+ Emit(_branchOps[cond], target);
+
+ return;
+ }
+ else if (_optOpLastCompare.Emitter == InstEmit.Adds && cond != Condition.GeUn
+ && cond != Condition.LtUn
+ && cond != Condition.GtUn
+ && cond != Condition.LeUn)
+ {
+ //There are several limitations that needs to be taken into account for CMN comparisons:
+ //* The unsigned comparisons are not valid, as they depend on the
+ //carry flag value, and they will have different values for addition and
+ //subtraction. For addition, it's carry, and for subtraction, it's borrow.
+ //So, we need to make sure we're not doing a unsigned compare for the CMN case.
+ //* We can only do the optimization for the immediate variants,
+ //because when the second operand value is exactly INT_MIN, we can't
+ //negate the value as theres no positive counterpart.
+ //Such invalid values can't be encoded on the immediate encodings.
+ if (_optOpLastCompare is IOpCodeAluImm64 op)
+ {
+ Ldloc(CmpOptTmp1Index, IoType.Int, _optOpLastCompare.RegisterSize);
+
+ if (_optOpLastCompare.RegisterSize == RegisterSize.Int32)
+ {
+ EmitLdc_I4((int)-op.Imm);
+ }
+ else
+ {
+ EmitLdc_I8(-op.Imm);
+ }
- ilOp = _branchOps[cond];
+ Emit(_branchOps[cond], target);
+
+ return;
+ }
+ }
}
- else if (intCond < 14)
+
+ OpCode ilOp;
+
+ int intCond = (int)cond;
+
+ if (intCond < 14)
{
int condTrue = intCond >> 1;