diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 4d184ad02..b3deb77d2 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -396,6 +396,7 @@ namespace ICSharpCode.Decompiler.Ast #endregion #region Comparison case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); + case ILCode.Cne: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); case ILCode.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case ILCode.Cgt_Un: { // can also mean Inequality, when used with object references @@ -405,8 +406,12 @@ namespace ICSharpCode.Decompiler.Ast else return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); } - case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case ILCode.Cge: + case ILCode.Cge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case ILCode.Clt: case ILCode.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case ILCode.Cle: + case ILCode.Cle_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); #endregion #region Logical case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 8b5cdc0bf..5268b9519 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -42,6 +42,7 @@ namespace ICSharpCode.Decompiler.ILAst SimplifyTernaryOperator, SimplifyNullCoalescing, JoinBasicBlocks, + SimplifyLogicNot, TransformDecimalCtorToConstant, SimplifyLdObjAndStObj, TransformArrayInitializers, @@ -129,6 +130,9 @@ namespace ICSharpCode.Decompiler.ILAst if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return; modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks); + + if (abortBeforeStep == ILAstOptimizationStep.SimplifyLogicNot) return; + modified |= block.RunOptimization(SimplifyLogicNot); if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return; modified |= block.RunOptimization(TransformDecimalCtorToConstant); @@ -283,27 +287,30 @@ namespace ICSharpCode.Decompiler.ILAst for (int i = 0; i < block.Body.Count; i++) { ILExpression expr = block.Body[i] as ILExpression; if (expr != null && expr.Prefixes == null) { + ILCode op; switch(expr.Code) { case ILCode.Switch: case ILCode.Brtrue: expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges); expr.ILRanges.Clear(); continue; - case ILCode.__Brfalse: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, expr.Arguments.Single())); break; - case ILCode.__Beq: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Ceq, null, expr.Arguments)); break; - case ILCode.__Bne_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Ceq, null, expr.Arguments))); break; - case ILCode.__Bgt: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Cgt, null, expr.Arguments)); break; - case ILCode.__Bgt_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Cgt_Un, null, expr.Arguments)); break; - case ILCode.__Ble: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Cgt, null, expr.Arguments))); break; - case ILCode.__Ble_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Cgt_Un, null, expr.Arguments))); break; - case ILCode.__Blt: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Clt, null, expr.Arguments)); break; - case ILCode.__Blt_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Clt_Un, null, expr.Arguments)); break; - case ILCode.__Bge: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Clt, null, expr.Arguments))); break; - case ILCode.__Bge_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Clt_Un, null, expr.Arguments))); break; + case ILCode.__Brfalse: op = ILCode.LogicNot; break; + case ILCode.__Beq: op = ILCode.Ceq; break; + case ILCode.__Bne_Un: op = ILCode.Cne; break; + case ILCode.__Bgt: op = ILCode.Cgt; break; + case ILCode.__Bgt_Un: op = ILCode.Cgt_Un; break; + case ILCode.__Ble: op = ILCode.Cle; break; + case ILCode.__Ble_Un: op = ILCode.Cle_Un; break; + case ILCode.__Blt: op = ILCode.Clt; break; + case ILCode.__Blt_Un: op = ILCode.Clt_Un; break; + case ILCode.__Bge: op = ILCode.Cge; break; + case ILCode.__Bge_Un: op = ILCode.Cge_Un; break; default: continue; } - ((ILExpression)block.Body[i]).Arguments.Single().ILRanges.AddRange(expr.ILRanges); + var newExpr = new ILExpression(op, null, expr.Arguments); + block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, newExpr); + newExpr.ILRanges = expr.ILRanges; } } } diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 36bc56d52..ddddf2b70 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -247,6 +247,11 @@ namespace ICSharpCode.Decompiler.ILAst Readonly, // Virtual codes - defined for convenience + Cne, + Cge, + Cge_Un, + Cle, + Cle_Un, Ldexception, // Operand holds the CatchType for catch handler, null for filter LogicNot, LogicAnd, diff --git a/ICSharpCode.Decompiler/ILAst/NullableOperators.cs b/ICSharpCode.Decompiler/ILAst/NullableOperators.cs index 96e490902..5a638fc34 100644 --- a/ICSharpCode.Decompiler/ILAst/NullableOperators.cs +++ b/ICSharpCode.Decompiler/ILAst/NullableOperators.cs @@ -96,11 +96,6 @@ namespace ICSharpCode.Decompiler.ILAst { return e.Code == this.code; } - - public override ILExpression BuildNew(ref PatternMatcher pm, ILExpression[] args) - { - return new ILExpression(this.code, null, args); - } } sealed class MethodPattern : Pattern @@ -144,10 +139,17 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Ceq: if (!equals.GetValueOrDefault() || custom.GetValueOrDefault()) return false; break; + case ILCode.Cne: + if (equals.GetValueOrDefault(true) || custom.GetValueOrDefault()) return false; + break; case ILCode.Cgt: case ILCode.Cgt_Un: + case ILCode.Cge: + case ILCode.Cge_Un: case ILCode.Clt: case ILCode.Clt_Un: + case ILCode.Cle: + case ILCode.Cle_Un: if (equals != null || custom.GetValueOrDefault()) return false; break; case ILCode.Call: @@ -219,37 +221,17 @@ namespace ICSharpCode.Decompiler.ILAst } } - sealed class PrimitivePattern : Pattern - { - readonly ILCode code; - readonly object operand; - - public PrimitivePattern(ILCode code, object operand, params Pattern[] arguments) - : base(arguments) - { - this.code = code; - this.operand = operand; - } - - public override bool Match(ref PatternMatcher pm, ILExpression e) - { - return e.Code == code && object.Equals(e.Operand, this.operand); - } - } - static readonly Tuple GetValueOrDefault = new Tuple("GetValueOrDefault", "Nullable`1", "System"); static readonly Tuple get_HasValue = new Tuple("get_HasValue", "Nullable`1", "System"); static readonly Pattern VariableRefA = new VariablePattern(ILCode.Ldloca, false), VariableRefB = new VariablePattern(ILCode.Ldloca, true); static readonly Pattern VariableA = new VariablePattern(ILCode.Ldloc, false), VariableB = new VariablePattern(ILCode.Ldloc, true); static readonly Pattern VariableAHasValue = new MethodPattern(ILCode.CallGetter, get_HasValue, VariableRefA); static readonly Pattern VariableAGetValueOrDefault = new MethodPattern(ILCode.Call, GetValueOrDefault, VariableRefA); - static readonly Pattern NotVariableAHasValue = new ILPattern(ILCode.Ceq, VariableAHasValue, new PrimitivePattern(ILCode.Ldc_I4, 0)); static readonly Pattern VariableBHasValue = new MethodPattern(ILCode.CallGetter, get_HasValue, VariableRefB); static readonly Pattern VariableBGetValueOrDefault = new MethodPattern(ILCode.Call, GetValueOrDefault, VariableRefB); - static readonly Pattern NotVariableBHasValue = new ILPattern(ILCode.Ceq, VariableBHasValue, new PrimitivePattern(ILCode.Ldc_I4, 0)); static readonly Pattern CeqHasValue = new ILPattern(ILCode.Ceq, VariableAHasValue, VariableBHasValue); + static readonly Pattern CneHasValue = new ILPattern(ILCode.Cne, VariableAHasValue, VariableBHasValue); static readonly Pattern AndHasValue = new ILPattern(ILCode.And, VariableAHasValue, VariableBHasValue); - static readonly Pattern NotCeqHasValue = new ILPattern(ILCode.Ceq, CeqHasValue, new PrimitivePattern(ILCode.Ldc_I4, 0)); static readonly Pattern[] LoadValuesNN = new[] { VariableAGetValueOrDefault, VariableBGetValueOrDefault }; static OperatorPattern OperatorNN(bool? equals = null, bool? custom = null) @@ -272,55 +254,43 @@ namespace ICSharpCode.Decompiler.ILAst static readonly Pattern[] Comparisons = new Pattern[] { /* both operands nullable */ // == (Primitive, Decimal) - !!OperatorNN(equals: true) & CeqHasValue, + OperatorNN(equals: true) & CeqHasValue, // == (Struct) - !!CeqHasValue & (!VariableAHasValue | OperatorNN(equals: true, custom: true)), - // != (Primitive) - !OperatorNN(equals: true, custom: false) | NotCeqHasValue, - // != (Decimal) - OperatorNN(equals: false, custom: true) | NotCeqHasValue, + CeqHasValue & (!VariableAHasValue | OperatorNN(equals: true, custom: true)), + // != (Primitive, Decimal) + OperatorNN(equals: false) | CneHasValue, // != (Struct) - !CeqHasValue | (!!VariableAHasValue & OperatorNN(equals: false, custom: true)), - // > , < (Primitive, Decimal), >= , <= (Decimal) - !!OperatorNN() & AndHasValue, - // >= , <= (Primitive) - !OperatorNN(custom: false) & AndHasValue, + CneHasValue | (VariableAHasValue & OperatorNN(equals: false, custom: true)), + // > , < , >= , <= (Primitive, Decimal) + OperatorNN() & AndHasValue, // > , < , >= , <= (Struct) AndHasValue & OperatorNN(custom: true), /* only first operand nullable */ // == (Primitive, Decimal) - !!OperatorNV(equals: true) & VariableAHasValue, + OperatorNV(equals: true) & VariableAHasValue, // == (Struct) - !!VariableAHasValue & OperatorNV(equals: true, custom: true), - // != (Primitive) - !OperatorNV(equals: true, custom: false) | NotVariableAHasValue, - // != (Decimal) - OperatorNV(equals: false, custom: true) | NotVariableAHasValue, + VariableAHasValue & OperatorNV(equals: true, custom: true), + // != (Primitive, Decimal) + OperatorNV(equals: false) | !VariableAHasValue, // != (Struct) !VariableAHasValue | OperatorNV(equals: false, custom: true), - // > , < (Primitive, Decimal), >= , <= (Decimal) - !!OperatorNV() & VariableAHasValue, - // >= , <= (Primitive) - !OperatorNV(custom: false) & VariableAHasValue, + // > , <, >= , <= (Primitive, Decimal) + OperatorNV() & VariableAHasValue, // > , < , >= , <= (Struct) VariableAHasValue & OperatorNV(custom: true), /* only second operand nullable */ // == (Primitive, Decimal) - !!OperatorVN(equals: true) & VariableBHasValue, + OperatorVN(equals: true) & VariableBHasValue, // == (Struct) - !!VariableBHasValue & OperatorVN(equals: true, custom: true), - // != (Primitive) - !OperatorVN(equals: true, custom: false) | NotVariableBHasValue, - // != (Decimal) - OperatorVN(equals: false, custom: true) | NotVariableBHasValue, + VariableBHasValue & OperatorVN(equals: true, custom: true), + // != (Primitive, Decimal) + OperatorVN(equals: false) | !VariableBHasValue, // != (Struct) !VariableBHasValue | OperatorVN(equals: false, custom: true), - // > , < (Primitive, Decimal), >= , <= (Decimal) - !!OperatorVN() & VariableBHasValue, - // >= , <= (Primitive) - !OperatorVN(custom: false) & VariableBHasValue, + // > , <, >= , <= (Primitive, Decimal) + OperatorVN() & VariableBHasValue, // > , < , >= , <= (Struct) VariableBHasValue & OperatorVN(custom: true), }; @@ -345,7 +315,6 @@ namespace ICSharpCode.Decompiler.ILAst } static readonly Pattern OperatorVariableAB = new OperatorPattern(VariableA, VariableB); - static readonly Pattern NotOperatorVariableAB = !OperatorVariableAB; public static bool Simplify(ILExpression expr) { @@ -355,12 +324,12 @@ namespace ICSharpCode.Decompiler.ILAst for (int i = 0; i < ps.Length; i++) { var pm = new PatternMatcher(); if (!pm.Match(ps[i], expr)) continue; - var pi = i % 8; - var n = pm.BuildNew(pi != 2 && pi != 6 ? OperatorVariableAB : NotOperatorVariableAB, expr); + var n = pm.BuildNew(OperatorVariableAB, expr); expr.Code = n.Code; expr.Operand = n.Operand; expr.Arguments = n.Arguments; expr.ILRanges = n.ILRanges; + expr.InferredType = n.InferredType; return true; } return false; diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index 270b9f7d4..c6d227ca3 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -821,5 +821,65 @@ namespace ICSharpCode.Decompiler.ILAst return false; } #endregion + + #region SimplifyLogicNot + static bool SimplifyLogicNot(List body, ILExpression expr, int pos) + { + bool modified = false; + expr = SimplifyLogicNot(expr, ref modified); + Debug.Assert(expr == null); + return modified; + } + + static ILExpression SimplifyLogicNot(ILExpression expr, ref bool modified) + { + if (expr.Code == ILCode.Ceq) { + var a = expr.Arguments[1]; + if (a.Code == ILCode.Ldc_I4 && TypeAnalysis.IsBoolean(a.InferredType) && TypeAnalysis.IsBoolean(expr.Arguments[0].InferredType)) { + expr.Code = ILCode.LogicNot; + expr.ILRanges.AddRange(a.ILRanges); + expr.Arguments.RemoveAt(1); + modified = true; + } + } + ILExpression res = null; + while (expr.Code == ILCode.LogicNot) { + var a = expr.Arguments[0]; + ILCode c = 0; + switch (a.Code) { + case ILCode.LogicNot: + res = a.Arguments[0]; + res.ILRanges.AddRange(expr.ILRanges); + res.ILRanges.AddRange(a.ILRanges); + expr = res; + continue; + case ILCode.Ceq: c = ILCode.Cne; break; + case ILCode.Cne: c = ILCode.Ceq; break; + case ILCode.Cgt: c = ILCode.Cle; break; + case ILCode.Cgt_Un: c = ILCode.Cle_Un; break; + case ILCode.Cge: c = ILCode.Clt; break; + case ILCode.Cge_Un: c = ILCode.Clt_Un; break; + case ILCode.Clt: c = ILCode.Cge; break; + case ILCode.Clt_Un: c = ILCode.Cge_Un; break; + case ILCode.Cle: c = ILCode.Cgt; break; + case ILCode.Cle_Un: c = ILCode.Cgt_Un; break; + } + if (c == 0) break; + res = a; + res.Code = c; + res.ILRanges.AddRange(expr.ILRanges); + expr = res; + break; + } + for (int i = 0; i < expr.Arguments.Count; i++) { + var a = SimplifyLogicNot(expr.Arguments[i], ref modified); + if (a != null) { + expr.Arguments[i] = a; + modified = true; + } + } + return res; + } + #endregion } } diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 1aa21a276..9474725d6 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -292,7 +292,10 @@ namespace ICSharpCode.Decompiler.ILAst ILVariable v = (ILVariable)expr.Operand; if (forceInferChildren) { // do not use 'expectedType' in here! - InferTypeForExpression(expr.Arguments.Single(), v.Type); + var arg = expr.Arguments.Single(); + InferTypeForExpression(arg, v.Type); + // there is no conversion from int to bool in C# and ldc.i4.0/ldc.i4.1 is used for variable initialization and logic negations (this needs a better solution - a new pass with the fixed variable type) + if (IsBoolean(arg.InferredType) && v.Type.MetadataType == MetadataType.Int32) v.Type = arg.ExpectedType = arg.InferredType; } return v.Type; } @@ -691,16 +694,21 @@ namespace ICSharpCode.Decompiler.ILAst #endregion #region Comparison instructions case ILCode.Ceq: + case ILCode.Cne: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, null, null); return typeSystem.Boolean; case ILCode.Clt: case ILCode.Cgt: + case ILCode.Cle: + case ILCode.Cge: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, true, null); return typeSystem.Boolean; case ILCode.Clt_Un: case ILCode.Cgt_Un: + case ILCode.Cle_Un: + case ILCode.Cge_Un: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, false, null); return typeSystem.Boolean; diff --git a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs index 71971273c..8bfebf71a 100644 --- a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs @@ -634,13 +634,14 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Ldc_I4: return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand); case ILCode.Ceq: + case ILCode.Cne: left = Eval(expr.Arguments[0]); right = Eval(expr.Arguments[1]); if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant) throw new YieldAnalysisFailedException(); // bool: (state + left.Constant == right.Constant) // bool: (state == right.Constant - left.Constant) - return new SymbolicValue(SymbolicValueType.StateEquals, unchecked ( right.Constant - left.Constant )); + return new SymbolicValue(expr.Code == ILCode.Ceq ? SymbolicValueType.StateEquals : SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant)); case ILCode.LogicNot: SymbolicValue val = Eval(expr.Arguments[0]); if (val.Type == SymbolicValueType.StateEquals)