Browse Source

Move TransformNullPropagationOnUnconstrainedGenericExpression into NullPropagationTransform and make use of IsValidAccessChain

pull/1953/head
Siegfried Pammer 6 years ago
parent
commit
effc49c479
  1. 141
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

141
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -142,9 +142,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary> /// </summary>
internal void RunStatements(Block block, int pos) internal void RunStatements(Block block, int pos)
{ {
var ifInst = block.Instructions[pos] as IfInstruction; if (block.Instructions[pos] is IfInstruction ifInst && ifInst.FalseInst.MatchNop()) {
if (ifInst == null || !ifInst.FalseInst.MatchNop())
return;
if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.Inequality if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.Inequality
&& comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) { && comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) {
TryNullPropForVoidCall(testedVar, Mode.ReferenceType, ifInst.TrueInst as Block, ifInst); TryNullPropForVoidCall(testedVar, Mode.ReferenceType, ifInst.TrueInst as Block, ifInst);
@ -156,6 +154,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
} }
if (TransformNullPropagationOnUnconstrainedGenericExpression(block, pos, out var target, out var value, out var rewrapPoint, out var endBlock)) {
context.Step("TransformNullPropagationOnUnconstrainedGenericExpression", block);
// A successful match was found:
// 1. The 'target' instruction, that is, the instruction where the actual 'null-propagating' call happens:
// <target>.Call() is replaced with <value>?.Call()
// 2. The 'value' instruction, that is, the instruction that produces the value:
// It is inlined at the location of 'target'.
// 3. The 'rewrapPoint' instruction is an ancestor of the call that is used as location for the NullableRewrap instruction
// Remove the fallback conditions and blocks
block.Instructions.RemoveRange(pos, 3);
// inline value and wrap it in a NullableUnwrap instruction to produce 'value?'.
target.ReplaceWith(new NullableUnwrap(value.ResultType, value, refInput: true));
var siblings = rewrapPoint.Parent.Children;
int index = rewrapPoint.ChildIndex;
// remove Nullable-ctor, if necessary
if (NullableLiftingTransform.MatchNullableCtor(rewrapPoint, out var utype, out var arg) && arg.InferType(context.TypeSystem).Equals(utype)) {
rewrapPoint = arg;
}
// insert a NullableRewrap instruction at the 'rewrapPoint'
siblings[index] = new NullableRewrap(rewrapPoint);
// if the endBlock is only reachable through the current block,
// combine both blocks.
if (endBlock?.IncomingEdgeCount == 1) {
block.Instructions.AddRange(endBlock.Instructions);
block.Instructions.RemoveAt(pos + 1);
endBlock.Remove();
}
}
}
void TryNullPropForVoidCall(ILVariable testedVar, Mode mode, Block body, IfInstruction ifInst) void TryNullPropForVoidCall(ILVariable testedVar, Mode mode, Block body, IfInstruction ifInst)
{ {
@ -310,60 +339,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
oldParentChildren[oldChildIndex] = replacement; oldParentChildren[oldChildIndex] = replacement;
} }
}
class NullPropagationStatementTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{
if (!context.Settings.NullPropagation)
return;
new NullPropagationTransform(context).RunStatements(block, pos);
if (TransformNullPropagationOnUnconstrainedGenericExpression(block, pos, out var target, out var value, out var rewrapPoint, out var endBlock)) {
context.Step("TransformNullPropagationOnUnconstrainedGenericExpression", block);
// A successful match was found:
// 1. The 'target' instruction, that is, the instruction where the actual 'null-propagating' call happens:
// <target>.Call() is replaced with <value>?.Call()
// 2. The 'value' instruction, that is, the instruction that produces the value:
// It is inlined at the location of 'target'.
// 3. The 'rewrapPoint' instruction is an ancestor of the call that is used as location for the NullableRewrap instruction,
// if the expression does not yet contain a NullableRewrap.
// First try to find a NullableRewrap instruction
bool needsRewrap = true;
var tmp = target;
while (needsRewrap && tmp != null) {
if (tmp is NullableRewrap) {
needsRewrap = false;
break;
}
tmp = tmp.Parent;
}
// Remove the fallback conditions and blocks
block.Instructions.RemoveRange(pos, 3);
// inline value and wrap it in a NullableUnwrap instruction to produce 'value?'.
target.ReplaceWith(new NullableUnwrap(value.ResultType, value, refInput: true));
var siblings = rewrapPoint.Parent.Children;
int index = rewrapPoint.ChildIndex;
// remove Nullable-ctor, if necessary
if (NullableLiftingTransform.MatchNullableCtor(rewrapPoint, out var utype, out var arg) && arg.InferType(context.TypeSystem).Equals(utype)) {
rewrapPoint = arg;
}
// if the ancestors do not yet have a NullableRewrap instruction
// insert it at the 'rewrapPoint'.
if (needsRewrap) {
siblings[index] = new NullableRewrap(rewrapPoint);
}
// if the endBlock is only reachable through the current block,
// combine both blocks.
if (endBlock?.IncomingEdgeCount == 1) {
block.Instructions.AddRange(endBlock.Instructions);
block.Instructions.RemoveAt(pos + 1);
endBlock.Remove();
}
}
}
// stloc valueTemporary(valueExpression) // stloc valueTemporary(valueExpression)
// stloc defaultTemporary(default.value type) // stloc defaultTemporary(default.value type)
@ -395,11 +370,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// => // =>
// leave (nullable.rewrap(constrained[type].call_instruction(nullable.unwrap(valueExpression), ...))) // leave (nullable.rewrap(constrained[type].call_instruction(nullable.unwrap(valueExpression), ...)))
private bool TransformNullPropagationOnUnconstrainedGenericExpression(Block block, int pos, private bool TransformNullPropagationOnUnconstrainedGenericExpression(Block block, int pos,
out ILInstruction target, out ILInstruction value, out ILInstruction rewrapPoint, out Block endBlock) out ILInstruction target, out ILInstruction value, out ILInstruction nonNullInst, out Block endBlock)
{ {
target = null; target = null;
value = null; value = null;
rewrapPoint = null; nonNullInst = null;
endBlock = null; endBlock = null;
if (pos + 3 >= block.Instructions.Count) if (pos + 3 >= block.Instructions.Count)
return false; return false;
@ -417,30 +392,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// if (logic.not(comp.o(box `0(ldloc defaultTemporary) != ldnull))) Block fallbackBlock // if (logic.not(comp.o(box `0(ldloc defaultTemporary) != ldnull))) Block fallbackBlock
if (!(block.Instructions[pos + 2].MatchIfInstruction(out var condition, out var fallbackBlock1) && condition.MatchCompEqualsNull(out var arg) && arg.MatchLdLoc(defaultTemporary))) if (!(block.Instructions[pos + 2].MatchIfInstruction(out var condition, out var fallbackBlock1) && condition.MatchCompEqualsNull(out var arg) && arg.MatchLdLoc(defaultTemporary)))
return false; return false;
if (!MatchStLocResultTemporary(block, pos, type, valueTemporary, defaultTemporary, fallbackBlock1, out rewrapPoint, out var call, out endBlock) && !MatchLeaveResult(block, pos, type, valueTemporary, defaultTemporary, fallbackBlock1, out rewrapPoint, out call)) if (!MatchStLocResultTemporary(block, pos, type, valueTemporary, defaultTemporary, fallbackBlock1, out nonNullInst, out target, out endBlock)
&& !MatchLeaveResult(block, pos, type, valueTemporary, defaultTemporary, fallbackBlock1, out nonNullInst, out target))
return false; return false;
target = call.Arguments[0];
return true; return true;
} }
// stloc resultTemporary(constrained[type].call_instruction(ldloc valueTemporary, ...)) // stloc resultTemporary(constrained[type].call_instruction(ldloc valueTemporary, ...))
// br IL_0035 // br IL_0035
private bool MatchStLocResultTemporary(Block block, int pos, IType type, ILVariable valueTemporary, ILVariable defaultTemporary, ILInstruction fallbackBlock1, out ILInstruction rewrapPoint, out CallInstruction call, out Block endBlock) private bool MatchStLocResultTemporary(Block block, int pos, IType type, ILVariable valueTemporary, ILVariable defaultTemporary, ILInstruction fallbackBlock1, out ILInstruction nonNullInst, out ILInstruction finalLoad, out Block endBlock)
{ {
call = null;
endBlock = null; endBlock = null;
rewrapPoint = null; nonNullInst = null;
finalLoad = null;
if (pos + 4 >= block.Instructions.Count) if (pos + 4 >= block.Instructions.Count)
return false; return false;
// stloc resultTemporary(constrained[type].call_instruction(ldloc valueTemporary, ...)) // stloc resultTemporary(constrained[type].call_instruction(ldloc valueTemporary, ...))
if (!(block.Instructions[pos + 3].MatchStLoc(out var resultTemporary, out rewrapPoint))) if (!(block.Instructions[pos + 3].MatchStLoc(out var resultTemporary, out nonNullInst)))
return false;
var loadInCall = FindLoadInExpression(valueTemporary, rewrapPoint);
if (!(loadInCall != null && loadInCall.Ancestors.OfType<CallInstruction>().FirstOrDefault() is CallInstruction c))
return false; return false;
if (!(c.Arguments.Count > 0 && loadInCall.IsDescendantOf(c.Arguments[0]))) if (!IsValidAccessChain(valueTemporary, Mode.ReferenceType, nonNullInst, out finalLoad))
return false; return false;
// br IL_0035 // br IL_0035
if (!(block.Instructions[pos + 4].MatchBranch(out endBlock))) if (!(block.Instructions[pos + 4].MatchBranch(out endBlock)))
@ -449,41 +421,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!(fallbackBlock1 is Block b && IsFallbackBlock(b, type, valueTemporary, defaultTemporary, resultTemporary, endBlock))) if (!(fallbackBlock1 is Block b && IsFallbackBlock(b, type, valueTemporary, defaultTemporary, resultTemporary, endBlock)))
return false; return false;
call = c;
return true; return true;
} }
private bool MatchLeaveResult(Block block, int pos, IType type, ILVariable valueTemporary, ILVariable defaultTemporary, ILInstruction fallbackBlock, out ILInstruction rewrapPoint, out CallInstruction call) private bool MatchLeaveResult(Block block, int pos, IType type, ILVariable valueTemporary, ILVariable defaultTemporary, ILInstruction fallbackBlock, out ILInstruction rewrapPoint, out ILInstruction finalLoad)
{ {
call = null;
rewrapPoint = null; rewrapPoint = null;
finalLoad = null;
// leave (constrained[type].call_instruction(ldloc valueTemporary, ...)) // leave (constrained[type].call_instruction(ldloc valueTemporary, ...))
if (!(block.Instructions[pos + 3] is Leave leave && leave.IsLeavingFunction)) if (!(block.Instructions[pos + 3] is Leave leave && leave.IsLeavingFunction))
return false; return false;
rewrapPoint = leave.Value; rewrapPoint = leave.Value;
var loadInCall = FindLoadInExpression(valueTemporary, rewrapPoint); if (!IsValidAccessChain(valueTemporary, Mode.ReferenceType, rewrapPoint, out finalLoad))
if (!(loadInCall != null && loadInCall.Ancestors.OfType<CallInstruction>().FirstOrDefault() is CallInstruction c))
return false;
if (!(c.Arguments.Count > 0 && loadInCall.IsDescendantOf(c.Arguments[0])))
return false; return false;
// Analyze Block fallbackBlock // Analyze Block fallbackBlock
if (!(fallbackBlock is Block b && IsFallbackBlock(b, type, valueTemporary, defaultTemporary, null, leave.TargetContainer))) if (!(fallbackBlock is Block b && IsFallbackBlock(b, type, valueTemporary, defaultTemporary, null, leave.TargetContainer)))
return false; return false;
call = c;
return true; return true;
} }
private ILInstruction FindLoadInExpression(ILVariable variable, ILInstruction expression)
{
foreach (var load in variable.LoadInstructions) {
if (load.IsDescendantOf(expression))
return load;
}
return null;
}
// Block fallbackBlock { // Block fallbackBlock {
// stloc defaultTemporary(ldobj type(ldloc valueTemporary)) // stloc defaultTemporary(ldobj type(ldloc valueTemporary))
// stloc valueTemporary(ldloca defaultTemporary) // stloc valueTemporary(ldloca defaultTemporary)
@ -531,4 +488,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return inst.MatchLdNull() || inst.MatchDefaultValue(out _); return inst.MatchLdNull() || inst.MatchDefaultValue(out _);
} }
} }
class NullPropagationStatementTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{
if (!context.Settings.NullPropagation)
return;
new NullPropagationTransform(context).RunStatements(block, pos);
}
}
} }

Loading…
Cancel
Save