Browse Source

#1852: Handle fixed statement with null-safe GetPinnableReference call

pull/1880/head
Daniel Grunwald 6 years ago
parent
commit
f0292808b3
  1. 155
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

155
ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
this.context = context;
foreach (var container in function.Descendants.OfType<BlockContainer>()) {
context.CancellationToken.ThrowIfCancellationRequested();
DetectNullSafeArrayToPointer(container);
DetectNullSafeArrayToPointerOrCustomRefPin(container);
SplitBlocksAtWritesToPinnedLocals(container);
foreach (var block in container.Blocks)
DetectPinnedRegion(block);
@ -112,30 +112,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -112,30 +112,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
#region null-safe array to pointer
void DetectNullSafeArrayToPointer(BlockContainer container)
void DetectNullSafeArrayToPointerOrCustomRefPin(BlockContainer container)
{
// Detect the following pattern:
// ...
// stloc V(ldloc S)
// if (comp(ldloc S == ldnull)) br B_null_or_empty
// br B_not_null
// }
// Block B_not_null {
// if (conv i->i4 (ldlen(ldloc V))) br B_not_null_and_not_empty
// br B_null_or_empty
// }
// Block B_not_null_and_not_empty {
// stloc P(ldelema(ldloc V, ldc.i4 0, ...))
// br B_target
// }
// Block B_null_or_empty {
// stloc P(conv i4->u(ldc.i4 0))
// br B_target
// }
// And convert the whole thing into:
// ...
// stloc P(array.to.pointer(V))
// br B_target
bool modified = false;
for (int i = 0; i < container.Blocks.Count; i++) {
var block = container.Blocks[i];
@ -149,13 +127,140 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -149,13 +127,140 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
.WithILRange(block.Instructions[block.Instructions.Count - 2]);
((Branch)block.Instructions.Last()).TargetBlock = targetBlock;
modified = true;
} else if (IsCustomRefPinPattern(block, out ILInstruction ldlocMem, out var callGPR, out v, out var stlocPtr, out targetBlock)) {
context.Step("CustomRefPinPattern", block);
ILInstruction gpr;
if (context.Settings.PatternBasedFixedStatement) {
gpr = new GetPinnableReference(ldlocMem, callGPR.Method);
} else {
gpr = new IfInstruction(
condition: new Comp(ComparisonKind.Inequality, Sign.None, ldlocMem, new LdNull()),
trueInst: callGPR,
falseInst: new Conv(new LdcI4(0), PrimitiveType.Ref, checkForOverflow: false, inputSign: Sign.None)
);
}
block.Instructions[block.Instructions.Count - 2] = new StLoc(v, gpr)
.WithILRange(block.Instructions[block.Instructions.Count - 2]);
block.Instructions.Insert(block.Instructions.Count - 1, stlocPtr);
((Branch)block.Instructions.Last()).TargetBlock = targetBlock;
modified = true;
}
}
if (modified) {
container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0); // remove blocks made unreachable
}
}
// Detect the following pattern:
// if (comp.o(ldloc mem != ldnull)) br notNullBlock
// br nullBlock
// }
//
// Block nullBlock (incoming: 1) {
// stloc ptr(conv i4->u <zero extend>(ldc.i4 0))
// br targetBlock
// }
//
// Block notNullBlock (incoming: 1) {
// stloc V_1(call GetPinnableReference(ldloc mem))
// stloc ptr(conv ref->u (ldloc V_1))
// br targetBlock
// }
// It will be replaced with:
// stloc V_1(get.pinnable.reference(ldloc mem))
// stloc ptr(conv ref->u (ldloc V_1))
// br targetBlock
private bool IsCustomRefPinPattern(Block block, out ILInstruction ldlocMem, out CallInstruction callGPR,
out ILVariable v, out StLoc ptrAssign, out Block targetBlock)
{
ldlocMem = null;
callGPR = null;
v = null;
ptrAssign = null;
targetBlock = null;
// if (comp.o(ldloc mem != ldnull)) br on_not_null
// br on_null
if (!block.MatchIfAtEndOfBlock(out var ifCondition, out var trueInst, out var falseInst))
return false;
if (!ifCondition.MatchCompNotEqualsNull(out ldlocMem))
return false;
if (!SemanticHelper.IsPure(ldlocMem.Flags))
return false;
if (!trueInst.MatchBranch(out Block notNullBlock) || notNullBlock.Parent != block.Parent)
return false;
if (!falseInst.MatchBranch(out Block nullBlock) || nullBlock.Parent != block.Parent)
return false;
// Block nullBlock (incoming: 1) {
// stloc ptr(conv i4->u <zero extend>(ldc.i4 0))
// br targetBlock
// }
if (nullBlock.IncomingEdgeCount != 1)
return false;
if (nullBlock.Instructions.Count != 2)
return false;
if (!nullBlock.Instructions[0].MatchStLoc(out var ptr, out var nullPointerInst))
return false;
if (!nullPointerInst.MatchLdcI(0))
return false;
if (!nullBlock.Instructions[1].MatchBranch(out targetBlock))
return false;
// Block notNullBlock (incoming: 1) {
// stloc V_1(call GetPinnableReference(ldloc mem))
// stloc ptr(conv ref->u (ldloc V_1))
// br targetBlock
// }
if (notNullBlock.IncomingEdgeCount != 1)
return false;
if (notNullBlock.Instructions.Count != 3)
return false;
// stloc V_1(call GetPinnableReference(ldloc mem))
if (!notNullBlock.Instructions[0].MatchStLoc(out v, out var value))
return false;
if (v.Kind != VariableKind.PinnedLocal)
return false;
callGPR = value as CallInstruction;
if (callGPR == null || callGPR.Arguments.Count != 1)
return false;
if (callGPR.Method.Name != "GetPinnableReference")
return false;
if (!ldlocMem.Match(callGPR.Arguments[0]).Success)
return false;
// stloc ptr(conv ref->u (ldloc V_1))
ptrAssign = notNullBlock.Instructions[1] as StLoc;
if (ptrAssign == null || ptrAssign.Variable != ptr)
return false;
if (!ptrAssign.Value.UnwrapConv(ConversionKind.StopGCTracking).MatchLdLoc(v))
return false;
// br targetBlock
if (!notNullBlock.Instructions[2].MatchBranch(targetBlock))
return false;
return true;
}
// Detect the following pattern:
// ...
// stloc V(ldloc S)
// if (comp(ldloc S == ldnull)) br B_null_or_empty
// br B_not_null
// }
// Block B_not_null {
// if (conv i->i4 (ldlen(ldloc V))) br B_not_null_and_not_empty
// br B_null_or_empty
// }
// Block B_not_null_and_not_empty {
// stloc P(ldelema(ldloc V, ldc.i4 0, ...))
// br B_target
// }
// Block B_null_or_empty {
// stloc P(conv i4->u(ldc.i4 0))
// br B_target
// }
// And convert the whole thing into:
// ...
// stloc P(array.to.pointer(V))
// br B_target
bool IsNullSafeArrayToPointerPattern(Block block, out ILVariable v, out ILVariable p, out Block targetBlock)
{
v = null;

Loading…
Cancel
Save