Browse Source

Simplify nullable value types throw expression pattern.

pull/1600/head
Siegfried Pammer 6 years ago
parent
commit
c0f954aaa6
  1. 119
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

119
ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

@ -36,9 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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) ...
/// </summary>
bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context)
{
@ -128,47 +117,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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;
}
}
/// <summary>
/// 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) ...
/// </summary>
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;
}
}
}
}

Loading…
Cancel
Save