Browse Source

Add support for stackalloc initializer pattern that uses initblk.

pull/1246/head
Siegfried Pammer 7 years ago
parent
commit
772b529d16
  1. 15
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 53
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

15
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -2203,25 +2203,30 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2203,25 +2203,30 @@ namespace ICSharpCode.Decompiler.CSharp
var initializer = stackAllocExpression.Initializer = new ArrayInitializerExpression();
var pointerType = new PointerType(elementType);
long expectedOffset = 0;
for (int i = 1; i < block.Instructions.Count; i++) {
// stobj type(binary.add.i(ldloc I_0, conv i4->i <sign extend> (ldc.i4 offset)), value)
if (!block.Instructions[i].MatchStObj(out var target, out var value, out var t) || !TypeUtils.IsCompatibleTypeForMemoryAccess(elementType, t))
throw new ArgumentException("given Block is invalid!");
if (i == 1) {
if (!target.MatchLdLoc(stloc.Variable))
throw new ArgumentException("given Block is invalid!");
} else {
long offset = 0;
if (!target.MatchLdLoc(stloc.Variable)) {
if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right))
throw new ArgumentException("given Block is invalid!");
var binary = (BinaryNumericInstruction)target;
var offsetInst = PointerArithmeticOffset.Detect(right, pointerType, binary.CheckForOverflow);
if (!left.MatchLdLoc(final.Variable) || offsetInst == null)
throw new ArgumentException("given Block is invalid!");
if (!offsetInst.MatchLdcI(i - 1))
if (!offsetInst.MatchLdcI(out offset))
throw new ArgumentException("given Block is invalid!");
}
while (expectedOffset < offset) {
initializer.Elements.Add(Translate(IL.Transforms.TransformArrayInitializers.GetNullExpression(elementType), typeHint: elementType));
expectedOffset++;
}
var val = Translate(value, typeHint: elementType).ConvertTo(elementType, this, allowImplicitConversion: true);
initializer.Elements.Add(val);
expectedOffset++;
}
return stackAllocExpression.WithILInstruction(block)
.WithRR(new ResolveResult(stloc.Variable.Type));

53
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -179,15 +179,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -179,15 +179,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInlining.InlineIfPossible(body, pos, context);
return true;
}
if (HandleSequentialLocAllocInitializer(body, pos + 1, v, lengthInst, out elementType, out StObj[] values)) {
if (HandleSequentialLocAllocInitializer(body, pos + 1, v, lengthInst, out elementType, out StObj[] values, out int instructionsToRemove)) {
context.Step("HandleSequentialLocAllocInitializer", inst);
var block = new Block(BlockKind.StackAllocInitializer);
var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType));
block.Instructions.Add(new StLoc(tempStore, new LocAlloc(lengthInst)));
block.Instructions.AddRange(values.Select(value => RewrapStore(tempStore, value, elementType)));
block.Instructions.AddRange(values.Where(value => value != null).Select(value => RewrapStore(tempStore, value, elementType)));
block.FinalInstruction = new LdLoc(tempStore);
body.Instructions[pos] = new StLoc(v, block);
body.Instructions.RemoveRange(pos + 1, values.Length);
body.Instructions.RemoveRange(pos + 1, instructionsToRemove);
ILInlining.InlineIfPossible(body, pos, context);
return true;
}
@ -217,15 +217,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -217,15 +217,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction lengthInstruction, out IType elementType, out StObj[] values)
bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction lengthInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove)
{
int elementCount = 0;
long minExpectedOffset = 0;
values = null;
elementType = null;
instructionsToRemove = 0;
if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size)
&& lengthInstruction.MatchLdcI(out long byteCount))
{
if (!dest.MatchLdLoc(store) || !value.MatchLdcI4(0) || !size.MatchLdcI(byteCount))
return false;
instructionsToRemove++;
pos++;
}
for (int i = pos; i < block.Instructions.Count; i++) {
// match the basic stobj pattern
if (!block.Instructions[i].MatchStObj(out ILInstruction target, out ILInstruction value, out var currentType)
if (!block.Instructions[i].MatchStObj(out ILInstruction target, out value, out var currentType)
|| value.Descendants.OfType<IInstructionWithVariableOperand>().Any(inst => inst.Variable == store))
break;
if (elementType != null && !currentType.Equals(elementType))
@ -234,19 +245,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -234,19 +245,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// match the target
// should be either ldloc store (at offset 0)
// or binary.add(ldloc store, offset) where offset is either 'elementSize' or 'i * elementSize'
if (elementCount == 0) {
if (!target.MatchLdLoc(store))
break;
} else {
if (!target.MatchLdLoc(store)) {
if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right))
break;
return false;
if (!left.MatchLdLoc(store))
break;
var offsetInst = PointerArithmeticOffset.Detect(right, new PointerType(elementType), ((BinaryNumericInstruction)target).CheckForOverflow);
if (offsetInst == null)
return false;
if (!offsetInst.MatchLdcI(out long offset) || offset < 0 || offset < minExpectedOffset)
break;
if (!offsetInst.MatchLdcI(elementCount))
break;
minExpectedOffset = offset;
}
if (values == null) {
var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, new PointerType(elementType), checkForOverflow: true);
@ -254,24 +263,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -254,24 +263,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
values = new StObj[(int)valuesLength];
}
if (i - pos >= values.Length)
if (minExpectedOffset >= values.Length)
break;
values[i - pos] = (StObj)block.Instructions[i];
values[minExpectedOffset] = (StObj)block.Instructions[i];
elementCount++;
}
if (values == null || store.Kind != VariableKind.StackSlot || store.StoreCount != 1
|| store.AddressCount != 0 || store.LoadCount != values.Length + 1)
|| store.AddressCount != 0 || store.LoadCount > values.Length + 1)
return false;
var finalStore = store.LoadInstructions.Last().Parent as StLoc;
if (finalStore == null)
return false;
elementType = ((PointerType)finalStore.Variable.Type).ElementType;
if (store.LoadInstructions.Last().Parent is StLoc finalStore) {
elementType = ((PointerType)finalStore.Variable.Type).ElementType;
}
instructionsToRemove += elementCount;
return elementCount == values.Length;
return elementCount <= values.Length;
}
ILInstruction RewrapStore(ILVariable target, StObj storeInstruction, IType type)
@ -528,7 +535,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -528,7 +535,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return new StObj(new LdElema(type, array, indices), value, type);
}
static ILInstruction GetNullExpression(IType elementType)
internal static ILInstruction GetNullExpression(IType elementType)
{
ITypeDefinition typeDef = elementType.GetEnumUnderlyingType().GetDefinition();
if (typeDef == null)

Loading…
Cancel
Save