Browse Source

Move TransformNullPropagationOnUnconstrainedGenericExpression into NullPropagationTransform and make use of IsValidAccessChain

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

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

@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
readonly ILTransformContext context;
public NullPropagationTransform(ILTransformContext context)
{
this.context = context;
@ -142,17 +142,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -142,17 +142,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary>
internal void RunStatements(Block block, int pos)
{
var ifInst = block.Instructions[pos] as IfInstruction;
if (ifInst == null || !ifInst.FalseInst.MatchNop())
return;
if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.Inequality
&& comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) {
TryNullPropForVoidCall(testedVar, Mode.ReferenceType, ifInst.TrueInst as Block, ifInst);
} else if (NullableLiftingTransform.MatchHasValueCall(ifInst.Condition, out ILInstruction arg)) {
if (arg.MatchLdLoca(out testedVar)) {
TryNullPropForVoidCall(testedVar, Mode.NullableByValue, ifInst.TrueInst as Block, ifInst);
} else if (arg.MatchLdLoc(out testedVar)) {
TryNullPropForVoidCall(testedVar, Mode.NullableByReference, ifInst.TrueInst as Block, ifInst);
if (block.Instructions[pos] is IfInstruction ifInst && ifInst.FalseInst.MatchNop()) {
if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.Inequality
&& comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) {
TryNullPropForVoidCall(testedVar, Mode.ReferenceType, ifInst.TrueInst as Block, ifInst);
} else if (NullableLiftingTransform.MatchHasValueCall(ifInst.Condition, out ILInstruction arg)) {
if (arg.MatchLdLoca(out testedVar)) {
TryNullPropForVoidCall(testedVar, Mode.NullableByValue, ifInst.TrueInst as Block, ifInst);
} else if (arg.MatchLdLoc(out testedVar)) {
TryNullPropForVoidCall(testedVar, Mode.NullableByReference, ifInst.TrueInst as Block, ifInst);
}
}
}
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();
}
}
}
@ -310,60 +339,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -310,60 +339,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
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 defaultTemporary(default.value type)
@ -395,11 +370,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -395,11 +370,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// =>
// leave (nullable.rewrap(constrained[type].call_instruction(nullable.unwrap(valueExpression), ...)))
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;
value = null;
rewrapPoint = null;
nonNullInst = null;
endBlock = null;
if (pos + 3 >= block.Instructions.Count)
return false;
@ -417,30 +392,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -417,30 +392,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// 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)))
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;
target = call.Arguments[0];
return true;
}
// stloc resultTemporary(constrained[type].call_instruction(ldloc valueTemporary, ...))
// 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;
rewrapPoint = null;
nonNullInst = null;
finalLoad = null;
if (pos + 4 >= block.Instructions.Count)
return false;
// 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;
if (!(c.Arguments.Count > 0 && loadInCall.IsDescendantOf(c.Arguments[0])))
if (!IsValidAccessChain(valueTemporary, Mode.ReferenceType, nonNullInst, out finalLoad))
return false;
// br IL_0035
if (!(block.Instructions[pos + 4].MatchBranch(out endBlock)))
@ -449,41 +421,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -449,41 +421,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!(fallbackBlock1 is Block b && IsFallbackBlock(b, type, valueTemporary, defaultTemporary, resultTemporary, endBlock)))
return false;
call = c;
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;
finalLoad = null;
// leave (constrained[type].call_instruction(ldloc valueTemporary, ...))
if (!(block.Instructions[pos + 3] is Leave leave && leave.IsLeavingFunction))
return false;
rewrapPoint = leave.Value;
var loadInCall = FindLoadInExpression(valueTemporary, rewrapPoint);
if (!(loadInCall != null && loadInCall.Ancestors.OfType<CallInstruction>().FirstOrDefault() is CallInstruction c))
return false;
if (!(c.Arguments.Count > 0 && loadInCall.IsDescendantOf(c.Arguments[0])))
if (!IsValidAccessChain(valueTemporary, Mode.ReferenceType, rewrapPoint, out finalLoad))
return false;
// Analyze Block fallbackBlock
if (!(fallbackBlock is Block b && IsFallbackBlock(b, type, valueTemporary, defaultTemporary, null, leave.TargetContainer)))
return false;
call = c;
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 {
// stloc defaultTemporary(ldobj type(ldloc valueTemporary))
// stloc valueTemporary(ldloca defaultTemporary)
@ -522,7 +479,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -522,7 +479,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else if (!(fallbackInst is Leave fallbackLeave && endBlockOrLeaveContainer is BlockContainer leaveContainer
&& fallbackLeave.TargetContainer == leaveContainer && MatchDefaultValueOrLdNull(fallbackLeave.Value)))
return false;
return true;
}
@ -531,4 +488,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -531,4 +488,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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