diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 01888ac50..bdac7b814 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -307,13 +307,20 @@ namespace ICSharpCode.Decompiler.CSharp return false; } - protected internal override TranslatedExpression VisitLdcF(LdcF inst, TranslationContext context) + protected internal override TranslatedExpression VisitLdcF4(LdcF4 inst, TranslationContext context) + { + return new PrimitiveExpression(inst.Value) + .WithILInstruction(inst) + .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Single), inst.Value)); + } + + protected internal override TranslatedExpression VisitLdcF8(LdcF8 inst, TranslationContext context) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Double), inst.Value)); } - + protected internal override TranslatedExpression VisitLdcDecimal(LdcDecimal inst, TranslationContext context) { return new PrimitiveExpression(inst.Value) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 3829ef634..9e75185f9 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -457,8 +457,10 @@ namespace ICSharpCode.Decompiler.IL return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None), Pop(), checkForOverflow: false, sign: Sign.None)); case StackType.I8: return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcI8(0), Pop(), checkForOverflow: false, sign: Sign.None)); - case StackType.F: - return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcF(0), Pop(), checkForOverflow: false, sign: Sign.None)); + case StackType.F4: + return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcF4(0), Pop(), checkForOverflow: false, sign: Sign.None)); + case StackType.F8: + return Push(new BinaryNumericInstruction(BinaryNumericOperator.Sub, new LdcF8(0), Pop(), checkForOverflow: false, sign: Sign.None)); default: Warn("Unsupported input type for neg."); goto case StackType.I4; @@ -665,9 +667,9 @@ namespace ICSharpCode.Decompiler.IL case Cil.Code.Ldc_I8: return Push(new LdcI8((long)cecilInst.Operand)); case Cil.Code.Ldc_R4: - return Push(new LdcF((float)cecilInst.Operand)); + return Push(new LdcF4((float)cecilInst.Operand)); case Cil.Code.Ldc_R8: - return Push(new LdcF((double)cecilInst.Operand)); + return Push(new LdcF8((double)cecilInst.Operand)); case Cil.Code.Ldc_I4_M1: return Push(new LdcI4(-1)); case Cil.Code.Ldc_I4_0: @@ -781,9 +783,9 @@ namespace ICSharpCode.Decompiler.IL case Cil.Code.Stind_I8: return new StObj(value: Pop(StackType.I8), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int64)); case Cil.Code.Stind_R4: - return new StObj(value: Pop(StackType.F), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Single)); + return new StObj(value: Pop(StackType.F4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Single)); case Cil.Code.Stind_R8: - return new StObj(value: Pop(StackType.F), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Double)); + return new StObj(value: Pop(StackType.F8), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Double)); case Cil.Code.Stind_I: return new StObj(value: Pop(StackType.I), target: PopPointer(), type: compilation.FindType(KnownTypeCode.IntPtr)); case Cil.Code.Stind_Ref: @@ -1287,7 +1289,15 @@ namespace ICSharpCode.Decompiler.IL } // Based on Table 4: Binary Comparison or Branch Operation - if (left.ResultType == StackType.F && right.ResultType == StackType.F) { + if (left.ResultType.IsFloatType() && right.ResultType.IsFloatType()) { + if (left.ResultType != right.ResultType) { + // make the implicit F4->F8 conversion explicit: + if (left.ResultType == StackType.F4) { + left = new Conv(left, PrimitiveType.R8, false, Sign.Signed); + } else if (right.ResultType == StackType.F4) { + right = new Conv(right, PrimitiveType.R8, false, Sign.Signed); + } + } if (un) { // for floats, 'un' means 'unordered' return Comp.LogicNot(new Comp(kind.Negate(), Sign.None, left, right)); diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index adcca82b2..c44254a04 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -44,8 +44,9 @@ namespace ICSharpCode.Decompiler.IL case MetadataType.FunctionPointer: return StackType.I; case MetadataType.Single: + return StackType.F4; case MetadataType.Double: - return StackType.F; + return StackType.F8; case MetadataType.ByReference: return StackType.Ref; case MetadataType.Void: diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 0ea2866d3..51f32fa88 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -108,8 +108,10 @@ namespace ICSharpCode.Decompiler.IL LdcI4, /// Loads a constant 64-bit integer. LdcI8, - /// Loads a constant floating-point number. - LdcF, + /// Loads a constant 32-bit floating-point number. + LdcF4, + /// Loads a constant 64-bit floating-point number. + LdcF8, /// Loads a constant decimal. LdcDecimal, /// Loads the null reference. @@ -2614,15 +2616,52 @@ namespace ICSharpCode.Decompiler.IL } namespace ICSharpCode.Decompiler.IL { - /// Loads a constant floating-point number. - public sealed partial class LdcF : SimpleInstruction + /// Loads a constant 32-bit floating-point number. + public sealed partial class LdcF4 : SimpleInstruction { - public LdcF(double value) : base(OpCode.LdcF) + public LdcF4(float value) : base(OpCode.LdcF4) + { + this.Value = value; + } + public readonly float Value; + public override StackType ResultType { get { return StackType.F4; } } + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + ILRange.WriteTo(output, options); + output.Write(OpCode); + output.Write(' '); + Disassembler.DisassemblerHelpers.WriteOperand(output, Value); + } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitLdcF4(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitLdcF4(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitLdcF4(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as LdcF4; + return o != null && this.Value == o.Value; + } + } +} +namespace ICSharpCode.Decompiler.IL +{ + /// Loads a constant 64-bit floating-point number. + public sealed partial class LdcF8 : SimpleInstruction + { + public LdcF8(double value) : base(OpCode.LdcF8) { this.Value = value; } public readonly double Value; - public override StackType ResultType { get { return StackType.F; } } + public override StackType ResultType { get { return StackType.F8; } } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { ILRange.WriteTo(output, options); @@ -2632,19 +2671,19 @@ namespace ICSharpCode.Decompiler.IL } public override void AcceptVisitor(ILVisitor visitor) { - visitor.VisitLdcF(this); + visitor.VisitLdcF8(this); } public override T AcceptVisitor(ILVisitor visitor) { - return visitor.VisitLdcF(this); + return visitor.VisitLdcF8(this); } public override T AcceptVisitor(ILVisitor visitor, C context) { - return visitor.VisitLdcF(this, context); + return visitor.VisitLdcF8(this, context); } protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { - var o = other as LdcF; + var o = other as LdcF8; return o != null && this.Value == o.Value; } } @@ -5058,7 +5097,11 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } - protected internal virtual void VisitLdcF(LdcF inst) + protected internal virtual void VisitLdcF4(LdcF4 inst) + { + Default(inst); + } + protected internal virtual void VisitLdcF8(LdcF8 inst) { Default(inst); } @@ -5364,7 +5407,11 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } - protected internal virtual T VisitLdcF(LdcF inst) + protected internal virtual T VisitLdcF4(LdcF4 inst) + { + return Default(inst); + } + protected internal virtual T VisitLdcF8(LdcF8 inst) { return Default(inst); } @@ -5670,7 +5717,11 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } - protected internal virtual T VisitLdcF(LdcF inst, C context) + protected internal virtual T VisitLdcF4(LdcF4 inst, C context) + { + return Default(inst, context); + } + protected internal virtual T VisitLdcF8(LdcF8 inst, C context) { return Default(inst, context); } @@ -5854,7 +5905,8 @@ namespace ICSharpCode.Decompiler.IL "ldstr", "ldc.i4", "ldc.i8", - "ldc.f", + "ldc.f4", + "ldc.f8", "ldc.decimal", "ldnull", "ldftn", @@ -6095,9 +6147,19 @@ namespace ICSharpCode.Decompiler.IL value = default(long); return false; } - public bool MatchLdcF(out double value) + public bool MatchLdcF4(out float value) + { + var inst = this as LdcF4; + if (inst != null) { + value = inst.Value; + return true; + } + value = default(float); + return false; + } + public bool MatchLdcF8(out double value) { - var inst = this as LdcF; + var inst = this as LdcF8; if (inst != null) { value = inst.Value; return true; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 04a85831d..35246b94b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -167,8 +167,10 @@ LoadConstant("int"), ResultType("I4")), new OpCode("ldc.i8", "Loads a constant 64-bit integer.", LoadConstant("long"), ResultType("I8")), - new OpCode("ldc.f", "Loads a constant floating-point number.", - LoadConstant("double"), ResultType("F")), + new OpCode("ldc.f4", "Loads a constant 32-bit floating-point number.", + LoadConstant("float"), ResultType("F4")), + new OpCode("ldc.f8", "Loads a constant 64-bit floating-point number.", + LoadConstant("double"), ResultType("F8")), new OpCode("ldc.decimal", "Loads a constant decimal.", LoadConstant("decimal"), ResultType("O")), new OpCode("ldnull", "Loads the null reference.", diff --git a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs index 9562466fa..cf79273d1 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs @@ -188,7 +188,8 @@ namespace ICSharpCode.Decompiler.IL case StackType.I8: case StackType.I: return ConversionKind.Truncate; - case StackType.F: + case StackType.F4: + case StackType.F8: return ConversionKind.FloatToInt; default: return ConversionKind.Invalid; @@ -201,7 +202,8 @@ namespace ICSharpCode.Decompiler.IL case StackType.I: case StackType.I8: return ConversionKind.Truncate; - case StackType.F: + case StackType.F4: + case StackType.F8: return ConversionKind.FloatToInt; default: return ConversionKind.Invalid; @@ -217,7 +219,8 @@ namespace ICSharpCode.Decompiler.IL return inputSign == Sign.Signed ? ConversionKind.SignExtend : ConversionKind.ZeroExtend; case StackType.I8: return ConversionKind.Nop; - case StackType.F: + case StackType.F4: + case StackType.F8: return ConversionKind.FloatToInt; case StackType.Ref: case StackType.O: @@ -237,7 +240,8 @@ namespace ICSharpCode.Decompiler.IL return ConversionKind.Nop; case StackType.I8: return ConversionKind.Truncate; - case StackType.F: + case StackType.F4: + case StackType.F8: return ConversionKind.FloatToInt; case StackType.Ref: case StackType.O: @@ -246,14 +250,28 @@ namespace ICSharpCode.Decompiler.IL return ConversionKind.Invalid; } case PrimitiveType.R4: + switch (inputType) { + case StackType.I4: + case StackType.I: + case StackType.I8: + return ConversionKind.IntToFloat; + case StackType.F4: + return ConversionKind.Nop; + case StackType.F8: + return ConversionKind.FloatPrecisionChange; + default: + return ConversionKind.Invalid; + } case PrimitiveType.R8: switch (inputType) { case StackType.I4: case StackType.I: case StackType.I8: return ConversionKind.IntToFloat; - case StackType.F: + case StackType.F4: return ConversionKind.FloatPrecisionChange; + case StackType.F8: + return ConversionKind.Nop; default: return ConversionKind.Invalid; } diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index 5e4f30113..1d0d58ab6 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -27,9 +27,14 @@ namespace ICSharpCode.Decompiler.IL return OpCode == OpCode.LdcI4 && ((LdcI4)this).Value == val; } - public bool MatchLdcF(double value) + public bool MatchLdcF4(float value) { - return MatchLdcF(out var v) && v == value; + return MatchLdcF4(out var v) && v == value; + } + + public bool MatchLdcF8(double value) + { + return MatchLdcF8(out var v) && v == value; } /// diff --git a/ICSharpCode.Decompiler/IL/StackType.cs b/ICSharpCode.Decompiler/IL/StackType.cs index b5a5b4d60..6c303161f 100644 --- a/ICSharpCode.Decompiler/IL/StackType.cs +++ b/ICSharpCode.Decompiler/IL/StackType.cs @@ -54,11 +54,16 @@ namespace ICSharpCode.Decompiler.IL /// and any enums with one of the above as underlying type. /// I8, - /// Floating point number + /// 32-bit floating point number /// - /// Used for C# float and double. + /// Used for C# float. /// - F, + F4, + /// 64-bit floating point number + /// + /// Used for C# double. + /// + F8, /// Another stack type. Includes objects, value types, ... O, /// A managed pointer diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 62e0ac4ed..59c8446ba 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -184,7 +184,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { ILInstruction lhs, rhs; if (arg is Comp comp) { - if ((comp.InputType != StackType.F && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality()) { + if ((!comp.InputType.IsFloatType() && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality()) { context.Step("push negation into comparison", inst); comp.Kind = comp.Kind.Negate(); comp.AddILRange(inst.ILRange); diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 7dffaf2b4..2f07d8153 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -327,7 +327,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms values = null; // stloc dictVar(newobj Dictionary..ctor(ldc.i4 valuesLength)) // -or- - // stloc dictVar(newobj Hashtable..ctor(ldc.i4 capacity, ldc.f loadFactor)) + // stloc dictVar(newobj Hashtable..ctor(ldc.i4 capacity, ldc.f4 loadFactor)) if (!(block.Instructions[0].MatchStLoc(out var dictVar, out var newObjDict) && newObjDict is NewObj newObj)) return false; if (!newObj.Method.DeclaringType.Equals(dictionaryType)) @@ -336,7 +336,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (newObj.Arguments.Count == 2) { if (!newObj.Arguments[0].MatchLdcI4(out valuesLength)) return false; - if (!newObj.Arguments[1].MatchLdcF(0.5)) + if (!newObj.Arguments[1].MatchLdcF4(0.5f)) return false; } else if (newObj.Arguments.Count == 1) { if (!newObj.Arguments[0].MatchLdcI4(out valuesLength)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index 801871925..e6884d395 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -146,8 +146,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms case KnownTypeCode.UInt64: return new LdcI8(0); case KnownTypeCode.Single: + return new LdcF4(0); case KnownTypeCode.Double: - return new LdcF(0); + return new LdcF8(0); case KnownTypeCode.Decimal: return new LdcDecimal(0); case KnownTypeCode.Void: @@ -384,9 +385,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms case TypeCode.UInt64: return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcI8(BitConverter.ToInt64(d, i))); case TypeCode.Single: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcF(BitConverter.ToSingle(d, i))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcF4(BitConverter.ToSingle(d, i))); case TypeCode.Double: - return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcF(BitConverter.ToDouble(d, i))); + return DecodeArrayInitializer(initialValue, array, arrayLength, output, typeCode, type, (d, i) => new LdcF8(BitConverter.ToDouble(d, i))); case TypeCode.Object: case TypeCode.Empty: var typeDef = type.GetDefinition(); diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index a5db26eb8..9adac5616 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -972,8 +972,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms case StackType.I: left = new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None); break; - case StackType.F: - left = new LdcF(0); + case StackType.F4: + left = new LdcF4(0); + break; + case StackType.F8: + left = new LdcF8(0); break; default: return (null, SpecialType.UnknownType); diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs index 8ef073fa4..3b87a91ec 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs @@ -24,7 +24,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public static class TypeUtils { public const int NativeIntSize = 6; // between 4 (Int32) and 8 (Int64) - + /// /// Gets the size (in bytes) of the input type. /// Returns NativeIntSize for pointer-sized types. @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem type = type.GetEnumUnderlyingType(); break; } - + var typeDef = type.GetDefinition(); if (typeDef == null) return 0; @@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.TypeSystem } return 0; } - + /// /// Gets the size of the input stack type. /// @@ -92,12 +92,12 @@ namespace ICSharpCode.Decompiler.TypeSystem return 0; } } - + public static IType GetLargerType(IType type1, IType type2) { return GetSize(type1) >= GetSize(type2) ? type1 : type2; } - + /// /// Gets whether the type is a small integer type. /// Small integer types are: @@ -165,7 +165,22 @@ namespace ICSharpCode.Decompiler.TypeSystem return false; } } - + + /// + /// Gets whether the type is an IL floating point type. + /// Returns true for F4 or F8. + /// + public static bool IsFloatType(this StackType type) + { + switch (type) { + case StackType.F4: + case StackType.F8: + return true; + default: + return false; + } + } + /// /// Gets whether reading/writing an element of accessType from the pointer /// is equivalent to reading/writing an element of the pointer's element type. @@ -223,8 +238,9 @@ namespace ICSharpCode.Decompiler.TypeSystem case KnownTypeCode.UInt64: return StackType.I8; case KnownTypeCode.Single: + return StackType.F4; case KnownTypeCode.Double: - return StackType.F; + return StackType.F8; case KnownTypeCode.Void: return StackType.Void; case KnownTypeCode.IntPtr: @@ -234,7 +250,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return StackType.O; } } - + /// /// If type is an enumeration type, returns the underlying type. /// Otherwise, returns type unmodified. @@ -243,7 +259,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { return (type.Kind == TypeKind.Enum) ? type.GetDefinition().EnumUnderlyingType : type; } - + /// /// Gets the sign of the input type. /// @@ -363,7 +379,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return KnownTypeCode.None; } } - + public static KnownTypeCode ToKnownTypeCode(this StackType stackType, Sign sign = Sign.None) { switch (stackType) { @@ -373,7 +389,9 @@ namespace ICSharpCode.Decompiler.TypeSystem return sign == Sign.Unsigned ? KnownTypeCode.UInt64 : KnownTypeCode.Int64; case StackType.I: return sign == Sign.Unsigned ? KnownTypeCode.UIntPtr : KnownTypeCode.IntPtr; - case StackType.F: + case StackType.F4: + return KnownTypeCode.Single; + case StackType.F8: return KnownTypeCode.Double; case StackType.O: return KnownTypeCode.Object; @@ -384,7 +402,7 @@ namespace ICSharpCode.Decompiler.TypeSystem } } } - + public enum Sign : byte { None,