From c0f954aaa6e6b495bcc882e226d93dbad4643b21 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Aug 2019 20:52:31 +0200 Subject: [PATCH] Simplify nullable value types throw expression pattern. --- .../IL/Transforms/NullCoalescingTransform.cs | 119 +++--------------- 1 file changed, 20 insertions(+), 99 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index aedf1038b..26cfd8b55 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -36,9 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public void Run(Block block, int pos, StatementTransformContext context) { if (!TransformRefTypes(block, pos, context)) { - if (!TransformThrowExpressionValueTypes(block, pos, context)) { - TransformThrowExpressionOnAddress(block, pos, context); - } + TransformThrowExpressionValueTypes(block, pos, context); } } @@ -112,15 +110,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ... /// => /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... - /// - /// -or- - /// - /// stloc v(value) - /// stloc s(ldloca v) - /// if (logic.not(call get_HasValue(ldloc s))) throw(...) - /// ... Call(arg1, arg2, call GetValueOrDefault(ldloc s), arg4) ... - /// => - /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... /// bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context) { @@ -128,47 +117,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(block.Instructions[pos] is StLoc stloc)) return false; - int offset = 1; + if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) + return false; + if (!(Block.Unwrap(trueInst) is Throw throwInst)) + return false; + if (!condition.MatchLogicNot(out var arg)) + return false; + if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, out ILInstruction target))) + return false; ILVariable v = stloc.Variable; - // alternative pattern using stack slot containing address of v - if (block.Instructions[pos + offset] is StLoc addrCopyStore - && addrCopyStore.Variable.Kind == VariableKind.StackSlot - && addrCopyStore.Value.MatchLdLoca(v) - && pos + 3 < block.Instructions.Count) - { - offset++; - // v turns into s in the pattern above. - v = addrCopyStore.Variable; + if (v.Type is ByReferenceType byRefType && byRefType.ElementType.IsReferenceType == false) { if (!(v.StoreCount == 1 && v.LoadCount == 2 && v.AddressCount == 0)) return false; + if (!target.MatchLdLoc(v)) + return false; } else { if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) return false; + if (!target.MatchLdLoca(v)) + return false; } - if (!block.Instructions[pos + offset].MatchIfInstruction(out var condition, out var trueInst)) - return false; - if (!(Block.Unwrap(trueInst) is Throw throwInst)) - return false; - if (!condition.MatchLogicNot(out var arg)) - return false; - if (!MatchNullableCall(arg, NullableLiftingTransform.MatchHasValueCall)) - return false; var throwInstParent = throwInst.Parent; var throwInstChildIndex = throwInst.ChildIndex; var nullCoalescingWithThrow = new NullCoalescingInstruction( NullCoalescingKind.NullableWithValueFallback, stloc.Value, throwInst); - var resultType = NullableType.GetUnderlyingType(v.Type).GetStackType(); + var resultType = NullableType.GetUnderlyingType(call.Method.DeclaringType).GetStackType(); nullCoalescingWithThrow.UnderlyingResultType = resultType; - var result = ILInlining.FindLoadInNext(block.Instructions[pos + offset + 1], v, nullCoalescingWithThrow, InliningOptions.None); + var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None); if (result.Type == ILInlining.FindResultType.Found && MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault)) { context.Step("NullCoalescingTransform (value types + throw expression)", stloc); throwInst.resultType = resultType; result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); - block.Instructions.RemoveRange(pos, offset + 1); // remove store(s) and if instruction + block.Instructions.RemoveRange(pos, 2); // remove store(s) and if instruction return true; } else { // reset the primary position (see remarks on ILInstruction.Parent) @@ -182,78 +166,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (!matcher(input, out var loadInst)) return false; - if (offset == 1) { // Pattern 1 - if (!loadInst.MatchLdLoca(v)) + if (v.Type is ByReferenceType) { + if (!loadInst.MatchLdLoc(v)) return false; } else { - if (!loadInst.MatchLdLoc(v)) + if (!loadInst.MatchLdLoca(v)) return false; } return true; } } - - /// - /// stloc s(addressOfValue) - /// if (logic.not(call get_HasValue(ldloc s))) throw(...) - /// ... Call(arg1, arg2, call GetValueOrDefault(ldloc s), arg4) ... - /// => - /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... - /// - bool TransformThrowExpressionOnAddress(Block block, int pos, StatementTransformContext context) - { - if (pos + 2 >= block.Instructions.Count) - return false; - if (!(block.Instructions[pos] is StLoc stloc)) - return false; - var s = stloc.Variable; - if (s.Kind != VariableKind.StackSlot) - return false; - if (!(s.StoreCount == 1 && s.LoadCount == 2 && s.AddressCount == 0)) - return false; - if (!(s.Type is ByReferenceType byRef && byRef.ElementType.IsReferenceType == false)) - return false; - if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) - return false; - if (!(Block.Unwrap(trueInst) is Throw throwInst)) - return false; - if (!condition.MatchLogicNot(out var arg)) - return false; - if (!MatchNullableCall(arg, NullableLiftingTransform.MatchHasValueCall)) - return false; - var throwInstParent = throwInst.Parent; - var throwInstChildIndex = throwInst.ChildIndex; - var ldobj = new LdObj(stloc.Value, byRef.ElementType); - var nullCoalescingWithThrow = new NullCoalescingInstruction( - NullCoalescingKind.NullableWithValueFallback, - ldobj, - throwInst); - var resultType = NullableType.GetUnderlyingType(byRef.ElementType).GetStackType(); - nullCoalescingWithThrow.UnderlyingResultType = resultType; - var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], s, nullCoalescingWithThrow, InliningOptions.None); - if (result.Type == ILInlining.FindResultType.Found - && MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault)) { - context.Step("NullCoalescingTransform (address + throw expression)", stloc); - throwInst.resultType = resultType; - result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); - block.Instructions.RemoveRange(pos, 2); // remove store and if instruction - return true; - } else { - // reset the primary position (see remarks on ILInstruction.Parent) - stloc.Value = stloc.Value; - var children = throwInstParent.Children; - children[throwInstChildIndex] = throwInst; - return false; - } - - bool MatchNullableCall(ILInstruction input, PatternMatcher matcher) - { - if (!matcher(input, out var loadInst)) - return false; - if (!loadInst.MatchLdLoc(s)) - return false; - return true; - } - } } }