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
protected internal override TranslatedExpression VisitNewObj(NewObj inst, TranslationContext context) 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); return new CallBuilder(this, typeSystem, settings).Build(inst);
} }
@ -310,6 +316,24 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(elementType))); .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) StackAllocExpression TranslateLocAlloc(LocAlloc inst, IType typeHint, out IType elementType)
{ {
TranslatedExpression countExpression; TranslatedExpression countExpression;
@ -2258,13 +2282,20 @@ namespace ICSharpCode.Decompiler.CSharp
var final = block.FinalInstruction as LdLoc; var final = block.FinalInstruction as LdLoc;
if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget)
throw new ArgumentException("given Block is invalid!"); throw new ArgumentException("given Block is invalid!");
if (!(stloc.Value is LocAlloc locAlloc)) StackAllocExpression stackAllocExpression;
throw new ArgumentException("given Block is invalid!"); IType elementType;
switch (stloc.Value) {
var stackAllocExpression = TranslateLocAlloc(locAlloc, typeHint, out var elementType); 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 initializer = stackAllocExpression.Initializer = new ArrayInitializerExpression();
var pointerType = new PointerType(elementType); var pointerType = new PointerType(elementType);
long expectedOffset = 0; long expectedOffset = 0;
for (int i = 1; i < block.Instructions.Count; i++) { for (int i = 1; i < block.Instructions.Count; i++) {
@ -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)) 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!"); throw new ArgumentException("given Block is invalid!");
long offset = 0; long offset = 0;
target = target.UnwrapConv(ConversionKind.StopGCTracking);
if (!target.MatchLdLoc(stloc.Variable)) { if (!target.MatchLdLoc(stloc.Variable)) {
if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right))
throw new ArgumentException("given Block is invalid!"); throw new ArgumentException("given Block is invalid!");
var binary = (BinaryNumericInstruction)target; var binary = (BinaryNumericInstruction)target;
left = left.UnwrapConv(ConversionKind.StopGCTracking);
var offsetInst = PointerArithmeticOffset.Detect(right, pointerType, binary.CheckForOverflow); var offsetInst = PointerArithmeticOffset.Detect(right, pointerType, binary.CheckForOverflow);
if (!left.MatchLdLoc(final.Variable) || offsetInst == null) if (!left.MatchLdLoc(final.Variable) || offsetInst == null)
throw new ArgumentException("given Block is invalid!"); throw new ArgumentException("given Block is invalid!");

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

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

81
ICSharpCode.Decompiler/IL/Instructions.cs

@ -141,6 +141,8 @@ namespace ICSharpCode.Decompiler.IL
LdMemberToken, LdMemberToken,
/// <summary>Allocates space in the stack frame</summary> /// <summary>Allocates space in the stack frame</summary>
LocAlloc, LocAlloc,
/// <summary>Allocates space in the stack frame and wraps it in a Span</summary>
LocAllocSpan,
/// <summary>memcpy(destAddress, sourceAddress, size);</summary> /// <summary>memcpy(destAddress, sourceAddress, size);</summary>
Cpblk, Cpblk,
/// <summary>memset(address, value, size)</summary> /// <summary>memset(address, value, size)</summary>
@ -3192,6 +3194,60 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
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> /// <summary>memcpy(destAddress, sourceAddress, size);</summary>
public sealed partial class Cpblk : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix public sealed partial class Cpblk : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix
@ -6497,6 +6553,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(inst); Default(inst);
} }
protected internal virtual void VisitLocAllocSpan(LocAllocSpan inst)
{
Default(inst);
}
protected internal virtual void VisitCpblk(Cpblk inst) protected internal virtual void VisitCpblk(Cpblk inst)
{ {
Default(inst); Default(inst);
@ -6875,6 +6935,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst); return Default(inst);
} }
protected internal virtual T VisitLocAllocSpan(LocAllocSpan inst)
{
return Default(inst);
}
protected internal virtual T VisitCpblk(Cpblk inst) protected internal virtual T VisitCpblk(Cpblk inst)
{ {
return Default(inst); return Default(inst);
@ -7253,6 +7317,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst, context); 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) protected internal virtual T VisitCpblk(Cpblk inst, C context)
{ {
return Default(inst, context); return Default(inst, context);
@ -7470,6 +7538,7 @@ namespace ICSharpCode.Decompiler.IL
"ldtypetoken", "ldtypetoken",
"ldmembertoken", "ldmembertoken",
"localloc", "localloc",
"localloc.span",
"cpblk", "cpblk",
"initblk", "initblk",
"ldflda", "ldflda",
@ -7816,6 +7885,18 @@ namespace ICSharpCode.Decompiler.IL
argument = default(ILInstruction); argument = default(ILInstruction);
return false; 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) public bool MatchCpblk(out ILInstruction destAddress, out ILInstruction sourceAddress, out ILInstruction size)
{ {
var inst = this as Cpblk; var inst = this as Cpblk;

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -218,6 +218,8 @@
CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, ResultType("O")), CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, ResultType("O")),
new OpCode("localloc", "Allocates space in the stack frame", new OpCode("localloc", "Allocates space in the stack frame",
CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow), 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);", new OpCode("cpblk", "memcpy(destAddress, sourceAddress, size);",
CustomArguments(("destAddress", new[] { "I", "Ref" }), ("sourceAddress", new[] { "I", "Ref" }), ("size", new[] { "I4" })), CustomArguments(("destAddress", new[] { "I", "Ref" }), ("sourceAddress", new[] { "I", "Ref" }), ("size", new[] { "I4" })),
MayThrow, MemoryAccess, MayThrow, MemoryAccess,

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

@ -262,15 +262,82 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitNewObj(NewObj inst) protected internal override void VisitNewObj(NewObj inst)
{ {
LdcDecimal decimalConstant; if (TransformDecimalCtorToConstant(inst, out LdcDecimal decimalConstant)) {
if (TransformDecimalCtorToConstant(inst, out decimalConstant)) {
context.Step("TransformDecimalCtorToConstant", inst); context.Step("TransformDecimalCtorToConstant", inst);
inst.ReplaceWith(decimalConstant); inst.ReplaceWith(decimalConstant);
return; 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); 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) bool TransformDecimalCtorToConstant(NewObj inst, out LdcDecimal result)
{ {
IType t = inst.Method.DeclaringType; IType t = inst.Method.DeclaringType;

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

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

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

@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
context.Step("HandleCpblkInitializer", inst); context.Step("HandleCpblkInitializer", inst);
var block = new Block(BlockKind.StackAllocInitializer); var block = new Block(BlockKind.StackAllocInitializer);
var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType)); 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) { while (blob.RemainingBytes > 0) {
block.Instructions.Add(StElemPtr(tempStore, blob.Offset, new LdcI4(blob.ReadByte()), elementType)); block.Instructions.Add(StElemPtr(tempStore, blob.Offset, new LdcI4(blob.ReadByte()), elementType));
@ -138,18 +138,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
body.Instructions[pos] = new StLoc(v, block); body.Instructions[pos] = new StLoc(v, block);
body.Instructions.RemoveAt(pos + 1); body.Instructions.RemoveAt(pos + 1);
ILInlining.InlineIfPossible(body, pos, context); ILInlining.InlineIfPossible(body, pos, context);
ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context);
return true; 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); context.Step("HandleSequentialLocAllocInitializer", inst);
var block = new Block(BlockKind.StackAllocInitializer); var block = new Block(BlockKind.StackAllocInitializer);
var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType)); 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.Instructions.AddRange(values.Where(value => value != null).Select(value => RewrapStore(tempStore, value, elementType)));
block.FinalInstruction = new LdLoc(tempStore); block.FinalInstruction = new LdLoc(tempStore);
body.Instructions[pos] = new StLoc(v, block); body.Instructions[pos] = new StLoc(v, block);
body.Instructions.RemoveRange(pos + 1, instructionsToRemove); body.Instructions.RemoveRange(pos + 1, instructionsToRemove);
ILInlining.InlineIfPossible(body, pos, context); ILInlining.InlineIfPossible(body, pos, context);
ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context);
return true; return true;
} }
} }
@ -178,7 +180,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; 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; int elementCount = 0;
long minExpectedOffset = 0; long minExpectedOffset = 0;
@ -186,10 +188,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
elementType = null; elementType = null;
instructionsToRemove = 0; instructionsToRemove = 0;
if (!locAllocInstruction.MatchLocAlloc(out var lengthInstruction))
return false;
if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size) if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size)
&& lengthInstruction.MatchLdcI(out long byteCount)) && lengthInstruction.MatchLdcI(out long byteCount))
{ {
if (!dest.MatchLdLoc(store) || !value.MatchLdcI4(0) || !size.MatchLdcI(byteCount)) if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount))
return false; return false;
instructionsToRemove++; instructionsToRemove++;
pos++; pos++;

Loading…
Cancel
Save