Browse Source

Make comparisons in the ILAst more similar to C# comparisons.

Add some logic.not simplification to ExpressionTransforms.
pull/728/head
Daniel Grunwald 9 years ago
parent
commit
f1021d18af
  1. 118
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 164
      ICSharpCode.Decompiler/CSharp/Transforms/PushNegation.cs
  3. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 89
      ICSharpCode.Decompiler/IL/ILReader.cs
  5. 5
      ICSharpCode.Decompiler/IL/ILTypeExtensions.cs
  6. 205
      ICSharpCode.Decompiler/IL/Instructions.cs
  7. 23
      ICSharpCode.Decompiler/IL/Instructions.tt
  8. 64
      ICSharpCode.Decompiler/IL/Instructions/BinaryComparisonInstruction.cs
  9. 155
      ICSharpCode.Decompiler/IL/Instructions/Comp.cs
  10. 75
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

118
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -289,14 +289,33 @@ namespace ICSharpCode.Decompiler.CSharp @@ -289,14 +289,33 @@ namespace ICSharpCode.Decompiler.CSharp
return Assignment(ConvertVariable(inst.Variable).WithoutILInstruction(), translatedValue).WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitCeq(Ceq inst)
protected internal override TranslatedExpression VisitComp(Comp inst)
{
if (inst.Kind.IsEqualityOrInequality()) {
bool negateOutput;
var result = TranslateCeq(inst, out negateOutput);
if (negateOutput)
return LogicNot(result).WithILInstruction(inst);
else
return result;
} else {
return TranslateComp(inst);
}
}
/// <summary>
/// Translates the equality comparison between left and right.
/// </summary>
TranslatedExpression TranslateCeq(Comp inst, out bool negateOutput)
{
// Translate '(e as T) == null' to '!(e is T)'.
// This is necessary for correctness when T is a value type.
if (inst.Left.OpCode == OpCode.IsInst && inst.Right.OpCode == OpCode.LdNull) {
return LogicNot(IsType((IsInst)inst.Left)).WithILInstruction(inst);
negateOutput = inst.Kind == ComparisonKind.Equality;
return IsType((IsInst)inst.Left);
} else if (inst.Right.OpCode == OpCode.IsInst && inst.Left.OpCode == OpCode.LdNull) {
return LogicNot(IsType((IsInst)inst.Right)).WithILInstruction(inst);
negateOutput = inst.Kind == ComparisonKind.Equality;
return IsType((IsInst)inst.Right);
}
var left = Translate(inst.Left);
@ -304,21 +323,37 @@ namespace ICSharpCode.Decompiler.CSharp @@ -304,21 +323,37 @@ namespace ICSharpCode.Decompiler.CSharp
// Remove redundant bool comparisons
if (left.Type.IsKnownType(KnownTypeCode.Boolean)) {
if (inst.Right.MatchLdcI4(0))
return LogicNot(left).WithILInstruction(inst); // 'b == 0' => '!b'
if (inst.Right.MatchLdcI4(1))
return left; // 'b == 1' => 'b'
if (inst.Right.MatchLdcI4(0)) {
// 'b == 0' => '!b'
// 'b != 0' => 'b'
negateOutput = inst.Kind == ComparisonKind.Equality;
return left;
}
if (inst.Right.MatchLdcI4(1)) {
// 'b == 1' => 'b'
// 'b != 1' => '!b'
negateOutput = inst.Kind == ComparisonKind.Inequality;
return left;
}
} else if (right.Type.IsKnownType(KnownTypeCode.Boolean)) {
if (inst.Left.MatchLdcI4(0))
return LogicNot(right).WithILInstruction(inst); // '0 == b' => '!b'
if (inst.Left.MatchLdcI4(1))
return right; // '1 == b' => 'b'
if (inst.Left.MatchLdcI4(0)) {
// '0 == b' => '!b'
// '0 != b' => 'b'
negateOutput = inst.Kind == ComparisonKind.Equality;
return right;
}
if (inst.Left.MatchLdcI4(1)) {
// '0 == b' => '!b'
// '0 != b' => 'b'
negateOutput = inst.Kind == ComparisonKind.Equality;
return right;
}
}
var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.Equality, left.ResolveResult, right.ResolveResult)
var rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult)
as OperatorResolveResult;
if (rr == null || rr.IsError || rr.UserDefinedOperatorMethod != null
|| rr.Operands[0].Type.GetStackType() != inst.OpType)
|| rr.Operands[0].Type.GetStackType() != inst.InputType)
{
var targetType = TypeUtils.GetLargerType(left.Type, right.Type);
if (targetType.Equals(left.Type)) {
@ -330,75 +365,40 @@ namespace ICSharpCode.Decompiler.CSharp @@ -330,75 +365,40 @@ namespace ICSharpCode.Decompiler.CSharp
BinaryOperatorExpression.GetLinqNodeType(BinaryOperatorType.Equality, false),
left.ResolveResult, right.ResolveResult);
}
return new BinaryOperatorExpression(left.Expression, BinaryOperatorType.Equality, right.Expression)
negateOutput = false;
return new BinaryOperatorExpression(left.Expression, inst.Kind.ToBinaryOperatorType(), right.Expression)
.WithILInstruction(inst)
.WithRR(rr);
}
protected internal override TranslatedExpression VisitClt(Clt inst)
{
return Comparison(inst, BinaryOperatorType.LessThan);
}
protected internal override TranslatedExpression VisitCgt(Cgt inst)
{
return Comparison(inst, BinaryOperatorType.GreaterThan);
}
protected internal override TranslatedExpression VisitClt_Un(Clt_Un inst)
{
return Comparison(inst, BinaryOperatorType.LessThan, un: true);
}
protected internal override TranslatedExpression VisitCgt_Un(Cgt_Un inst)
{
return Comparison(inst, BinaryOperatorType.GreaterThan, un: true);
}
TranslatedExpression Comparison(BinaryComparisonInstruction inst, BinaryOperatorType op, bool un = false)
/// <summary>
/// Handle Comp instruction, operators other than equality/inequality.
/// </summary>
TranslatedExpression TranslateComp(Comp inst)
{
var left = Translate(inst.Left);
var right = Translate(inst.Right);
// Ensure the inputs have the correct sign:
KnownTypeCode inputType = KnownTypeCode.None;
switch (inst.OpType) {
switch (inst.InputType) {
case StackType.I: // In order to generate valid C# we need to treat (U)IntPtr as (U)Int64 in comparisons.
case StackType.I8:
inputType = un ? KnownTypeCode.UInt64 : KnownTypeCode.Int64;
inputType = inst.Sign == Sign.Unsigned ? KnownTypeCode.UInt64 : KnownTypeCode.Int64;
break;
case StackType.I4:
inputType = un ? KnownTypeCode.UInt32 : KnownTypeCode.Int32;
break;
case StackType.F:
if (un) {
// for floats, _Un means "unordered":
// clt_un returns 1 if left < right or if either input is not-a-number
// The C# operators never return true for NaN, so we need to use a negation:
// clt_un => !(left >= right)
// cgt_un => !(left <= right)
if (op == BinaryOperatorType.LessThan)
op = BinaryOperatorType.GreaterThanOrEqual;
else if (op == BinaryOperatorType.GreaterThan)
op = BinaryOperatorType.LessThanOrEqual;
else
throw new ArgumentException("op");
}
inputType = inst.Sign == Sign.Unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32;
break;
}
if (inputType != KnownTypeCode.None) {
left = left.ConvertTo(compilation.FindType(inputType), this);
right = right.ConvertTo(compilation.FindType(inputType), this);
}
var result = new BinaryOperatorExpression(left.Expression, op, right.Expression)
var op = inst.Kind.ToBinaryOperatorType();
return new BinaryOperatorExpression(left.Expression, op, right.Expression)
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(compilation.FindType(TypeCode.Boolean),
BinaryOperatorExpression.GetLinqNodeType(op, false),
left.ResolveResult, right.ResolveResult));
if (un && inst.OpType == StackType.F) {
// add negation if we turned around the operator symbol above
result = LogicNot(result).WithILInstruction(inst);
}
return result;
}
ExpressionWithResolveResult Assignment(TranslatedExpression left, TranslatedExpression right)

164
ICSharpCode.Decompiler/CSharp/Transforms/PushNegation.cs

@ -1,164 +0,0 @@ @@ -1,164 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
public class PushNegation: DepthFirstAstVisitor<object, object>, IAstTransform
{
sealed class LiftedOperator { }
/// <summary>
/// Annotation for lifted operators that cannot be transformed by PushNegation
/// </summary>
public static readonly object LiftedOperatorAnnotation = new LiftedOperator();
public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unary, object data)
{
// lifted operators can't be transformed
if (unary.Annotation<LiftedOperator>() != null || unary.Expression.Annotation<LiftedOperator>() != null)
return base.VisitUnaryOperatorExpression(unary, data);
// Remove double negation
// !!a
if (unary.Operator == UnaryOperatorType.Not &&
unary.Expression is UnaryOperatorExpression &&
(unary.Expression as UnaryOperatorExpression).Operator == UnaryOperatorType.Not)
{
AstNode newNode = (unary.Expression as UnaryOperatorExpression).Expression;
unary.ReplaceWith(newNode);
return newNode.AcceptVisitor(this, data);
}
// Push through binary operation
// !((a) op (b))
BinaryOperatorExpression binaryOp = unary.Expression as BinaryOperatorExpression;
if (unary.Operator == UnaryOperatorType.Not && binaryOp != null) {
bool successful = true;
switch (binaryOp.Operator) {
case BinaryOperatorType.Equality:
binaryOp.Operator = BinaryOperatorType.InEquality;
break;
case BinaryOperatorType.InEquality:
binaryOp.Operator = BinaryOperatorType.Equality;
break;
case BinaryOperatorType.GreaterThan: // TODO: these are invalid for floats (stupid NaN)
binaryOp.Operator = BinaryOperatorType.LessThanOrEqual;
break;
case BinaryOperatorType.GreaterThanOrEqual:
binaryOp.Operator = BinaryOperatorType.LessThan;
break;
case BinaryOperatorType.LessThanOrEqual:
binaryOp.Operator = BinaryOperatorType.GreaterThan;
break;
case BinaryOperatorType.LessThan:
binaryOp.Operator = BinaryOperatorType.GreaterThanOrEqual;
break;
default:
successful = false;
break;
}
if (successful) {
unary.ReplaceWith(binaryOp);
return binaryOp.AcceptVisitor(this, data);
}
successful = true;
switch (binaryOp.Operator) {
case BinaryOperatorType.ConditionalAnd:
binaryOp.Operator = BinaryOperatorType.ConditionalOr;
break;
case BinaryOperatorType.ConditionalOr:
binaryOp.Operator = BinaryOperatorType.ConditionalAnd;
break;
default:
successful = false;
break;
}
if (successful) {
binaryOp.Left.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
binaryOp.Right.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
unary.ReplaceWith(binaryOp);
return binaryOp.AcceptVisitor(this, data);
}
}
return base.VisitUnaryOperatorExpression(unary, data);
}
readonly static AstNode asCastIsNullPattern = new BinaryOperatorExpression(
new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
BinaryOperatorType.Equality,
new NullReferenceExpression()
);
readonly static AstNode asCastIsNotNullPattern = new BinaryOperatorExpression(
new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
BinaryOperatorType.InEquality,
new NullReferenceExpression()
);
public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data)
{
// lifted operators can't be transformed
if (binaryOperatorExpression.Annotation<LiftedOperator>() != null)
return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
BinaryOperatorType op = binaryOperatorExpression.Operator;
bool? rightOperand = null;
if (binaryOperatorExpression.Right is PrimitiveExpression)
rightOperand = ((PrimitiveExpression)binaryOperatorExpression.Right).Value as bool?;
if (op == BinaryOperatorType.Equality && rightOperand == true || op == BinaryOperatorType.InEquality && rightOperand == false) {
// 'b == true' or 'b != false' is useless
binaryOperatorExpression.Left.AcceptVisitor(this, data);
binaryOperatorExpression.ReplaceWith(binaryOperatorExpression.Left);
return null;
} else if (op == BinaryOperatorType.Equality && rightOperand == false || op == BinaryOperatorType.InEquality && rightOperand == true) {
// 'b == false' or 'b != true' is a negation:
Expression left = binaryOperatorExpression.Left;
left.Remove();
UnaryOperatorExpression uoe = new UnaryOperatorExpression(UnaryOperatorType.Not, left);
binaryOperatorExpression.ReplaceWith(uoe);
return uoe.AcceptVisitor(this, data);
} else {
bool negate = false;
Match m = asCastIsNotNullPattern.Match(binaryOperatorExpression);
if (!m.Success) {
m = asCastIsNullPattern.Match(binaryOperatorExpression);
negate = true;
}
if (m.Success) {
Expression expr = m.Get<Expression>("expr").Single().Detach().IsType(m.Get<AstType>("type").Single().Detach());
if (negate)
expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr);
binaryOperatorExpression.ReplaceWith(expr);
return expr.AcceptVisitor(this, data);
} else {
return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
}
}
}
void IAstTransform.Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -97,7 +97,7 @@ @@ -97,7 +97,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>Instructions.tt</DependentUpon>
</Compile>
<Compile Include="IL\Instructions\BinaryComparisonInstruction.cs" />
<Compile Include="IL\Instructions\Comp.cs" />
<Compile Include="IL\Instructions\BinaryNumericInstruction.cs" />
<Compile Include="IL\Instructions\Block.cs" />
<Compile Include="IL\Instructions\BlockContainer.cs" />

89
ICSharpCode.Decompiler/IL/ILReader.cs

@ -361,45 +361,45 @@ namespace ICSharpCode.Decompiler.IL @@ -361,45 +361,45 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Arglist:
return Push(new Arglist());
case ILOpCode.Beq:
return DecodeComparisonBranch(false, OpCode.Ceq, OpCode.Ceq, false);
return DecodeComparisonBranch(false, ComparisonKind.Equality);
case ILOpCode.Beq_S:
return DecodeComparisonBranch(true, OpCode.Ceq, OpCode.Ceq, false);
return DecodeComparisonBranch(true, ComparisonKind.Equality);
case ILOpCode.Bge:
return DecodeComparisonBranch(false, OpCode.Clt, OpCode.Clt_Un, true);
return DecodeComparisonBranch(false, ComparisonKind.GreaterThanOrEqual);
case ILOpCode.Bge_S:
return DecodeComparisonBranch(true, OpCode.Clt, OpCode.Clt_Un, true);
return DecodeComparisonBranch(true, ComparisonKind.GreaterThanOrEqual);
case ILOpCode.Bge_Un:
return DecodeComparisonBranch(false, OpCode.Clt_Un, OpCode.Clt, true);
return DecodeComparisonBranch(false, ComparisonKind.GreaterThanOrEqual, un: true);
case ILOpCode.Bge_Un_S:
return DecodeComparisonBranch(true, OpCode.Clt_Un, OpCode.Clt, true);
return DecodeComparisonBranch(true, ComparisonKind.GreaterThanOrEqual, un: true);
case ILOpCode.Bgt:
return DecodeComparisonBranch(false, OpCode.Cgt, OpCode.Cgt, false);
return DecodeComparisonBranch(false, ComparisonKind.GreaterThan);
case ILOpCode.Bgt_S:
return DecodeComparisonBranch(true, OpCode.Cgt, OpCode.Cgt, false);
return DecodeComparisonBranch(true, ComparisonKind.GreaterThan);
case ILOpCode.Bgt_Un:
return DecodeComparisonBranch(false, OpCode.Cgt_Un, OpCode.Clt_Un, false);
return DecodeComparisonBranch(false, ComparisonKind.GreaterThan, un: true);
case ILOpCode.Bgt_Un_S:
return DecodeComparisonBranch(true, OpCode.Cgt_Un, OpCode.Clt_Un, false);
return DecodeComparisonBranch(true, ComparisonKind.GreaterThan, un: true);
case ILOpCode.Ble:
return DecodeComparisonBranch(false, OpCode.Cgt, OpCode.Cgt_Un, true);
return DecodeComparisonBranch(false, ComparisonKind.LessThanOrEqual);
case ILOpCode.Ble_S:
return DecodeComparisonBranch(true, OpCode.Cgt, OpCode.Cgt_Un, true);
return DecodeComparisonBranch(true, ComparisonKind.LessThanOrEqual);
case ILOpCode.Ble_Un:
return DecodeComparisonBranch(false, OpCode.Cgt_Un, OpCode.Cgt, true);
return DecodeComparisonBranch(false, ComparisonKind.LessThanOrEqual, un: true);
case ILOpCode.Ble_Un_S:
return DecodeComparisonBranch(true, OpCode.Cgt_Un, OpCode.Cgt, true);
return DecodeComparisonBranch(true, ComparisonKind.LessThanOrEqual, un: true);
case ILOpCode.Blt:
return DecodeComparisonBranch(false, OpCode.Clt, OpCode.Clt, false);
return DecodeComparisonBranch(false, ComparisonKind.LessThan);
case ILOpCode.Blt_S:
return DecodeComparisonBranch(true, OpCode.Clt, OpCode.Clt, false);
return DecodeComparisonBranch(true, ComparisonKind.LessThan);
case ILOpCode.Blt_Un:
return DecodeComparisonBranch(false, OpCode.Clt_Un, OpCode.Clt_Un, false);
return DecodeComparisonBranch(false, ComparisonKind.LessThan, un: true);
case ILOpCode.Blt_Un_S:
return DecodeComparisonBranch(true, OpCode.Clt_Un, OpCode.Clt_Un, false);
return DecodeComparisonBranch(true, ComparisonKind.LessThan, un: true);
case ILOpCode.Bne_Un:
return DecodeComparisonBranch(false, OpCode.Ceq, OpCode.Ceq, true);
return DecodeComparisonBranch(false, ComparisonKind.Inequality, un: true);
case ILOpCode.Bne_Un_S:
return DecodeComparisonBranch(true, OpCode.Ceq, OpCode.Ceq, true);
return DecodeComparisonBranch(true, ComparisonKind.Inequality, un: true);
case ILOpCode.Br:
return DecodeUnconditionalBranch(false);
case ILOpCode.Br_S:
@ -421,15 +421,15 @@ namespace ICSharpCode.Decompiler.IL @@ -421,15 +421,15 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Calli:
throw new NotImplementedException();
case ILOpCode.Ceq:
return Push(Comparison(OpCode.Ceq, OpCode.Ceq));
return Push(Comparison(ComparisonKind.Equality));
case ILOpCode.Cgt:
return Push(Comparison(OpCode.Cgt, OpCode.Cgt));
return Push(Comparison(ComparisonKind.GreaterThan));
case ILOpCode.Cgt_Un:
return Push(Comparison(OpCode.Cgt_Un, OpCode.Cgt_Un));
return Push(Comparison(ComparisonKind.GreaterThan, un: true));
case ILOpCode.Clt:
return Push(Comparison(OpCode.Clt, OpCode.Clt));
return Push(Comparison(ComparisonKind.LessThan));
case ILOpCode.Clt_Un:
return Push(Comparison(OpCode.Clt_Un, OpCode.Clt_Un));
return Push(Comparison(ComparisonKind.LessThan, un: true));
case ILOpCode.Ckfinite:
return new Ckfinite(Peek());
case ILOpCode.Conv_I1:
@ -1017,29 +1017,35 @@ namespace ICSharpCode.Decompiler.IL @@ -1017,29 +1017,35 @@ namespace ICSharpCode.Decompiler.IL
return popCount;
}
ILInstruction Comparison(OpCode opCode_I, OpCode opCode_F)
ILInstruction Comparison(ComparisonKind kind, bool un = false)
{
var right = Pop();
var left = Pop();
// Based on Table 4: Binary Comparison or Branch Operation
OpCode opCode;
if (left.ResultType == StackType.F && right.ResultType == StackType.F)
opCode = opCode_F;
else
opCode = opCode_I;
return BinaryComparisonInstruction.Create(opCode, left, right);
if (left.ResultType == StackType.F && right.ResultType == StackType.F) {
if (un) {
// for floats, 'un' means 'unordered'
return new LogicNot(new Comp(kind.Negate(), Sign.None, left, right));
} else {
return new Comp(kind, Sign.None, left, right);
}
} else if (left.ResultType.IsIntegerType() && !kind.IsEqualityOrInequality()) {
// integer comparison where the sign matters
Debug.Assert(right.ResultType.IsIntegerType());
return new Comp(kind, un ? Sign.Unsigned : Sign.Signed, left, right);
} else {
// object reference or managed reference comparison
return new Comp(kind, Sign.None, left, right);
}
}
ILInstruction DecodeComparisonBranch(bool shortForm, OpCode comparisonOpCodeForInts, OpCode comparisonOpCodeForFloats, bool negate)
ILInstruction DecodeComparisonBranch(bool shortForm, ComparisonKind kind, bool un = false)
{
int start = reader.Position - 1;
var condition = Comparison(comparisonOpCodeForInts, comparisonOpCodeForFloats);
var condition = Comparison(kind, un);
int target = shortForm ? reader.ReadSByte() : reader.ReadInt32();
target += reader.Position;
condition.ILRange = new Interval(start, reader.Position);
if (negate) {
condition = new LogicNot(condition);
}
MarkBranchTarget(target);
return new IfInstruction(condition, new Branch(target));
}
@ -1052,18 +1058,15 @@ namespace ICSharpCode.Decompiler.IL @@ -1052,18 +1058,15 @@ namespace ICSharpCode.Decompiler.IL
switch (condition.ResultType) {
case StackType.O:
// introduce explicit comparison with null
condition = new Ceq(condition, new LdNull());
negate = !negate;
condition = new Comp(ComparisonKind.Inequality, Sign.None, condition, new LdNull());
break;
case StackType.I:
// introduce explicit comparison with 0
condition = new Ceq(condition, new LdcI4(0));
negate = !negate;
condition = new Comp(ComparisonKind.Inequality, Sign.None, condition, new LdcI4(0));
break;
case StackType.I8:
// introduce explicit comparison with 0
condition = new Ceq(condition, new LdcI8(0));
negate = !negate;
condition = new Comp(ComparisonKind.Inequality, Sign.None, condition, new LdcI8(0));
break;
}
if (negate) {

5
ICSharpCode.Decompiler/IL/ILTypeExtensions.cs

@ -90,5 +90,10 @@ namespace ICSharpCode.Decompiler.IL @@ -90,5 +90,10 @@ namespace ICSharpCode.Decompiler.IL
{
return ((MetadataType)primitiveType).GetStackType();
}
public static bool IsIntegerType(this PrimitiveType primitiveType)
{
return primitiveType.GetStackType().IsIntegerType();
}
}
}

205
ICSharpCode.Decompiler/IL/Instructions.cs

@ -79,16 +79,8 @@ namespace ICSharpCode.Decompiler.IL @@ -79,16 +79,8 @@ namespace ICSharpCode.Decompiler.IL
TryFault,
/// <summary>Breakpoint instruction</summary>
DebugBreak,
/// <summary>Compare equal. Returns 1 (of type I4) if two numbers or object references are equal; 0 otherwise.</summary>
Ceq,
/// <summary>Compare greater than. For integers, perform a signed comparison. For floating-point numbers, return 0 for unordered numbers.</summary>
Cgt,
/// <summary>Compare greater than (unordered/unsigned). For integers, perform a signed comparison. For floating-point numbers, return 1 for unordered numbers.</summary>
Cgt_Un,
/// <summary>Compare less than. For integers, perform a signed comparison. For floating-point numbers, return 0 for unordered numbers.</summary>
Clt,
/// <summary>Compare less than (unordered/unsigned). For integers, perform a signed comparison. For floating-point numbers, return 1 for unordered numbers.</summary>
Clt_Un,
/// <summary>Comparison. The inputs must be both integers; or both floats; or both object references. Object references can only be compared for equality or inequality. Floating-point comparisons evaluate to 0 (false) when an input is NaN, except for 'NaN != NaN' which evaluates to 1 (true).</summary>
Comp,
/// <summary>Non-virtual method call.</summary>
Call,
/// <summary>Virtual method call.</summary>
@ -1111,88 +1103,17 @@ namespace ICSharpCode.Decompiler.IL @@ -1111,88 +1103,17 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>Compare equal. Returns 1 (of type I4) if two numbers or object references are equal; 0 otherwise.</summary>
public sealed partial class Ceq : BinaryComparisonInstruction
/// <summary>Comparison. The inputs must be both integers; or both floats; or both object references. Object references can only be compared for equality or inequality. Floating-point comparisons evaluate to 0 (false) when an input is NaN, except for 'NaN != NaN' which evaluates to 1 (true).</summary>
public sealed partial class Comp : BinaryInstruction
{
public Ceq(ILInstruction left, ILInstruction right) : base(OpCode.Ceq, left, right)
{
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitCeq(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitCeq(this);
}
}
/// <summary>Compare greater than. For integers, perform a signed comparison. For floating-point numbers, return 0 for unordered numbers.</summary>
public sealed partial class Cgt : BinaryComparisonInstruction
{
public Cgt(ILInstruction left, ILInstruction right) : base(OpCode.Cgt, left, right)
{
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitCgt(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitCgt(this);
}
}
/// <summary>Compare greater than (unordered/unsigned). For integers, perform a signed comparison. For floating-point numbers, return 1 for unordered numbers.</summary>
public sealed partial class Cgt_Un : BinaryComparisonInstruction
{
public Cgt_Un(ILInstruction left, ILInstruction right) : base(OpCode.Cgt_Un, left, right)
{
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitCgt_Un(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitCgt_Un(this);
}
}
/// <summary>Compare less than. For integers, perform a signed comparison. For floating-point numbers, return 0 for unordered numbers.</summary>
public sealed partial class Clt : BinaryComparisonInstruction
{
public Clt(ILInstruction left, ILInstruction right) : base(OpCode.Clt, left, right)
{
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitClt(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitClt(this);
}
}
/// <summary>Compare less than (unordered/unsigned). For integers, perform a signed comparison. For floating-point numbers, return 1 for unordered numbers.</summary>
public sealed partial class Clt_Un : BinaryComparisonInstruction
{
public Clt_Un(ILInstruction left, ILInstruction right) : base(OpCode.Clt_Un, left, right)
{
}
public override StackType ResultType { get { return StackType.I4; } }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitClt_Un(this);
visitor.VisitComp(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitClt_Un(this);
return visitor.VisitComp(this);
}
}
@ -3259,23 +3180,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3259,23 +3180,7 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitCeq(Ceq inst)
{
Default(inst);
}
protected internal virtual void VisitCgt(Cgt inst)
{
Default(inst);
}
protected internal virtual void VisitCgt_Un(Cgt_Un inst)
{
Default(inst);
}
protected internal virtual void VisitClt(Clt inst)
{
Default(inst);
}
protected internal virtual void VisitClt_Un(Clt_Un inst)
protected internal virtual void VisitComp(Comp inst)
{
Default(inst);
}
@ -3573,23 +3478,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3573,23 +3478,7 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitCeq(Ceq inst)
{
return Default(inst);
}
protected internal virtual T VisitCgt(Cgt inst)
{
return Default(inst);
}
protected internal virtual T VisitCgt_Un(Cgt_Un inst)
{
return Default(inst);
}
protected internal virtual T VisitClt(Clt inst)
{
return Default(inst);
}
protected internal virtual T VisitClt_Un(Clt_Un inst)
protected internal virtual T VisitComp(Comp inst)
{
return Default(inst);
}
@ -3815,16 +3704,6 @@ namespace ICSharpCode.Decompiler.IL @@ -3815,16 +3704,6 @@ namespace ICSharpCode.Decompiler.IL
public static BinaryComparisonInstruction Create(OpCode opCode, ILInstruction left, ILInstruction right)
{
switch (opCode) {
case OpCode.Ceq:
return new Ceq(left, right);
case OpCode.Cgt:
return new Cgt(left, right);
case OpCode.Cgt_Un:
return new Cgt_Un(left, right);
case OpCode.Clt:
return new Clt(left, right);
case OpCode.Clt_Un:
return new Clt_Un(left, right);
default:
throw new ArgumentException("opCode is not a binary comparison instruction");
}
@ -3859,11 +3738,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3859,11 +3738,7 @@ namespace ICSharpCode.Decompiler.IL
"try.finally",
"try.fault",
"debug.break",
"ceq",
"cgt",
"cgt.un",
"clt",
"clt.un",
"comp",
"call",
"callvirt",
"ckfinite",
@ -4069,66 +3944,6 @@ namespace ICSharpCode.Decompiler.IL @@ -4069,66 +3944,6 @@ namespace ICSharpCode.Decompiler.IL
}
return false;
}
public bool MatchCeq(out ILInstruction left, out ILInstruction right)
{
var inst = this as Ceq;
if (inst != null) {
left = inst.Left;
right = inst.Right;
return true;
}
left = default(ILInstruction);
right = default(ILInstruction);
return false;
}
public bool MatchCgt(out ILInstruction left, out ILInstruction right)
{
var inst = this as Cgt;
if (inst != null) {
left = inst.Left;
right = inst.Right;
return true;
}
left = default(ILInstruction);
right = default(ILInstruction);
return false;
}
public bool MatchCgt_Un(out ILInstruction left, out ILInstruction right)
{
var inst = this as Cgt_Un;
if (inst != null) {
left = inst.Left;
right = inst.Right;
return true;
}
left = default(ILInstruction);
right = default(ILInstruction);
return false;
}
public bool MatchClt(out ILInstruction left, out ILInstruction right)
{
var inst = this as Clt;
if (inst != null) {
left = inst.Left;
right = inst.Right;
return true;
}
left = default(ILInstruction);
right = default(ILInstruction);
return false;
}
public bool MatchClt_Un(out ILInstruction left, out ILInstruction right)
{
var inst = this as Clt_Un;
if (inst != null) {
left = inst.Left;
right = inst.Right;
return true;
}
left = default(ILInstruction);
right = default(ILInstruction);
return false;
}
public bool MatchCkfinite(out ILInstruction argument)
{
var inst = this as Ckfinite;

23
ICSharpCode.Decompiler/IL/Instructions.tt

@ -88,16 +88,11 @@ @@ -88,16 +88,11 @@
BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new OpCode("debug.break", "Breakpoint instruction",
NoArguments, VoidResult, SideEffect),
new OpCode("ceq", "Compare equal. Returns 1 (of type I4) if two numbers or object references are equal; 0 otherwise.",
BinaryComparison),
new OpCode("cgt", "Compare greater than. For integers, perform a signed comparison. For floating-point numbers, return 0 for unordered numbers.",
BinaryComparison),
new OpCode("cgt.un", "Compare greater than (unordered/unsigned). For integers, perform a signed comparison. For floating-point numbers, return 1 for unordered numbers.",
CustomClassName("Cgt_Un"), BinaryComparison),
new OpCode("clt", "Compare less than. For integers, perform a signed comparison. For floating-point numbers, return 0 for unordered numbers.",
BinaryComparison),
new OpCode("clt.un", "Compare less than (unordered/unsigned). For integers, perform a signed comparison. For floating-point numbers, return 1 for unordered numbers.",
CustomClassName("Clt_Un"), BinaryComparison),
new OpCode("comp", "Comparison. The inputs must be both integers; or both floats; or both object references. "
+ "Object references can only be compared for equality or inequality. "
+ "Floating-point comparisons evaluate to 0 (false) when an input is NaN, except for 'NaN != NaN' which "
+ "evaluates to 1 (true).",
Binary, CustomConstructor, CustomWriteTo, ResultType("I4")),
new OpCode("call", "Non-virtual method call.", Call),
new OpCode("callvirt", "Virtual method call.",
CustomClassName("CallVirt"), Call),
@ -580,14 +575,6 @@ namespace ICSharpCode.Decompiler.IL @@ -580,14 +575,6 @@ namespace ICSharpCode.Decompiler.IL
opCode.BaseConstructorArguments.Add("compoundAssignmentType");
};
// BinaryNumeric trait: the instruction is derived from BinaryComparisonInstruction. Implies Binary and I4Result.
static Action<OpCode> BinaryComparison = opCode => {
Binary(opCode);
opCode.BaseClass = "BinaryComparisonInstruction";
opCode.WriteOpCodeSuffix.Add("output.Write('.');");
opCode.WriteOpCodeSuffix.Add("output.Write(OpType);");
};
static Action<OpCode> CustomArguments(params string[] arguments)
{
return CustomChildren(arguments.Select(arg => new ArgumentInfo(arg)).ToArray(), generateInline: true);

64
ICSharpCode.Decompiler/IL/Instructions/BinaryComparisonInstruction.cs

@ -1,64 +0,0 @@ @@ -1,64 +0,0 @@
// Copyright (c) 2014 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.IL
{
public abstract partial class BinaryComparisonInstruction : BinaryInstruction
{
public readonly StackType OpType;
protected BinaryComparisonInstruction(OpCode opCode, ILInstruction left, ILInstruction right) : base(opCode, left, right)
{
this.OpType = ComputeOpType(left.ResultType, right.ResultType);
}
static StackType ComputeOpType(StackType left, StackType right)
{
if (left == StackType.I || right == StackType.I)
return StackType.I;
Debug.Assert(left == right);
return left;
}
public sealed override StackType ResultType {
get {
return StackType.I4;
}
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
output.Write('.');
output.Write(OpType);
output.Write('(');
Left.WriteTo(output);
output.Write(", ");
Right.WriteTo(output);
output.Write(')');
}
}
}

155
ICSharpCode.Decompiler/IL/Instructions/Comp.cs

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
// Copyright (c) 2014 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.IL
{
public enum ComparisonKind : byte
{
Equality,
Inequality,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual
}
static class ComparisonKindExtensions
{
public static bool IsEqualityOrInequality(this ComparisonKind kind)
{
return kind == ComparisonKind.Equality || kind == ComparisonKind.Inequality;
}
public static ComparisonKind Negate(this ComparisonKind kind)
{
switch (kind) {
case ComparisonKind.Equality:
return ComparisonKind.Inequality;
case ComparisonKind.Inequality:
return ComparisonKind.Equality;
case ComparisonKind.LessThan:
return ComparisonKind.GreaterThanOrEqual;
case ComparisonKind.LessThanOrEqual:
return ComparisonKind.GreaterThan;
case ComparisonKind.GreaterThan:
return ComparisonKind.LessThanOrEqual;
case ComparisonKind.GreaterThanOrEqual:
return ComparisonKind.LessThan;
default:
throw new NotSupportedException();
}
}
public static BinaryOperatorType ToBinaryOperatorType(this ComparisonKind kind)
{
switch (kind) {
case ComparisonKind.Equality:
return BinaryOperatorType.Equality;
case ComparisonKind.Inequality:
return BinaryOperatorType.InEquality;
case ComparisonKind.LessThan:
return BinaryOperatorType.LessThan;
case ComparisonKind.LessThanOrEqual:
return BinaryOperatorType.LessThanOrEqual;
case ComparisonKind.GreaterThan:
return BinaryOperatorType.GreaterThan;
case ComparisonKind.GreaterThanOrEqual:
return BinaryOperatorType.GreaterThanOrEqual;
default:
throw new NotSupportedException();
}
}
public static string GetToken(this ComparisonKind kind)
{
return BinaryOperatorExpression.GetOperatorRole(kind.ToBinaryOperatorType()).Token;
}
}
partial class Comp
{
ComparisonKind kind;
public ComparisonKind Kind {
get { return kind; }
set {
kind = value;
MakeDirty();
}
}
StackType inputType;
/// <summary>
/// Gets the stack type of the comparison inputs.
/// </summary>
public StackType InputType {
get { return inputType; }
set {
inputType = value;
MakeDirty();
}
}
/// <summary>
/// If this is an integer comparison, specifies the sign used to interpret the integers.
/// </summary>
public readonly Sign Sign;
public Comp(ComparisonKind kind, Sign sign, ILInstruction left, ILInstruction right) : base(OpCode.Comp, left, right)
{
this.kind = kind;
this.Sign = sign;
this.inputType = ComputeInputType(left.ResultType, right.ResultType);
}
static StackType ComputeInputType(StackType left, StackType right)
{
if (left == StackType.I || right == StackType.I)
return StackType.I;
Debug.Assert(left == right);
return left;
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
switch (Sign) {
case Sign.Signed:
output.Write(".signed");
break;
case Sign.Unsigned:
output.Write(".unsigned");
break;
}
output.Write('(');
Left.WriteTo(output);
output.Write(' ');
output.Write(Kind.GetToken());
output.Write(' ');
Right.WriteTo(output);
output.Write(')');
}
}
}

75
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -44,34 +44,75 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -44,34 +44,75 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
protected internal override void VisitCgt_Un(Cgt_Un inst)
protected internal override void VisitComp(Comp inst)
{
base.VisitCgt_Un(inst);
base.VisitComp(inst);
if (inst.Right.MatchLdNull()) {
// cgt.un(left, ldnull)
// => logic.not(ceq(left, ldnull))
inst.ReplaceWith(new LogicNot(new Ceq(inst.Left, inst.Right) { ILRange = inst.ILRange }));
// comp(left > ldnull) => comp(left != ldnull)
// comp(left <= ldnull) => comp(left == ldnull)
if (inst.Kind == ComparisonKind.GreaterThan)
inst.Kind = ComparisonKind.Inequality;
else if (inst.Kind == ComparisonKind.LessThanOrEqual)
inst.Kind = ComparisonKind.Equality;
} else if (inst.Left.MatchLdNull()) {
// comp(ldnull < right) => comp(ldnull != right)
// comp(ldnull >= right) => comp(ldnull == right)
if (inst.Kind == ComparisonKind.LessThan)
inst.Kind = ComparisonKind.Inequality;
else if (inst.Kind == ComparisonKind.GreaterThanOrEqual)
inst.Kind = ComparisonKind.Equality;
}
if (inst.Right.MatchLdcI4(0)) {
// cgt.un(left, ldc.i4 0)
// => logic.not(ceq(left, ldc.i4 0))
if (inst.Right.MatchLdcI4(0) && inst.Sign == Sign.Unsigned
&& (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThan))
{
ILInstruction array;
if (inst.Left.MatchLdLen(StackType.I, out array)) {
// cgt.un(ldlen array, ldc.i4 0)
// => logic.not(ceq(ldlen.i4 array, ldc.i4 0))
// comp.unsigned(ldlen array > ldc.i4 0)
// => comp(ldlen.i4 array > ldc.i4 0)
// This is a special case where the C# compiler doesn't generate conv.i4 after ldlen.
inst.Left.ReplaceWith(new LdLen(StackType.I4, array) { ILRange = inst.Left.ILRange });
inst.InputType = StackType.I4;
}
// comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)
// comp.unsigned(left <= ldc.i4 0) => comp(left == ldc.i4 0)
if (inst.Kind == ComparisonKind.GreaterThan)
inst.Kind = ComparisonKind.Inequality;
else if (inst.Kind == ComparisonKind.LessThanOrEqual)
inst.Kind = ComparisonKind.Equality;
}
}
inst.ReplaceWith(new LogicNot(new Ceq(inst.Left, inst.Right) { ILRange = inst.ILRange }));
protected internal override void VisitConv(Conv inst)
{
inst.Argument.AcceptVisitor(this);
ILInstruction array;
if (inst.Argument.MatchLdLen(StackType.I, out array) && inst.TargetType.IsIntegerType() && !inst.CheckForOverflow) {
// conv.i4(ldlen array) => ldlen.i4(array)
inst.AddILRange(inst.Argument.ILRange);
inst.ReplaceWith(new LdLen(inst.TargetType.GetStackType(), array) { ILRange = inst.ILRange });
}
}
protected internal override void VisitClt_Un(Clt_Un inst)
protected internal override void VisitLogicNot(LogicNot inst)
{
base.VisitClt_Un(inst);
if (inst.Left.MatchLdNull()) {
// clt.un(ldnull, right)
// => logic.not(ceq(ldnull, right))
inst.ReplaceWith(new LogicNot(new Ceq(inst.Left, inst.Right) { ILRange = inst.ILRange }));
inst.Argument.AcceptVisitor(this);
ILInstruction arg;
if (inst.Argument.MatchLogicNot(out arg)) {
// logic.not(logic.not(arg))
// ==> arg
Debug.Assert(arg.ResultType == StackType.I4);
arg.AddILRange(inst.ILRange);
arg.AddILRange(inst.Argument.ILRange);
inst.ReplaceWith(arg);
} else if (inst.Argument is Comp) {
Comp comp = (Comp)inst.Argument;
if (comp.InputType != StackType.F || comp.Kind.IsEqualityOrInequality()) {
// push negation into comparison:
comp.Kind = comp.Kind.Negate();
comp.AddILRange(inst.ILRange);
inst.ReplaceWith(comp);
}
}
}

Loading…
Cancel
Save