Browse Source

Add transformation for stackalloc to Span<T>.

pull/1246/head
Siegfried Pammer 7 years ago
parent
commit
0dac55d584
  1. 44
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 2
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  3. 81
      ICSharpCode.Decompiler/IL/Instructions.cs
  4. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  5. 73
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  6. 2
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  7. 15
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

44
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -287,6 +287,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -287,6 +287,12 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitNewObj(NewObj inst, TranslationContext context)
{
var type = inst.Method.DeclaringType;
if (type.IsKnownType(KnownTypeCode.SpanOfT) || type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) {
if (inst.Arguments.Count == 2 && inst.Arguments[0] is Block b && b.Kind == BlockKind.StackAllocInitializer) {
return TranslateStackAllocInitializer(b, type.TypeArguments[0]);
}
}
return new CallBuilder(this, typeSystem, settings).Build(inst);
}
@ -310,6 +316,24 @@ namespace ICSharpCode.Decompiler.CSharp @@ -310,6 +316,24 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(elementType)));
}
protected internal override TranslatedExpression VisitLocAllocSpan(LocAllocSpan inst, TranslationContext context)
{
return TranslateLocAllocSpan(inst, context.TypeHint, out var elementType)
.WithILInstruction(inst).WithRR(new ResolveResult(inst.Type));
}
StackAllocExpression TranslateLocAllocSpan(LocAllocSpan inst, IType typeHint, out IType elementType)
{
elementType = inst.Type.TypeArguments[0];
PointerType pointerType = new PointerType(elementType);
TranslatedExpression countExpression = Translate(inst.Argument)
.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this);
return new StackAllocExpression {
Type = ConvertType(elementType),
CountExpression = countExpression
};
}
StackAllocExpression TranslateLocAlloc(LocAlloc inst, IType typeHint, out IType elementType)
{
TranslatedExpression countExpression;
@ -2258,13 +2282,20 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2258,13 +2282,20 @@ namespace ICSharpCode.Decompiler.CSharp
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);
StackAllocExpression stackAllocExpression;
IType elementType;
switch (stloc.Value) {
case LocAlloc locAlloc:
stackAllocExpression = TranslateLocAlloc(locAlloc, typeHint, out elementType);
break;
case LocAllocSpan locAllocSpan:
stackAllocExpression = TranslateLocAllocSpan(locAllocSpan, typeHint, out elementType);
break;
default:
throw new ArgumentException("given Block is invalid!");
}
var initializer = stackAllocExpression.Initializer = new ArrayInitializerExpression();
var pointerType = new PointerType(elementType);
long expectedOffset = 0;
for (int i = 1; i < block.Instructions.Count; i++) {
@ -2272,10 +2303,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2272,10 +2303,13 @@ namespace ICSharpCode.Decompiler.CSharp
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!");
long offset = 0;
target = target.UnwrapConv(ConversionKind.StopGCTracking);
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;
left = left.UnwrapConv(ConversionKind.StopGCTracking);
var offsetInst = PointerArithmeticOffset.Detect(right, pointerType, binary.CheckForOverflow);
if (!left.MatchLdLoc(final.Variable) || offsetInst == null)
throw new ArgumentException("given Block is invalid!");

2
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs

@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override bool VisitStackAllocExpression(StackAllocExpression stackAllocExpression)
{
base.VisitStackAllocExpression(stackAllocExpression);
return true;
return stackAllocExpression.GetResolveResult().Type is PointerType;
}
public override bool VisitInvocationExpression(InvocationExpression invocationExpression)

81
ICSharpCode.Decompiler/IL/Instructions.cs

@ -141,6 +141,8 @@ namespace ICSharpCode.Decompiler.IL @@ -141,6 +141,8 @@ namespace ICSharpCode.Decompiler.IL
LdMemberToken,
/// <summary>Allocates space in the stack frame</summary>
LocAlloc,
/// <summary>Allocates space in the stack frame and wraps it in a Span</summary>
LocAllocSpan,
/// <summary>memcpy(destAddress, sourceAddress, size);</summary>
Cpblk,
/// <summary>memset(address, value, size)</summary>
@ -3192,6 +3194,60 @@ namespace ICSharpCode.Decompiler.IL @@ -3192,6 +3194,60 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Allocates space in the stack frame and wraps it in a Span</summary>
public sealed partial class LocAllocSpan : UnaryInstruction
{
public LocAllocSpan(ILInstruction argument, IType type) : base(OpCode.LocAllocSpan, argument)
{
this.type = type;
}
IType type;
/// <summary>Returns the type operand.</summary>
public IType Type {
get { return type; }
set { type = value; InvalidateFlags(); }
}
public override StackType ResultType { get { return StackType.O; } }
protected override InstructionFlags ComputeFlags()
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
output.Write('(');
Argument.WriteTo(output, options);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitLocAllocSpan(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitLocAllocSpan(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitLocAllocSpan(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as LocAllocSpan;
return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>memcpy(destAddress, sourceAddress, size);</summary>
public sealed partial class Cpblk : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix
@ -6497,6 +6553,10 @@ namespace ICSharpCode.Decompiler.IL @@ -6497,6 +6553,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitLocAllocSpan(LocAllocSpan inst)
{
Default(inst);
}
protected internal virtual void VisitCpblk(Cpblk inst)
{
Default(inst);
@ -6875,6 +6935,10 @@ namespace ICSharpCode.Decompiler.IL @@ -6875,6 +6935,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitLocAllocSpan(LocAllocSpan inst)
{
return Default(inst);
}
protected internal virtual T VisitCpblk(Cpblk inst)
{
return Default(inst);
@ -7253,6 +7317,10 @@ namespace ICSharpCode.Decompiler.IL @@ -7253,6 +7317,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitLocAllocSpan(LocAllocSpan inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitCpblk(Cpblk inst, C context)
{
return Default(inst, context);
@ -7470,6 +7538,7 @@ namespace ICSharpCode.Decompiler.IL @@ -7470,6 +7538,7 @@ namespace ICSharpCode.Decompiler.IL
"ldtypetoken",
"ldmembertoken",
"localloc",
"localloc.span",
"cpblk",
"initblk",
"ldflda",
@ -7816,6 +7885,18 @@ namespace ICSharpCode.Decompiler.IL @@ -7816,6 +7885,18 @@ namespace ICSharpCode.Decompiler.IL
argument = default(ILInstruction);
return false;
}
public bool MatchLocAllocSpan(out ILInstruction argument, out IType type)
{
var inst = this as LocAllocSpan;
if (inst != null) {
argument = inst.Argument;
type = inst.Type;
return true;
}
argument = default(ILInstruction);
type = default(IType);
return false;
}
public bool MatchCpblk(out ILInstruction destAddress, out ILInstruction sourceAddress, out ILInstruction size)
{
var inst = this as Cpblk;

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -218,6 +218,8 @@ @@ -218,6 +218,8 @@
CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, ResultType("O")),
new OpCode("localloc", "Allocates space in the stack frame",
CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow),
new OpCode("localloc.span", "Allocates space in the stack frame and wraps it in a Span",
CustomClassName("LocAllocSpan"), Unary, HasTypeOperand, ResultType("O"), MayThrow),
new OpCode("cpblk", "memcpy(destAddress, sourceAddress, size);",
CustomArguments(("destAddress", new[] { "I", "Ref" }), ("sourceAddress", new[] { "I", "Ref" }), ("size", new[] { "I4" })),
MayThrow, MemoryAccess,

73
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -262,15 +262,82 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -262,15 +262,82 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitNewObj(NewObj inst)
{
LdcDecimal decimalConstant;
if (TransformDecimalCtorToConstant(inst, out decimalConstant)) {
if (TransformDecimalCtorToConstant(inst, out LdcDecimal decimalConstant)) {
context.Step("TransformDecimalCtorToConstant", inst);
inst.ReplaceWith(decimalConstant);
return;
}
if (TransformSpanTCtorContainingStackAlloc(inst, out ILInstruction locallocSpan)) {
inst.ReplaceWith(locallocSpan);
Block block = null;
ILInstruction stmt = locallocSpan;
while (stmt.Parent != null) {
if (stmt.Parent is Block b) {
block = b;
break;
}
stmt = stmt.Parent;
}
//ILInlining.InlineIfPossible(block, stmt.ChildIndex - 1, context);
return;
}
base.VisitNewObj(inst);
}
/// <summary>
/// newobj Span..ctor(localloc(conv i4->u &lt;zero extend&gt;(ldc.i4 sizeInBytes)), numberOfElementsExpr)
/// =>
/// localloc.span T(numberOfElementsExpr)
///
/// -or-
///
/// newobj Span..ctor(Block IL_0000 (StackAllocInitializer) {
/// stloc I_0(localloc(conv i4->u&lt;zero extend>(ldc.i4 sizeInBytes)))
/// ...
/// final: ldloc I_0
/// }, numberOfElementsExpr)
/// =>
/// Block IL_0000 (StackAllocInitializer) {
/// stloc I_0(localloc.span T(numberOfElementsExpr))
/// ...
/// final: ldloc I_0
/// }
/// </summary>
bool TransformSpanTCtorContainingStackAlloc(NewObj newObj, out ILInstruction locallocSpan)
{
locallocSpan = null;
IType type = newObj.Method.DeclaringType;
if (!type.IsKnownType(KnownTypeCode.SpanOfT) && !type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
return false;
if (newObj.Arguments.Count != 2 || type.TypeArguments.Count != 1)
return false;
if (newObj.Arguments[0] is LocAlloc) {
locallocSpan = new LocAllocSpan(newObj.Arguments[1], type);
return true;
}
if (newObj.Arguments[0] is Block initializer && initializer.Kind == BlockKind.StackAllocInitializer) {
if (!initializer.Instructions[0].MatchStLoc(out var initializerVariable, out var value))
return false;
if (!value.MatchLocAlloc(out _))
return false;
var newVariable = initializerVariable.Function.RegisterVariable(VariableKind.InitializerTarget, type);
foreach (var load in initializerVariable.LoadInstructions.ToArray()) {
ILInstruction newInst = new LdLoc(newVariable);
newInst.AddILRange(load.ILRange);
if (load.Parent != initializer)
newInst = new Conv(newInst, PrimitiveType.I, false, Sign.None);
load.ReplaceWith(newInst);
}
foreach (var store in initializerVariable.StoreInstructions.ToArray()) {
store.Variable = newVariable;
}
value.ReplaceWith(new LocAllocSpan(newObj.Arguments[1], type));
locallocSpan = initializer;
return true;
}
return false;
}
bool TransformDecimalCtorToConstant(NewObj inst, out LdcDecimal result)
{
IType t = inst.Method.DeclaringType;

2
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -359,6 +359,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -359,6 +359,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign)
return true; // inline into dynamic compound assignments
break;
case OpCode.LocAllocSpan:
return true; // inline size-expressions into localloc.span
}
// decide based on the target into which we are inlining
switch (next.OpCode) {

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

@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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)));
block.Instructions.Add(new StLoc(tempStore, locallocExpr));
while (blob.RemainingBytes > 0) {
block.Instructions.Add(StElemPtr(tempStore, blob.Offset, new LdcI4(blob.ReadByte()), elementType));
@ -138,18 +138,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -138,18 +138,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
body.Instructions[pos] = new StLoc(v, block);
body.Instructions.RemoveAt(pos + 1);
ILInlining.InlineIfPossible(body, pos, context);
ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context);
return true;
}
if (HandleSequentialLocAllocInitializer(body, pos + 1, v, lengthInst, out elementType, out StObj[] values, out int instructionsToRemove)) {
if (HandleSequentialLocAllocInitializer(body, pos + 1, v, locallocExpr, 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.Add(new StLoc(tempStore, locallocExpr));
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, instructionsToRemove);
ILInlining.InlineIfPossible(body, pos, context);
ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context);
return true;
}
}
@ -178,7 +180,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -178,7 +180,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction lengthInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove)
bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction locAllocInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove)
{
int elementCount = 0;
long minExpectedOffset = 0;
@ -186,10 +188,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -186,10 +188,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
elementType = null;
instructionsToRemove = 0;
if (!locAllocInstruction.MatchLocAlloc(out var lengthInstruction))
return false;
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))
if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount))
return false;
instructionsToRemove++;
pos++;

Loading…
Cancel
Save