From 58edd4f38f795cf392451e01dacba5f4df693110 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 6 Jul 2026 09:44:54 +0200 Subject: [PATCH] Extract localloc initializer prefix and store offset matching HandleSequentialLocAllocInitializer mixed three concerns in one method body: consuming an optional initblk/cpblk prefix, matching the offset of each sequential store, and assembling the values array. Moving the first two into TryHandleLocAllocInitializerPrefix and TryGetSequentialStoreOffset reduces the method to the scan loop itself. Behavior-preserving; the break-vs-abort distinction of the offset matcher is kept via an out flag. --- .../Transforms/TransformArrayInitializers.cs | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index 3ee94313e..b2d3d640b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -428,28 +428,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms BlobReader blob = default; - if (lengthInstruction.MatchLdcI(out long byteCount)) + if (lengthInstruction.MatchLdcI(out long byteCount) + && !TryHandleLocAllocInitializerPrefix(block, ref pos, store, byteCount, ref blob, ref instructionsToRemove)) { - 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++; - } + return false; } for (int i = pos; i < block.Instructions.Count; i++) @@ -480,22 +462,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms { break; } - // 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 (!TryGetSequentialStoreOffset(target, store, elementType, minExpectedOffset, out var offset, out var abortTransform)) { - if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) + if (abortTransform) return false; - if (!left.MatchLdLoc(store)) - break; - var offsetInst = PointerArithmeticOffset.Detect(right, elementType, ((BinaryNumericInstruction)target).CheckForOverflow); - if (offsetInst == null) - return false; - if (!offsetInst.MatchLdcI(out long offset) || offset < 0 || offset < minExpectedOffset) - break; - minExpectedOffset = offset; + break; } + minExpectedOffset = offset; if (values == null) { var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, elementType, checkForOverflow: true); @@ -526,6 +499,66 @@ namespace ICSharpCode.Decompiler.IL.Transforms return elementCount <= values.Length; } + bool TryHandleLocAllocInitializerPrefix(Block block, ref int pos, ILVariable store, long byteCount, ref BlobReader blob, ref int instructionsToRemove) + { + // initblk(ldloc store, value, byteCount) + if (block.Instructions[pos].MatchInitblk(out var dest, out _, out var size)) + { + if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount)) + return false; + instructionsToRemove++; + pos++; + return true; + } + + // cpblk(ldloc store, ldsflda/call get_Item(CreateSpan(field)) data, byteCount) + 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++; + } + + return true; + } + + static bool TryGetSequentialStoreOffset(ILInstruction target, ILVariable store, IType elementType, long minExpectedOffset, out long offset, out bool abortTransform) + { + offset = 0; + abortTransform = false; + + // stobj T(ldloc store, value) writes at the current expected offset, initially 0. + if (target.MatchLdLoc(store)) + { + offset = minExpectedOffset; + return true; + } + + // stobj T(binary.add(ldloc store, offset), value) + // The offset is either sizeof(T) or an element index multiplied by sizeof(T). + if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) + { + abortTransform = true; + return false; + } + if (!left.MatchLdLoc(store)) + return false; + var offsetInst = PointerArithmeticOffset.Detect(right, elementType, ((BinaryNumericInstruction)target).CheckForOverflow); + if (offsetInst == null) + { + abortTransform = true; + return false; + } + return offsetInst.MatchLdcI(out offset) && offset >= 0 && offset >= minExpectedOffset; + } + ILInstruction RewrapStore(ILVariable target, StObj storeInstruction, IType type) { ILInstruction targetInst;