From 12e74daf48cab8a76cac7cf6cead53b512a2fd7e Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 14 Oct 2018 17:29:15 +0200 Subject: [PATCH] Handle Comp in InferType(). --- ICSharpCode.Decompiler/IL/ILTypeExtensions.cs | 16 +++++++-- .../CompoundAssignmentInstruction.cs | 2 +- ICSharpCode.Decompiler/IL/SemanticHelper.cs | 21 ------------ .../IL/Transforms/AssignVariableNames.cs | 2 +- .../IL/Transforms/ExpressionTransforms.cs | 10 +++--- .../IL/Transforms/NullPropagationTransform.cs | 2 +- .../IL/Transforms/RemoveDeadVariableInit.cs | 2 +- .../IL/Transforms/TransformAssignment.cs | 34 +++++++++---------- .../IL/Transforms/TransformExpressionTrees.cs | 2 +- 9 files changed, 40 insertions(+), 51 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index 14b67f9ba..afe7d6c18 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -131,7 +131,7 @@ namespace ICSharpCode.Decompiler.IL /// /// Returns SpecialType.UnknownType for unsupported instructions. /// - public static IType InferType(this ILInstruction inst) + public static IType InferType(this ILInstruction inst, ICompilation compilation) { switch (inst) { case NewObj newObj: @@ -159,12 +159,24 @@ namespace ICSharpCode.Decompiler.IL case LdsFlda ldsflda: return new ByReferenceType(ldsflda.Field.Type); case LdElema ldelema: - if (ldelema.Array.InferType() is ArrayType arrayType) { + if (ldelema.Array.InferType(compilation) is ArrayType arrayType) { if (TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, ldelema.Type)) { return new ByReferenceType(arrayType.ElementType); } } return new ByReferenceType(ldelema.Type); + case Comp comp: + if (compilation == null) + return SpecialType.UnknownType; + switch (comp.LiftingKind) { + case ComparisonLiftingKind.None: + case ComparisonLiftingKind.CSharp: + return compilation.FindType(KnownTypeCode.Boolean); + case ComparisonLiftingKind.ThreeValuedLogic: + return NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); + default: + return SpecialType.UnknownType; + } default: return SpecialType.UnknownType; } diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index 4afafe1af..0c599ee35 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -148,7 +148,7 @@ namespace ICSharpCode.Decompiler.IL } } // Can't transform if the RHS value would be need to be truncated for the LHS type. - if (Transforms.TransformAssignment.IsImplicitTruncation(binary.Right, type, binary.IsLifted)) + if (Transforms.TransformAssignment.IsImplicitTruncation(binary.Right, type, null, binary.IsLifted)) return false; return true; } diff --git a/ICSharpCode.Decompiler/IL/SemanticHelper.cs b/ICSharpCode.Decompiler/IL/SemanticHelper.cs index 38328d1f7..7102628b9 100644 --- a/ICSharpCode.Decompiler/IL/SemanticHelper.cs +++ b/ICSharpCode.Decompiler/IL/SemanticHelper.cs @@ -44,27 +44,6 @@ namespace ICSharpCode.Decompiler.IL return (inst & ~pureFlags) == 0; } - /// - /// Gets whether the instruction sequence 'inst1; inst2;' may be ordered to 'inst2; inst1;' - /// - internal static bool MayReorder(InstructionFlags inst1, InstructionFlags inst2) - { - // If both instructions perform an impure action, we cannot reorder them - if (!IsPure(inst1) && !IsPure(inst2)) - return false; - // We cannot reorder if inst2 might write what inst1 looks at - if (ConflictingPair(inst1, inst2, InstructionFlags.MayReadLocals, InstructionFlags.MayWriteLocals | InstructionFlags.SideEffect)) - return false; - return true; - } - - private static bool ConflictingPair(InstructionFlags inst1, InstructionFlags inst2, InstructionFlags readFlag, InstructionFlags writeFlag) - { - // if one instruction has the read flag and the other the write flag, that's a conflict - return (inst1 & readFlag) != 0 && (inst2 & writeFlag) != 0 - || (inst2 & readFlag) != 0 && (inst1 & writeFlag) != 0; - } - /// /// Gets whether the instruction sequence 'inst1; inst2;' may be ordered to 'inst2; inst1;' /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 31fb157af..7cac67978 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -448,7 +448,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!variableType.IsKnownType(KnownTypeCode.Object)) return variableType; - IType inferredType = inst.InferType(); + IType inferredType = inst.InferType(context.TypeSystem); if (inferredType.Kind != TypeKind.Unknown) return inferredType; else diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index a90988b0f..19263dbdf 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -498,7 +498,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } break; case BinaryNumericOperator.BitAnd: - if (IsBoolean(inst.Left) && IsBoolean(inst.Right) && SemanticHelper.IsPure(inst.Right.Flags)) + if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) + && inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) + && SemanticHelper.IsPure(inst.Right.Flags)) { context.Step("Replace bit.and with logic.and", inst); var expr = IfInstruction.LogicAnd(inst.Left, inst.Right); @@ -508,11 +510,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; } } - - private static bool IsBoolean(ILInstruction inst) => - inst is Comp c && c.ResultType == StackType.I4 || - inst.InferType().IsKnownType(KnownTypeCode.Boolean); - + protected internal override void VisitTryCatchHandler(TryCatchHandler inst) { base.VisitTryCatchHandler(inst); diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index e2b707086..6737b6ac9 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -111,7 +111,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!IsValidAccessChain(testedVar, mode, nonNullInst, out var varLoad)) return null; // note: InferType will be accurate in this case because the access chain consists of calls and field accesses - IType returnType = nonNullInst.InferType(); + IType returnType = nonNullInst.InferType(context.TypeSystem); if (nullInst.MatchLdNull()) { context.Step($"Null propagation (mode={mode}, output=reference type)", nonNullInst); // testedVar != null ? testedVar.AccessChain : null diff --git a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs index 52ecb6e81..ff8f08ac4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms IType newType = null; // Multiple store are possible in case of (c ? ref a : ref b) += 1, for example. foreach (var stloc in v.StoreInstructions.OfType()) { - var inferredType = stloc.Value.InferType(); + var inferredType = stloc.Value.InferType(context.TypeSystem); // cancel, if types of values do not match exactly if (newType != null && !newType.Equals(inferredType)) { newType = SpecialType.UnknownType; diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 9a941a244..2ad21b491 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // in some cases it can be a compiler-generated local if (inst == null || (inst.Variable.Kind != VariableKind.StackSlot && inst.Variable.Kind != VariableKind.Local)) return false; - if (IsImplicitTruncation(inst.Value, inst.Variable.Type)) { + if (IsImplicitTruncation(inst.Value, inst.Variable.Type, context.TypeSystem)) { // 'stloc s' is implicitly truncating the value return false; } @@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!SemanticHelper.IsPure(stobj.Target.Flags) || inst.Variable.IsUsedWithin(stobj.Target)) return false; - var pointerType = stobj.Target.InferType(); + var pointerType = stobj.Target.InferType(context.TypeSystem); IType newType = stobj.Type; if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(pointerType, stobj.Type)) { if (pointerType is ByReferenceType byref) @@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms else if (pointerType is PointerType pointer) newType = pointer.ElementType; } - if (IsImplicitTruncation(inst.Value, newType)) { + if (IsImplicitTruncation(inst.Value, newType, context.TypeSystem)) { // 'stobj' is implicitly truncating the value return false; } @@ -146,7 +146,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!SemanticHelper.IsPure(arg.Flags) || inst.Variable.IsUsedWithin(arg)) return false; } - if (IsImplicitTruncation(inst.Value, call.Method.Parameters.Last().Type)) { + if (IsImplicitTruncation(inst.Value, call.Method.Parameters.Last().Type, context.TypeSystem)) { // setter call is implicitly truncating the value return false; } @@ -242,7 +242,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // changes the return value of the expression, so this is only valid on block-level. return false; } - if (!IsCompoundStore(compoundStore, out var targetType, out var setterValue)) + if (!IsCompoundStore(compoundStore, out var targetType, out var setterValue, context.TypeSystem)) return false; // targetType = The type of the property/field/etc. being stored to. // setterValue = The value being stored. @@ -342,11 +342,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!nextInst.Value.MatchLdLoc(inst.Variable)) return false; - if (IsImplicitTruncation(inst.Value, inst.Variable.Type)) { + if (IsImplicitTruncation(inst.Value, inst.Variable.Type, context.TypeSystem)) { // 'stloc s' is implicitly truncating the stack value return false; } - if (IsImplicitTruncation(inst.Value, nextInst.Variable.Type)) { + if (IsImplicitTruncation(inst.Value, nextInst.Variable.Type, context.TypeSystem)) { // 'stloc l' is implicitly truncating the stack value return false; } @@ -371,7 +371,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Gets whether 'stobj type(..., value)' would evaluate to a different value than 'value' /// due to implicit truncation. /// - static internal bool IsImplicitTruncation(ILInstruction value, IType type, bool allowNullableValue = false) + static internal bool IsImplicitTruncation(ILInstruction value, IType type, ICompilation compilation, bool allowNullableValue = false) { if (!type.IsSmallIntegerType()) { // Implicit truncation in ILAst only happens for small integer types; @@ -401,10 +401,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else if (value is Comp) { return false; // comp returns 0 or 1, which always fits } else if (value is IfInstruction ifInst) { - return IsImplicitTruncation(ifInst.TrueInst, type, allowNullableValue) - || IsImplicitTruncation(ifInst.FalseInst, type, allowNullableValue); + return IsImplicitTruncation(ifInst.TrueInst, type, compilation, allowNullableValue) + || IsImplicitTruncation(ifInst.FalseInst, type, compilation, allowNullableValue); } else { - IType inferredType = value.InferType(); + IType inferredType = value.InferType(compilation); if (allowNullableValue) { inferredType = NullableType.GetUnderlyingType(inferredType); } @@ -456,14 +456,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Gets whether 'inst' is a possible store for use as a compound store. /// - static bool IsCompoundStore(ILInstruction inst, out IType storeType, out ILInstruction value) + static bool IsCompoundStore(ILInstruction inst, out IType storeType, out ILInstruction value, ICompilation compilation) { value = null; storeType = null; if (inst is StObj stobj) { // stobj.Type may just be 'int' (due to stind.i4) when we're actually operating on a 'ref MyEnum'. // Try to determine the real type of the object we're modifying: - storeType = stobj.Target.InferType(); + storeType = stobj.Target.InferType(compilation); if (storeType is ByReferenceType refType) { storeType = refType.ElementType; } else if (storeType is PointerType pointerType) { @@ -530,7 +530,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool TransformPostIncDecOperatorWithInlineStore(Block block, int pos) { var store = block.Instructions[pos]; - if (!IsCompoundStore(store, out var targetType, out var value)) + if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) return false; StLoc stloc; var binary = UnwrapSmallIntegerConv(value, out var conv) as BinaryNumericInstruction; @@ -555,7 +555,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!IsMatchingCompoundLoad(stloc.Value, store, stloc.Variable)) return false; - if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type)) + if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem)) return false; context.Step("TransformPostIncDecOperatorWithInlineStore", store); if (binary != null) { @@ -585,9 +585,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms var store = block.Instructions.ElementAtOrDefault(i + 1); if (inst == null || store == null) return false; - if (!IsCompoundStore(store, out var targetType, out var value)) + if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) return false; - if (IsImplicitTruncation(inst.Value, targetType)) { + if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem)) { // 'stloc l' is implicitly truncating the value return false; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 23535e578..3bb9dd3ac 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -1065,7 +1065,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms value = call.Arguments[0]; if (call.Arguments.Count == 2) return MatchGetTypeFromHandle(call.Arguments[1], out type); - type = value.InferType(); + type = value.InferType(context.TypeSystem); return true; } return false;