Skip to content

Commit

Permalink
Update stackalloc initializer patterns for Roslyn 4.10.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
siegfriedpammer committed Jul 20, 2024
1 parent a5ed5ec commit dab256c
Showing 1 changed file with 95 additions and 19 deletions.
114 changes: 95 additions & 19 deletions ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ bool DoTransformStackAllocInitializer(Block body, int pos)

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);
Expand Down Expand Up @@ -271,13 +271,30 @@ bool DoTransformStackAllocInitializer(Block body, int pos)
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)
{
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))
if (!dest.MatchLdLoc(v) || !MatchGetStaticFieldAddress(src, out var field) || !size.MatchLdcI4((int)length))
return false;
if (!(v.IsSingleDefinition && v.LoadCount == 2))
return false;
Expand All @@ -303,9 +320,11 @@ bool HandleCpblkInitializer(Block block, int pos, ILVariable v, long length, out
else if (value is NewObj { Arguments: { Count: 2 } } newObj
&& newObj.Method.DeclaringType.IsKnownType(KnownTypeCode.SpanOfT)
&& 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];
if (elementCount != length / elementType.GetSize())
return false;
}
else
{
Expand All @@ -315,7 +334,32 @@ bool HandleCpblkInitializer(Block block, int pos, ILVariable v, long length, out
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;
long minExpectedOffset = 0;
Expand All @@ -326,24 +370,60 @@ bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store,
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))
BlobReader blob = default;

if (lengthInstruction.MatchLdcI(out long byteCount))
{
if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount))
return false;
instructionsToRemove++;
pos++;
if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size))
{
if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount))
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++)
{
// 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))
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;
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'
Expand Down Expand Up @@ -403,7 +483,7 @@ ILInstruction RewrapStore(ILVariable target, StObj storeInstruction, IType 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(
BinaryNumericOperator.Add,
Expand Down Expand Up @@ -698,12 +778,8 @@ bool MatchInitializeArrayCall(ILInstruction instruction, out ILInstruction array
IMethod method = call.Method;
if (!method.IsStatic || method.Name != "InitializeArray" || method.DeclaringTypeDefinition == null)
return false;
var declaringType = method.DeclaringTypeDefinition;
if (declaringType.DeclaringType != null || declaringType.Name != "RuntimeHelpers"
|| declaringType.Namespace != "System.Runtime.CompilerServices")
{
if (!IsRuntimeHelpers(method.DeclaringType))
return false;
}
array = call.Arguments[0];
if (!call.Arguments[1].MatchLdMemberToken(out var member))
return false;
Expand Down

0 comments on commit dab256c

Please sign in to comment.