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) { + if (_optOpLastCompare != null && + _optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond)) + { + 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); + } + + Emit(_branchOps[cond], target); + + return; + } + } + } + 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); - - ilOp = _branchOps[cond]; - } - else if (intCond < 14) + if (intCond < 14) { int condTrue = intCond >> 1;