Browse Source

Add basic stackalloc initializer transforms.

pull/1246/head
Siegfried Pammer 8 years ago
parent
commit
057ab82534
  1. 61
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 18
      ICSharpCode.Decompiler/DecompilerSettings.cs
  3. 1
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  4. 160
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

61
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -286,20 +286,26 @@ namespace ICSharpCode.Decompiler.CSharp @@ -286,20 +286,26 @@ namespace ICSharpCode.Decompiler.CSharp
return expr.WithILInstruction(inst)
.WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, dimensions), args.Select(a => a.ResolveResult).ToList(), new ResolveResult[0]));
}
protected internal override TranslatedExpression VisitLocAlloc(LocAlloc inst, TranslationContext context)
{
return TranslateLocAlloc(inst, context.TypeHint, out var elementType)
.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(elementType)));
}
StackAllocExpression TranslateLocAlloc(LocAlloc inst, IType typeHint, out IType elementType)
{
TranslatedExpression countExpression;
PointerType pointerType;
if (inst.Argument.MatchBinaryNumericInstruction(BinaryNumericOperator.Mul, out var left, out var right)
&& right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend).MatchSizeOf(out var elementType))
&& right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend).MatchSizeOf(out elementType))
{
// Determine the element type from the sizeof
countExpression = Translate(left.UnwrapConv(ConversionKind.ZeroExtend));
pointerType = new PointerType(elementType);
} else {
// Determine the element type from the expected pointer type in this context
pointerType = context.TypeHint as PointerType;
pointerType = typeHint as PointerType;
if (pointerType != null && GetPointerArithmeticOffset(
inst.Argument, Translate(inst.Argument),
pointerType, checkForOverflow: true,
@ -318,7 +324,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -318,7 +324,7 @@ namespace ICSharpCode.Decompiler.CSharp
return new StackAllocExpression {
Type = ConvertType(elementType),
CountExpression = countExpression
}.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(elementType)));
};
}
protected internal override TranslatedExpression VisitLdcI4(LdcI4 inst, TranslationContext context)
@ -1972,6 +1978,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1972,6 +1978,8 @@ namespace ICSharpCode.Decompiler.CSharp
switch (block.Kind) {
case BlockKind.ArrayInitializer:
return TranslateArrayInitializer(block);
case BlockKind.StackAllocInitializer:
return TranslateStackAllocInitializer(block, context.TypeHint);
case BlockKind.CollectionInitializer:
case BlockKind.ObjectInitializer:
return TranslateObjectAndCollectionInitializer(block);
@ -2123,9 +2131,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2123,9 +2131,9 @@ namespace ICSharpCode.Decompiler.CSharp
if (stloc == null || final == null || !stloc.Value.MatchNewArr(out type) || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget)
throw new ArgumentException("given Block is invalid!");
var newArr = (NewArr)stloc.Value;
var translatedDimensions = newArr.Indices.Select(i => Translate(i)).ToArray();
if (!translatedDimensions.All(dim => dim.ResolveResult.IsCompileTimeConstant))
throw new ArgumentException("given Block is invalid!");
int dimensions = newArr.Indices.Count;
@ -2134,7 +2142,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2134,7 +2142,7 @@ namespace ICSharpCode.Decompiler.CSharp
var root = new ArrayInitializerExpression();
container.Push(root);
var elementResolveResults = new List<ResolveResult>();
for (int i = 1; i < block.Instructions.Count; i++) {
ILInstruction target, value, array;
IType t;
@ -2181,7 +2189,44 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2181,7 +2189,44 @@ namespace ICSharpCode.Decompiler.CSharp
return expr.WithILInstruction(block)
.WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, type, dimensions), newArr.Indices.Select(i => Translate(i).ResolveResult).ToArray(), elementResolveResults));
}
TranslatedExpression TranslateStackAllocInitializer(Block block, IType typeHint)
{
var stloc = block.Instructions.FirstOrDefault() as StLoc;
var final = block.FinalInstruction as LdLoc;
if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget)
throw new ArgumentException("given Block is invalid!");
if (!(stloc.Value is LocAlloc locAlloc))
throw new ArgumentException("given Block is invalid!");
var stackAllocExpression = TranslateLocAlloc(locAlloc, typeHint, out var elementType);
var initializer = stackAllocExpression.Initializer = new ArrayInitializerExpression();
var pointerType = new PointerType(elementType);
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 {
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))
throw new ArgumentException("given Block is invalid!");
}
var val = Translate(value, typeHint: elementType).ConvertTo(elementType, this, allowImplicitConversion: true);
initializer.Elements.Add(val);
}
return stackAllocExpression.WithILInstruction(block)
.WithRR(new ResolveResult(stloc.Variable.Type));
}
TranslatedExpression TranslatePostfixOperator(Block block)
{
var targetInst = (block.Instructions.ElementAtOrDefault(0) as StLoc)?.Value;

18
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -89,13 +89,14 @@ namespace ICSharpCode.Decompiler @@ -89,13 +89,14 @@ namespace ICSharpCode.Decompiler
}
if (languageVersion < CSharp.LanguageVersion.CSharp7_3) {
//introduceUnmanagedTypeConstraint = false;
stackAllocInitializers = false;
tupleComparisons = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (tupleComparisons)
if (tupleComparisons || stackAllocInitializers)
return CSharp.LanguageVersion.CSharp7_3;
if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments)
return CSharp.LanguageVersion.CSharp7_2;
@ -682,6 +683,21 @@ namespace ICSharpCode.Decompiler @@ -682,6 +683,21 @@ namespace ICSharpCode.Decompiler
}
}
bool stackAllocInitializers = true;
/// <summary>
/// Gets/Sets whether C# 7.3 stackalloc initializers should be used.
/// </summary>
public bool StackAllocInitializers {
get { return stackAllocInitializers; }
set {
if (stackAllocInitializers != value) {
stackAllocInitializers = value;
OnPropertyChanged();
}
}
}
bool tupleTypes = true;
/// <summary>

1
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -308,6 +308,7 @@ namespace ICSharpCode.Decompiler.IL @@ -308,6 +308,7 @@ namespace ICSharpCode.Decompiler.IL
ArrayInitializer,
CollectionInitializer,
ObjectInitializer,
StackAllocInitializer,
/// <summary>
/// Block is used for postfix operator on local variable.
/// </summary>

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

@ -39,8 +39,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -39,8 +39,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return;
this.context = context;
try {
if (!DoTransform(block, pos))
DoTransformMultiDim(block, pos);
if (DoTransform(block, pos))
return;
if (DoTransformMultiDim(block, pos))
return;
if (context.Settings.StackAllocInitializers && DoTransformStackAllocInitializer(block, pos))
return;
} finally {
this.context = null;
}
@ -152,16 +156,159 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -152,16 +156,159 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
bool DoTransformStackAllocInitializer(Block body, int pos)
{
if (pos >= body.Instructions.Count - 2)
return false;
ILInstruction inst = body.Instructions[pos];
IType elementType;
if (inst.MatchStLoc(out var v, out var locallocExpr) && locallocExpr.MatchLocAlloc(out var lengthInst)) {
if (lengthInst.MatchLdcI(out var lengthInBytes) && HandleCpblkInitializer(body, pos + 1, v, lengthInBytes, out var blob, out elementType)) {
context.Step("HandleCpblkInitializer", 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)));
while (blob.RemainingBytes > 0) {
block.Instructions.Add(StElemPtr(tempStore, blob.Offset, new LdcI4(blob.ReadByte()), elementType));
}
block.FinalInstruction = new LdLoc(tempStore);
body.Instructions[pos] = new StLoc(v, block);
body.Instructions.RemoveAt(pos + 1);
ILInlining.InlineIfPossible(body, pos, context);
return true;
}
if (HandleSequentialLocAllocInitializer(body, pos + 1, v, lengthInst, out elementType, out StObj[] values)) {
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.FinalInstruction = new LdLoc(tempStore);
body.Instructions[pos] = new StLoc(v, block);
body.Instructions.RemoveRange(pos + 1, values.Length);
ILInlining.InlineIfPossible(body, pos, context);
return true;
}
}
return false;
}
bool HandleCpblkInitializer(Block block, int pos, ILVariable v, long length, out BlobReader blob, out IType elementType)
{
blob = default;
elementType = null;
if (!block.Instructions[pos].MatchCpblk(out var dest, out var src, out var size))
return false;
if (!dest.MatchLdLoc(v) || !src.MatchLdsFlda(out var field) || !size.MatchLdcI4((int)length))
return false;
if (field.MetadataToken.IsNil)
return false;
if (!block.Instructions[pos + 1].MatchStLoc(out var finalStore, out var value))
return false;
if (!value.MatchLdLoc(v))
return false;
var fd = context.PEFile.Metadata.GetFieldDefinition((FieldDefinitionHandle)field.MetadataToken);
if (!fd.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA))
return false;
blob = fd.GetInitialValue(context.PEFile.Reader, context.TypeSystem);
elementType = ((PointerType)finalStore.Type).ElementType;
return true;
}
bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction lengthInstruction, out IType elementType, out StObj[] values)
{
int elementCount = 0;
values = null;
elementType = null;
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)
|| value.Descendants.OfType<IInstructionWithVariableOperand>().Any(inst => inst.Variable == store))
break;
if (elementType != null && !currentType.Equals(elementType))
break;
elementType = currentType;
// 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.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right))
break;
if (!left.MatchLdLoc(store))
break;
var offsetInst = PointerArithmeticOffset.Detect(right, new PointerType(elementType), ((BinaryNumericInstruction)target).CheckForOverflow);
if (offsetInst == null)
break;
if (!offsetInst.MatchLdcI(elementCount))
break;
}
if (values == null) {
var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, new PointerType(elementType), checkForOverflow: true);
if (countInstruction == null || !countInstruction.MatchLdcI(out long valuesLength) || valuesLength < 1)
return false;
values = new StObj[(int)valuesLength];
}
if (i - pos >= values.Length)
break;
values[i - pos] = (StObj)block.Instructions[i];
elementCount++;
}
if (values == null || store.Kind != VariableKind.StackSlot || store.StoreCount != 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;
return elementCount == values.Length;
}
ILInstruction RewrapStore(ILVariable target, StObj storeInstruction, IType type)
{
ILInstruction targetInst;
if (storeInstruction.Target.MatchLdLoc(out _))
targetInst = new LdLoc(target);
else if (storeInstruction.Target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) {
var old = (BinaryNumericInstruction)storeInstruction.Target;
targetInst = new BinaryNumericInstruction(BinaryNumericOperator.Add, new LdLoc(target), right,
old.CheckForOverflow, old.Sign);
} else
throw new NotSupportedException("This should never happen: Bug in HandleSequentialLocAllocInitializer!");
return new StObj(targetInst, storeInstruction.Value, storeInstruction.Type);
}
ILInstruction StElemPtr(ILVariable target, int offset, LdcI4 value, IType type)
{
var targetInst = offset == 0 ? (ILInstruction)new LdLoc(target) : new BinaryNumericInstruction(
BinaryNumericOperator.Add,
new LdLoc(target),
new Conv(new LdcI4(offset), PrimitiveType.I, false, Sign.Signed),
false,
Sign.None
);
return new StObj(targetInst, value, type);
}
/// <summary>
/// Handle simple case where RuntimeHelpers.InitializeArray is not used.
/// </summary>
internal static bool HandleSimpleArrayInitializer(Block block, int pos, ILVariable store, IType elementType, int length, out ILInstruction[] values, out int instructionsToRemove)
internal static bool HandleSimpleArrayInitializer(Block block, int pos, ILVariable store, IType elementType, int length, out ILInstruction[] values, out int elementCount)
{
instructionsToRemove = 0;
values = null;
elementCount = 0;
values = new ILInstruction[length];
int nextMinimumIndex = 0;
int elementCount = 0;
for (int i = pos; i < block.Instructions.Count; i++) {
if (nextMinimumIndex >= length)
break;
@ -180,7 +327,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -180,7 +327,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
values[nextMinimumIndex] = value;
nextMinimumIndex++;
elementCount++;
instructionsToRemove++;
}
if (pos + elementCount >= block.Instructions.Count)
return false;

Loading…
Cancel
Save