Browse Source

Update stackalloc initializer patterns for Roslyn 4.10.0.

pull/3243/head
Siegfried Pammer 10 months ago
parent
commit
dab256ceb0
  1. 114
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

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

@ -243,7 +243,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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, ReadElement(ref blob, elementType), elementType));
} }
block.FinalInstruction = new LdLoc(tempStore); block.FinalInstruction = new LdLoc(tempStore);
@ -271,13 +271,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} }
private ILInstruction ReadElement(ref BlobReader blob, IType elementType)
{
switch (elementType.GetSize())
{
case 1:
return new LdcI4(blob.ReadByte());
case 2:
return new LdcI4(blob.ReadInt16());
case 4:
return new LdcI4(blob.ReadInt32());
case 8:
return new LdcI8(blob.ReadInt64());
default:
throw new NotSupportedException();
}
}
bool HandleCpblkInitializer(Block block, int pos, ILVariable v, long length, out BlobReader blob, out IType elementType) bool HandleCpblkInitializer(Block block, int pos, ILVariable v, long length, out BlobReader blob, out IType elementType)
{ {
blob = default; blob = default;
elementType = null; elementType = null;
if (!block.Instructions[pos].MatchCpblk(out var dest, out var src, out var size)) if (!block.Instructions[pos].MatchCpblk(out var dest, out var src, out var size))
return false; return false;
if (!dest.MatchLdLoc(v) || !src.MatchLdsFlda(out var field) || !size.MatchLdcI4((int)length)) if (!dest.MatchLdLoc(v) || !MatchGetStaticFieldAddress(src, out var field) || !size.MatchLdcI4((int)length))
return false; return false;
if (!(v.IsSingleDefinition && v.LoadCount == 2)) if (!(v.IsSingleDefinition && v.LoadCount == 2))
return false; return false;
@ -303,9 +320,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
else if (value is NewObj { Arguments: { Count: 2 } } newObj else if (value is NewObj { Arguments: { Count: 2 } } newObj
&& newObj.Method.DeclaringType.IsKnownType(KnownTypeCode.SpanOfT) && newObj.Method.DeclaringType.IsKnownType(KnownTypeCode.SpanOfT)
&& newObj.Arguments[0].MatchLdLoc(v) && newObj.Arguments[0].MatchLdLoc(v)
&& newObj.Arguments[1].MatchLdcI4((int)length)) && newObj.Arguments[1].MatchLdcI4(out var elementCount))
{ {
elementType = ((ParameterizedType)newObj.Method.DeclaringType).TypeArguments[0]; elementType = ((ParameterizedType)newObj.Method.DeclaringType).TypeArguments[0];
if (elementCount != length / elementType.GetSize())
return false;
} }
else else
{ {
@ -315,7 +334,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction locAllocInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove) bool MatchGetStaticFieldAddress(ILInstruction input, out IField field)
{
if (input.MatchLdsFlda(out field))
return true;
// call get_Item(addressof System.ReadOnlySpan`1[[T]](call CreateSpan(ldmembertoken field)), ldc.i4 0)
if (input is not Call { Method.Name: "get_Item", Arguments.Count: 2 } call)
return false;
if (!call.Method.DeclaringType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
return false;
if (!call.Arguments[1].MatchLdcI4(0))
return false;
if (call.Arguments[0] is not AddressOf addressOf)
return false;
if (addressOf.Value is not Call { Method.Name: "CreateSpan", Arguments.Count: 1 } createSpanCall)
return false;
if (!IsRuntimeHelpers(createSpanCall.Method.DeclaringType))
return false;
if (!createSpanCall.Arguments[0].MatchLdMemberToken(out var member))
return false;
field = member as IField;
return field != null;
}
static bool IsRuntimeHelpers(IType type) => type is { Name: "RuntimeHelpers", Namespace: "System.Runtime.CompilerServices" };
unsafe 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;
@ -326,24 +370,60 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!locAllocInstruction.MatchLocAlloc(out var lengthInstruction)) if (!locAllocInstruction.MatchLocAlloc(out var lengthInstruction))
return false; return false;
if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size) BlobReader blob = default;
&& lengthInstruction.MatchLdcI(out long byteCount))
if (lengthInstruction.MatchLdcI(out long byteCount))
{ {
if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount)) if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size))
return false; {
instructionsToRemove++; if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount))
pos++; return false;
instructionsToRemove++;
pos++;
}
else if (block.Instructions[pos].MatchCpblk(out dest, out var src, out size))
{
if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount))
return false;
if (!MatchGetStaticFieldAddress(src, out var field))
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, context.TypeSystem);
instructionsToRemove++;
pos++;
}
} }
for (int i = pos; i < block.Instructions.Count; i++) for (int i = pos; i < block.Instructions.Count; i++)
{ {
// match the basic stobj pattern // match the basic stobj pattern
if (!block.Instructions[i].MatchStObj(out ILInstruction target, out value, out var currentType) if (!block.Instructions[i].MatchStObj(out ILInstruction target, out var value, out var currentType)
|| value.Descendants.OfType<IInstructionWithVariableOperand>().Any(inst => inst.Variable == store)) || value.Descendants.OfType<IInstructionWithVariableOperand>().Any(inst => inst.Variable == store))
break; break;
if (elementType != null && !currentType.Equals(elementType)) // first
if (elementType == null)
{
elementType = currentType;
if (blob.StartPointer != null)
{
var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, elementType, checkForOverflow: true);
if (countInstruction == null || !countInstruction.MatchLdcI(out long valuesLength) || valuesLength < 1)
return false;
values = new StObj[(int)valuesLength];
int valueIndex = 0;
while (blob.RemainingBytes > 0 && valueIndex < values.Length)
{
values[valueIndex] = StElemPtr(store, blob.Offset, ReadElement(ref blob, elementType), elementType);
valueIndex++;
}
}
}
else if (!currentType.Equals(elementType))
{
break; break;
elementType = currentType; }
// match the target // match the target
// should be either ldloc store (at offset 0) // should be either ldloc store (at offset 0)
// or binary.add(ldloc store, offset) where offset is either 'elementSize' or 'i * elementSize' // or binary.add(ldloc store, offset) where offset is either 'elementSize' or 'i * elementSize'
@ -403,7 +483,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return new StObj(targetInst, storeInstruction.Value, storeInstruction.Type); return new StObj(targetInst, storeInstruction.Value, storeInstruction.Type);
} }
ILInstruction StElemPtr(ILVariable target, int offset, LdcI4 value, IType type) StObj StElemPtr(ILVariable target, int offset, ILInstruction value, IType type)
{ {
var targetInst = offset == 0 ? (ILInstruction)new LdLoc(target) : new BinaryNumericInstruction( var targetInst = offset == 0 ? (ILInstruction)new LdLoc(target) : new BinaryNumericInstruction(
BinaryNumericOperator.Add, BinaryNumericOperator.Add,
@ -698,12 +778,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
IMethod method = call.Method; IMethod method = call.Method;
if (!method.IsStatic || method.Name != "InitializeArray" || method.DeclaringTypeDefinition == null) if (!method.IsStatic || method.Name != "InitializeArray" || method.DeclaringTypeDefinition == null)
return false; return false;
var declaringType = method.DeclaringTypeDefinition; if (!IsRuntimeHelpers(method.DeclaringType))
if (declaringType.DeclaringType != null || declaringType.Name != "RuntimeHelpers"
|| declaringType.Namespace != "System.Runtime.CompilerServices")
{
return false; return false;
}
array = call.Arguments[0]; array = call.Arguments[0];
if (!call.Arguments[1].MatchLdMemberToken(out var member)) if (!call.Arguments[1].MatchLdMemberToken(out var member))
return false; return false;

Loading…
Cancel
Save