// 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 ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.TypeSystem; 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; } } public enum ComparisonLiftingKind { /// /// Not a lifted comparison. /// None, /// /// C#-style lifted comparison: /// * operands that have a ResultType != this.InputType are expected to return a value of /// type Nullable{T}, where T.GetStackType() == this.InputType. /// * if both operands are null, equality comparisons evaluate to 1, all other comparisons to 0. /// * if one operand is null, inequality comparisons evaluate to 1, all other comparisons to 0. /// * if neither operand is null, the underlying comparison is performed. /// /// Note that even though C#-style lifted comparisons set IsLifted=true, /// the ResultType remains I4 as with normal comparisons. /// CSharp, /// /// SQL-style lifted comparison: works like a lifted binary numeric instruction, /// that is, if any input operand is null, the comparison evaluates to null. /// /// /// This lifting kind is currently not used. /// ThreeValuedLogic } partial class Comp : ILiftableInstruction { ComparisonKind kind; public ComparisonKind Kind { get { return kind; } set { kind = value; MakeDirty(); } } public readonly ComparisonLiftingKind LiftingKind; /// /// Gets the stack type of the comparison inputs. /// For lifted comparisons, this is the underlying input type. /// public StackType InputType; /// /// If this is an integer comparison, specifies the sign used to interpret the integers. /// public readonly Sign Sign; public Comp(ComparisonKind kind, Sign sign, ILInstruction left, ILInstruction right) : base(OpCode.Comp, left, right) { this.kind = kind; this.LiftingKind = ComparisonLiftingKind.None; this.InputType = left.ResultType; this.Sign = sign; Debug.Assert(left.ResultType == right.ResultType); } public Comp(ComparisonKind kind, ComparisonLiftingKind lifting, StackType inputType, Sign sign, ILInstruction left, ILInstruction right) : base(OpCode.Comp, left, right) { this.kind = kind; this.LiftingKind = lifting; this.InputType = inputType; this.Sign = sign; } public override StackType ResultType => LiftingKind == ComparisonLiftingKind.ThreeValuedLogic ? StackType.O : StackType.I4; public bool IsLifted => LiftingKind != ComparisonLiftingKind.None; public StackType UnderlyingResultType => StackType.I4; internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); if (LiftingKind == ComparisonLiftingKind.None) { Debug.Assert(Left.ResultType == InputType); Debug.Assert(Right.ResultType == InputType); } else { Debug.Assert(Left.ResultType == InputType || Left.ResultType == StackType.O); Debug.Assert(Right.ResultType == InputType || Right.ResultType == StackType.O); } } 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; } switch (LiftingKind) { case ComparisonLiftingKind.CSharp: output.Write(".lifted[C#]"); break; case ComparisonLiftingKind.ThreeValuedLogic: output.Write(".lifted[3VL]"); break; } output.Write('('); Left.WriteTo(output); output.Write(' '); output.Write(Kind.GetToken()); output.Write(' '); Right.WriteTo(output); output.Write(')'); } } }