Browse Source

Merge pull request #1246 from siegfriedpammer/cs73-stackalloc-initializers

C# 7.3 stackalloc initializers
pull/1347/head
Siegfried Pammer 7 years ago committed by GitHub
parent
commit
457813bac7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 392
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs
  5. 1249
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.opt.roslyn.il
  6. 1406
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.roslyn.il
  7. 105
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  8. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  9. 4
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  10. 5
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs
  11. 14
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs
  12. 7
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  13. 18
      ICSharpCode.Decompiler/DecompilerSettings.cs
  14. 4
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  15. 81
      ICSharpCode.Decompiler/IL/Instructions.cs
  16. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  17. 1
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  18. 83
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  19. 2
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  20. 361
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

5
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -186,9 +186,11 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -186,9 +186,11 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Core.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, @"Facades\System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "System.Xml.dll")),
MetadataReference.CreateFromFile(Path.Combine(refAsmPath, "Microsoft.CSharp.dll")),
MetadataReference.CreateFromFile(typeof(ValueTuple).Assembly.Location)
MetadataReference.CreateFromFile(typeof(ValueTuple).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Span<>).Assembly.Location),
};
});
@ -401,6 +403,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -401,6 +403,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
var module = new PEFile(assemblyFileName, file, PEStreamOptions.PrefetchEntireImage);
var resolver = new UniversalAssemblyResolver(assemblyFileName, false,
module.Reader.DetectTargetFrameworkId(), PEStreamOptions.PrefetchMetadata);
resolver.AddSearchDirectory(Path.GetDirectoryName(typeof(Span<>).Assembly.Location));
var typeSystem = new DecompilerTypeSystem(module, resolver, settings);
CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings);
decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAtttributes());

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -42,6 +42,7 @@ @@ -42,6 +42,7 @@
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="System.Memory" Version="4.5.1" />
</ItemGroup>
<ItemGroup>
@ -72,6 +73,7 @@ @@ -72,6 +73,7 @@
<Compile Include="TestCases\Correctness\RefLocalsAndReturns.cs" />
<Compile Include="TestCases\ILPretty\Issue1256.cs" />
<Compile Include="TestCases\ILPretty\Issue1323.cs" />
<Compile Include="TestCases\Pretty\CS73_StackAllocInitializers.cs" />
<Compile Include="TestCases\Pretty\OptionalArguments.cs" />
<Compile Include="TestCases\Pretty\CustomShortCircuitOperators.cs" />
<Compile Include="TestCases\Pretty\ReduceNesting.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -287,6 +287,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -287,6 +287,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test]
public void CS73_StackAllocInitializers([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void RefLocalsAndReturns([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{

392
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs

@ -0,0 +1,392 @@ @@ -0,0 +1,392 @@
// Copyright (c) 2018 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Runtime.InteropServices;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class CS73_StackAllocInitializers
{
[StructLayout(LayoutKind.Sequential, Size = 5)]
private struct StructWithSize5
{
public byte a;
public byte b;
public byte c;
public byte d;
public byte e;
public StructWithSize5(byte a, byte b, byte c, byte d, byte e)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
}
}
public unsafe string SimpleStackAllocStruct1()
{
StructWithSize5* ptr = stackalloc StructWithSize5[4] {
new StructWithSize5(1, 2, 3, 4, 5),
new StructWithSize5(11, 22, 33, 44, 55),
new StructWithSize5(1, 4, 8, 6, 2),
new StructWithSize5(12, 23, 34, 45, 56)
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocBool()
{
bool* ptr = stackalloc bool[4] {
false,
true,
false,
true
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string DoNotInlineTest()
{
bool* ptr = stackalloc bool[4] {
false,
true,
false,
true
};
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocByte()
{
byte* ptr = stackalloc byte[2] {
0,
1
};
Console.WriteLine(*ptr);
return UsePointer(ptr);
}
public unsafe string SimpleStackAllocPrimesAsBytes()
{
byte* ptr = stackalloc byte[55] {
1,
2,
3,
5,
7,
11,
13,
17,
19,
23,
29,
31,
37,
41,
43,
47,
53,
59,
61,
67,
71,
73,
79,
83,
89,
97,
101,
103,
107,
109,
113,
127,
131,
137,
139,
149,
151,
157,
163,
167,
173,
179,
181,
191,
193,
197,
199,
211,
223,
227,
229,
233,
239,
241,
251
};
Console.WriteLine(*ptr);
return UsePointer(ptr);
}
public unsafe string SimpleStackAllocChar()
{
char* ptr = stackalloc char[4] {
'1',
'2',
'3',
'4'
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocCharAlphabet()
{
char* ptr = stackalloc char[26] {
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z'
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocSByte()
{
sbyte* ptr = stackalloc sbyte[3] {
1,
2,
3
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocInt16()
{
short* ptr = stackalloc short[3] {
1,
2,
3
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocUInt16()
{
ushort* ptr = stackalloc ushort[3] {
1,
2,
3
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocInt32()
{
int* ptr = stackalloc int[3] {
1,
2,
3
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocInt32(int a, int b, int c)
{
int* ptr = stackalloc int[6] {
1,
a,
2,
b,
3,
c
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocInt32Fibonacci()
{
int* ptr = stackalloc int[17] {
1,
1,
2,
3,
5,
8,
13,
21,
34,
55,
89,
144,
233,
377,
610,
987,
1597
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocUInt32()
{
uint* ptr = stackalloc uint[3] {
1u,
2u,
3u
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocInt64()
{
long* ptr = stackalloc long[3] {
1L,
2L,
3L
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocUInt64()
{
ulong* ptr = stackalloc ulong[3] {
1uL,
2uL,
3uL
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string SimpleStackAllocInt32NonConstant(int a, int b, int c)
{
int* ptr = stackalloc int[6] {
0,
1,
0,
a,
b,
c
};
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string NotAnInitializer(int a, int b, int c)
{
int* ptr = stackalloc int[6];
ptr[1] = a;
ptr[3] = b;
ptr[5] = c;
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
}
public unsafe string NegativeOffsets(int a, int b, int c)
{
#if OPT
byte* intPtr = stackalloc byte[12];
*(int*)intPtr = 1;
*(int*)(intPtr - 4) = 2;
*(int*)(intPtr - 8) = 3;
int* ptr = (int*)intPtr;
Console.WriteLine(*ptr);
return UsePointer((byte*)ptr);
#else
byte* ptr = stackalloc byte[12];
*(int*)ptr = 1;
*(int*)(ptr - 4) = 2;
*(int*)(ptr - 8) = 3;
int* ptr2 = (int*)ptr;
Console.WriteLine(*ptr2);
return UsePointer((byte*)ptr2);
#endif
}
public unsafe string UsePointer(byte* ptr)
{
return ptr->ToString();
}
public string GetSpan()
{
Span<int> span = stackalloc int[GetSize()];
return UseSpan(span);
}
public string GetSpan2()
{
Span<int> span = stackalloc int[4] {
1,
2,
3,
4
};
return UseSpan(span);
}
public string UseSpan(Span<int> span)
{
throw new NotImplementedException();
}
public int GetSize()
{
throw new NotImplementedException();
}
}
}

1249
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.opt.roslyn.il

File diff suppressed because it is too large Load Diff

1406
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.roslyn.il

File diff suppressed because it is too large Load Diff

105
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);
}
@ -303,20 +309,44 @@ namespace ICSharpCode.Decompiler.CSharp @@ -303,20 +309,44 @@ 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)));
}
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;
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,
@ -335,7 +365,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -335,7 +365,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)
@ -1993,6 +2023,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1993,6 +2023,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);
@ -2188,9 +2220,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2188,9 +2220,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;
@ -2199,7 +2231,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2199,7 +2231,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;
@ -2246,7 +2278,64 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2246,7 +2278,64 @@ 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!");
StackAllocExpression stackAllocExpression;
IType elementType;
if (block.Instructions.Count < 2 || !block.Instructions[1].MatchStObj(out _, out _, out var t))
throw new ArgumentException("given Block is invalid!");
if (typeHint is PointerType pt && !TypeUtils.IsCompatibleTypeForMemoryAccess(t, pt.ElementType)) {
typeHint = new PointerType(t);
}
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++) {
// 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 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!");
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));
}
TranslatedExpression TranslatePostfixOperator(Block block)
{
var targetInst = (block.Instructions.ElementAtOrDefault(0) as StLoc)?.Value;

1
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -1048,6 +1048,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1048,6 +1048,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteKeyword(StackAllocExpression.StackallocKeywordRole);
stackAllocExpression.Type.AcceptVisitor(this);
WriteCommaSeparatedListInBrackets(new[] { stackAllocExpression.CountExpression });
stackAllocExpression.Initializer.AcceptVisitor(this);
EndNode(stackAllocExpression);
}

4
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -965,7 +965,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -965,7 +965,7 @@ namespace ICSharpCode.Decompiler.CSharp
exprBuilder.Translate(inst.Size)
}
});
stmt.AddChild(new Comment(" IL initblk instruction"), Roles.Comment);
stmt.InsertChildAfter(null, new Comment(" IL initblk instruction"), Roles.Comment);
return stmt;
}
@ -979,7 +979,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -979,7 +979,7 @@ namespace ICSharpCode.Decompiler.CSharp
exprBuilder.Translate(inst.Size)
}
});
stmt.AddChild(new Comment(" IL cpblk instruction"), Roles.Comment);
stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment);
return stmt;
}
}

5
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/ArrayCreateExpression.cs

@ -72,7 +72,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -72,7 +72,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
ArrayCreateExpression o = other as ArrayCreateExpression;
return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.AdditionalArraySpecifiers.DoMatch(o.AdditionalArraySpecifiers, match) && this.Initializer.DoMatch(o.Initializer, match);
return o != null && this.Type.DoMatch(o.Type, match)
&& this.Arguments.DoMatch(o.Arguments, match)
&& this.AdditionalArraySpecifiers.DoMatch(o.AdditionalArraySpecifiers, match)
&& this.Initializer.DoMatch(o.Initializer, match);
}
}
}

14
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/StackAllocExpression.cs

@ -33,7 +33,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -33,7 +33,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public class StackAllocExpression : Expression
{
public readonly static TokenRole StackallocKeywordRole = new TokenRole ("stackalloc");
public readonly static Role<ArrayInitializerExpression> InitializerRole = new Role<ArrayInitializerExpression>("Initializer", ArrayInitializerExpression.Null);
public CSharpTokenNode StackAllocToken {
get { return GetChildByRole (StackallocKeywordRole); }
}
@ -55,7 +56,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -55,7 +56,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public CSharpTokenNode RBracketToken {
get { return GetChildByRole (Roles.RBracket); }
}
public ArrayInitializerExpression Initializer {
get { return GetChildByRole(InitializerRole); }
set { SetChildByRole(InitializerRole, value); }
}
public override void AcceptVisitor (IAstVisitor visitor)
{
visitor.VisitStackAllocExpression (this);
@ -74,7 +80,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -74,7 +80,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
StackAllocExpression o = other as StackAllocExpression;
return o != null && this.Type.DoMatch(o.Type, match) && this.CountExpression.DoMatch(o.CountExpression, match);
return o != null && this.Type.DoMatch(o.Type, match)
&& this.CountExpression.DoMatch(o.CountExpression, match)
&& this.Initializer.DoMatch(o.Initializer, match);
}
}
}

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

@ -141,8 +141,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -141,8 +141,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override bool VisitStackAllocExpression(StackAllocExpression stackAllocExpression)
{
base.VisitStackAllocExpression(stackAllocExpression);
return true;
bool result = base.VisitStackAllocExpression(stackAllocExpression);
var rr = stackAllocExpression.GetResolveResult();
if (rr?.Type is PointerType)
return true;
return result;
}
public override bool VisitInvocationExpression(InvocationExpression invocationExpression)

18
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -90,13 +90,14 @@ namespace ICSharpCode.Decompiler @@ -90,13 +90,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;
@ -683,6 +684,21 @@ namespace ICSharpCode.Decompiler @@ -683,6 +684,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>

4
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -74,9 +74,7 @@ @@ -74,9 +74,7 @@
<Compile Include="CSharp\Syntax\ErrorNode.cs" />
<Compile Include="CSharp\Syntax\Expressions\AnonymousMethodExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\AnonymousTypeCreateExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\ArrayCreateExpression.cs">
<DependentUpon>ArrayInitializerExpression.cs</DependentUpon>
</Compile>
<Compile Include="CSharp\Syntax\Expressions\ArrayCreateExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\ArrayInitializerExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\AsExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\AssignmentExpression.cs" />

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,

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

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

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

@ -262,15 +262,92 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -262,15 +262,92 @@ 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;
IType elementType = type.TypeArguments[0];
if (newObj.Arguments[0].MatchLocAlloc(out var sizeInBytes) && MatchesElementCount(sizeInBytes, elementType, newObj.Arguments[1])) {
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 sizeInBytes) && MatchesElementCount(sizeInBytes, elementType, newObj.Arguments[1])))
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 MatchesElementCount(ILInstruction sizeInBytesInstr, IType elementType, ILInstruction elementCountInstr2)
{
var pointerType = new PointerType(elementType);
var elementCountInstr = PointerArithmeticOffset.Detect(sizeInBytesInstr, pointerType, checkForOverflow: true, unwrapZeroExtension: true);
if (!elementCountInstr.Match(elementCountInstr2).Success)
return false;
return true;
}
bool TransformDecimalCtorToConstant(NewObj inst, out LdcDecimal result)
{
IType t = inst.Method.DeclaringType;

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

@ -360,6 +360,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -360,6 +360,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 top-level target instruction into which we are inlining:
switch (next.OpCode) {

361
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;
}
@ -51,14 +55,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -51,14 +55,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (pos >= body.Instructions.Count - 2)
return false;
ILInstruction inst = body.Instructions[pos];
ILVariable v;
ILInstruction newarrExpr;
IType elementType;
int[] arrayLength;
if (inst.MatchStLoc(out v, out newarrExpr) && MatchNewArr(newarrExpr, out elementType, out arrayLength)) {
ILInstruction[] values;
int initArrayPos;
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out values, out initArrayPos)) {
if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var arrayLength)) {
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out var values, out var initArrayPos)) {
context.Step("ForwardScanInitializeArrayRuntimeHelper", inst);
var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type);
var block = BlockFromInitializer(tempStore, elementType, arrayLength, values);
@ -68,8 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -68,8 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
if (arrayLength.Length == 1) {
int instructionsToRemove;
if (HandleSimpleArrayInitializer(body, pos + 1, v, elementType, arrayLength[0], out values, out instructionsToRemove)) {
if (HandleSimpleArrayInitializer(body, pos + 1, v, elementType, arrayLength[0], out values, out var instructionsToRemove)) {
context.Step("HandleSimpleArrayInitializer", inst);
var block = new Block(BlockKind.ArrayInitializer);
var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type);
@ -100,86 +97,205 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -100,86 +97,205 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
}
// Put in a limit so that we don't consume too much memory if the code allocates a huge array
// and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler!
// const int maxConsecutiveDefaultValueExpressions = 300;
// var operands = new List<ILInstruction>();
// int numberOfInstructionsToRemove = 0;
// for (int j = pos + 1; j < body.Instructions.Count; j++) {
// var nextExpr = body.Instructions[j] as Void;
// int arrayPos;
// if (nextExpr != null && nextExpr is a.IsStoreToArray() &&
// nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) &&
// v == v3 &&
// nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) &&
// arrayPos >= operands.Count &&
// arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions &&
// !nextExpr.Arguments[2].ContainsReferenceTo(v3))
// {
// while (operands.Count < arrayPos)
// operands.Add(new ILExpression(ILCode.DefaultValue, elementType));
// operands.Add(nextExpr.Arguments[2]);
// numberOfInstructionsToRemove++;
// } else {
// break;
// }
// }
}
return false;
}
bool DoTransformMultiDim(Block body, int pos)
{
if (pos >= body.Instructions.Count - 2)
return false;
ILInstruction instr = body.Instructions[pos];
if (instr.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var arrayType, out var length)) {
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, length, out var values, out var initArrayPos)) {
var block = BlockFromInitializer(v, arrayType, length, values);
body.Instructions[pos].ReplaceWith(new StLoc(v, block));
body.Instructions.RemoveAt(initArrayPos);
ILInlining.InlineIfPossible(body, pos, context);
return true;
}
}
return false;
}
ILInstruction GetNullExpression(IType elementType)
bool DoTransformStackAllocInitializer(Block body, int pos)
{
ITypeDefinition typeDef = elementType.GetEnumUnderlyingType().GetDefinition();
if (typeDef == null)
return new DefaultValue(elementType);
switch (typeDef.KnownTypeCode) {
case KnownTypeCode.Boolean:
case KnownTypeCode.Char:
case KnownTypeCode.SByte:
case KnownTypeCode.Byte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
case KnownTypeCode.Int32:
case KnownTypeCode.UInt32:
return new LdcI4(0);
case KnownTypeCode.Int64:
case KnownTypeCode.UInt64:
return new LdcI8(0);
case KnownTypeCode.Single:
return new LdcF4(0);
case KnownTypeCode.Double:
return new LdcF8(0);
case KnownTypeCode.Decimal:
return new LdcDecimal(0);
case KnownTypeCode.Void:
throw new ArgumentException("void is not a valid element type!");
case KnownTypeCode.IntPtr:
case KnownTypeCode.UIntPtr:
default:
return new DefaultValue(elementType);
if (pos >= body.Instructions.Count - 2)
return false;
ILInstruction inst = body.Instructions[pos];
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 var 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, locallocExpr));
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);
ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context);
return true;
}
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, 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;
}
}
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 locAllocInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove)
{
int elementCount = 0;
long minExpectedOffset = 0;
values = null;
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) || !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 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 (!target.MatchLdLoc(store)) {
if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right))
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;
minExpectedOffset = offset;
}
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 (minExpectedOffset >= values.Length)
break;
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)
return false;
if (store.LoadInstructions.Last().Parent is StLoc finalStore) {
elementType = ((PointerType)finalStore.Variable.Type).ElementType;
}
instructionsToRemove += elementCount;
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;
if (!block.Instructions[i].MatchStObj(out ILInstruction target, out ILInstruction value, out IType type) || value.Descendants.OfType<IInstructionWithVariableOperand>().Any(inst => inst.Variable == store))
if (!block.Instructions[i].MatchStObj(out ILInstruction target, out ILInstruction value, out IType type)
|| value.Descendants.OfType<IInstructionWithVariableOperand>().Any(inst => inst.Variable == store))
{
break;
var ldelem = target as LdElema;
if (ldelem == null || !ldelem.Array.MatchLdLoc(store) || ldelem.Indices.Count != 1 || !ldelem.Indices[0].MatchLdcI4(out int index))
}
if (!(target is LdElema ldelem) || !ldelem.Array.MatchLdLoc(store) || ldelem.Indices.Count != 1
|| !ldelem.Indices[0].MatchLdcI4(out int index))
{
break;
}
// index must be in range [0..length[ and must be greater than or equal to nextMinimumIndex
// to avoid running out of bounds or accidentally reordering instructions or overwriting previous instructions.
// However, leaving array slots empty is allowed, as those are filled with default values when the
@ -190,7 +306,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -190,7 +306,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
values[nextMinimumIndex] = value;
nextMinimumIndex++;
elementCount++;
instructionsToRemove++;
}
if (pos + elementCount >= block.Instructions.Count)
return false;
@ -207,7 +322,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -207,7 +322,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
IType type;
for (int i = 0; i < length; i++) {
// 1. Instruction: (optional) temporary copy of store
bool hasTemporaryCopy = block.Instructions[pos].MatchStLoc(out ILVariable temp, out ILInstruction storeLoad) && storeLoad.MatchLdLoc(store);
bool hasTemporaryCopy = block.Instructions[pos].MatchStLoc(out var temp, out var storeLoad) && storeLoad.MatchLdLoc(store);
if (hasTemporaryCopy) {
if (!MatchJaggedArrayStore(block, pos + 1, temp, i, out initializer, out type))
return false;
@ -220,10 +335,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -220,10 +335,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
pos += inc;
instructionsToRemove += inc;
}
ILInstruction array;
// In case there is an extra copy of the store variable
// Remove it and use its value instead.
if (block.Instructions[pos].MatchStLoc(out finalStore, out array)) {
if (block.Instructions[pos].MatchStLoc(out finalStore, out var array)) {
instructionsToRemove++;
return array.MatchLdLoc(store);
}
@ -237,39 +351,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -237,39 +351,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
type = null;
// 3. Instruction: stobj(ldelema(ldloc temp, ldc.i4 0), ldloc tempArrayLoad)
var finalInstruction = block.Instructions.ElementAtOrDefault(pos + 1);
ILInstruction tempAccess, tempArrayLoad;
ILVariable initializerStore;
if (finalInstruction == null || !finalInstruction.MatchStObj(out tempAccess, out tempArrayLoad, out type) || !tempArrayLoad.MatchLdLoc(out initializerStore))
if (finalInstruction == null || !finalInstruction.MatchStObj(out var tempAccess, out var tempArrayLoad, out type)
|| !tempArrayLoad.MatchLdLoc(out var initializerStore))
{
return false;
var elemLoad = tempAccess as LdElema;
if (elemLoad == null || !elemLoad.Array.MatchLdLoc(store) || elemLoad.Indices.Count != 1 || !elemLoad.Indices[0].MatchLdcI4(index))
}
if (!(tempAccess is LdElema elemLoad) || !elemLoad.Array.MatchLdLoc(store) || elemLoad.Indices.Count != 1
|| !elemLoad.Indices[0].MatchLdcI4(index))
{
return false;
}
// 2. Instruction: stloc(temp) with block (array initializer)
var nextInstruction = block.Instructions.ElementAtOrDefault(pos);
return nextInstruction != null && nextInstruction.MatchStLoc(initializerStore, out initializer) && initializer.OpCode == OpCode.Block;
}
bool DoTransformMultiDim(Block body, int pos)
{
if (pos >= body.Instructions.Count - 2)
return false;
ILVariable v;
ILInstruction newarrExpr;
IType arrayType;
int[] length;
ILInstruction instr = body.Instructions[pos];
if (instr.MatchStLoc(out v, out newarrExpr) && MatchNewArr(newarrExpr, out arrayType, out length)) {
ILInstruction[] values;
int initArrayPos;
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, length, out values, out initArrayPos)) {
var block = BlockFromInitializer(v, arrayType, length, values);
body.Instructions[pos].ReplaceWith(new StLoc(v, block));
body.Instructions.RemoveAt(initArrayPos);
ILInlining.InlineIfPossible(body, pos, context);
return true;
}
}
return false;
return nextInstruction != null
&& nextInstruction.MatchStLoc(initializerStore, out initializer)
&& initializer.OpCode == OpCode.Block;
}
Block BlockFromInitializer(ILVariable v, IType elementType, int[] arrayLength, ILInstruction[] values)
@ -290,19 +386,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -290,19 +386,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return block;
}
internal static bool MatchNewArr(ILInstruction instruction, out IType arrayType, out int[] length)
static bool MatchNewArr(ILInstruction instruction, out IType arrayType, out int[] length)
{
NewArr newArr = instruction as NewArr;
length = null;
arrayType = null;
if (newArr == null)
if (!(instruction is NewArr newArr))
return false;
arrayType = newArr.Type;
var args = newArr.Indices;
length = new int[args.Count];
for (int i = 0; i < args.Count; i++) {
int value;
if (!args[i].MatchLdcI4(out value) || value <= 0) return false;
if (!args[i].MatchLdcI4(out int value) || value <= 0) return false;
length[i] = value;
}
return true;
@ -313,18 +407,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -313,18 +407,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
method = null;
array = null;
field = default;
Call call = instruction as Call;
if (call == null || call.Arguments.Count != 2)
if (!(instruction is Call call) || call.Arguments.Count != 2)
return false;
method = call.Method;
if (method.DeclaringTypeDefinition == null || method.DeclaringTypeDefinition.FullName != "System.Runtime.CompilerServices.RuntimeHelpers")
if (!method.IsStatic || method.Name != "InitializeArray" || method.DeclaringTypeDefinition == null)
return false;
if (method.Name != "InitializeArray")
var declaringType = method.DeclaringTypeDefinition;
if (declaringType.DeclaringType != null || declaringType.Name != "RuntimeHelpers"
|| declaringType.Namespace != "System.Runtime.CompilerServices")
{
return false;
}
if (!call.Arguments[0].MatchLdLoc(out array))
return false;
IMember member;
if (!call.Arguments[1].MatchLdMemberToken(out member))
if (!call.Arguments[1].MatchLdMemberToken(out var member))
return false;
if (member.MetadataToken.IsNil)
return false;
@ -415,6 +511,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -415,6 +511,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return new StObj(new LdElema(type, array, indices), value, type);
}
internal static ILInstruction GetNullExpression(IType elementType)
{
ITypeDefinition typeDef = elementType.GetEnumUnderlyingType().GetDefinition();
if (typeDef == null)
return new DefaultValue(elementType);
switch (typeDef.KnownTypeCode) {
case KnownTypeCode.Boolean:
case KnownTypeCode.Char:
case KnownTypeCode.SByte:
case KnownTypeCode.Byte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
case KnownTypeCode.Int32:
case KnownTypeCode.UInt32:
return new LdcI4(0);
case KnownTypeCode.Int64:
case KnownTypeCode.UInt64:
return new LdcI8(0);
case KnownTypeCode.Single:
return new LdcF4(0);
case KnownTypeCode.Double:
return new LdcF8(0);
case KnownTypeCode.Decimal:
return new LdcDecimal(0);
case KnownTypeCode.Void:
throw new ArgumentException("void is not a valid element type!");
case KnownTypeCode.IntPtr:
case KnownTypeCode.UIntPtr:
default:
return new DefaultValue(elementType);
}
}
static int ElementSizeOf(TypeCode elementType)
{
switch (elementType) {

Loading…
Cancel
Save