Repository: dotnet/runtime
File: src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs (C#)

Code before:
                    uint typeArgCount = decoder.ReadUInt();
                    methodTypeArgs = new string[typeArgCount];
                    for (int typeArgIndex = 0; typeArgIndex < typeArgCount; typeArgIndex++)
                    {
                        methodTypeArgs[typeArgIndex] = decoder.ReadTypeSignatureNoEmit();
                    }
                }

                string constrainedType = null;
                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_Constrained) != 0)
                {
                    constrainedType = decoder.ReadTypeSignatureNoEmit();
                }

                List<string> signaturePrefixes = [];
                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_AsyncVariant) != 0)
                {
                    signaturePrefixes.Add("[Async]");
                }
                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0)
                {
                    throw new NotImplementedException("Crossgen2 should not emit code for Instantiating stubs.");
                }
                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0)
                {
                    throw new NotImplementedException("Crossgen2 should not emit code for Unboxing stubs.");
                }

                GetPgoOffsetAndVersion(decoder.Offset, out int pgoFormatVersion, out int pgoOffset);

                PgoInfoKey key = new PgoInfoKey(mdReader, owningType, methodHandle, methodTypeArgs, signaturePrefixes.ToArray());
                PgoInfo info = new PgoInfo(key, this, pgoFormatVersion, Image, pgoOffset);

                // Since we do non-assembly qualified name based matching for generic instantiations, we can have conflicts.
                // This is rare, so the current strategy is to just ignore them. This will allow the tooling to work, although
                // the text output for PGO will be slightly wrong.
                if (!_pgoInfos.ContainsKey(key))
                    _pgoInfos.Add(key, info);
                curParser = allEntriesEnum.GetNext();
            }

            return;

            // Broken out into helper function in case we ever implement pgo data that isn't tied to a signature
            void GetPgoOffsetAndVersion(int offset, out int version, out int pgoDataOffset)
            {
                version = 0;
                // get the id of the entry point runtime function from the MethodEntryPoints NativeArray
                uint versionAndFlags = 0; // the RUNTIME_FUNCTIONS index
                offset = (int)ImageReader.DecodeUnsigned((uint)offset, ref versionAndFlags);


Review comment (bug):
Similarly to the instance method entrypoints path, throwing `NotImplementedException` here for `READYTORUN_METHOD_SIG_InstantiatingStub` and `READYTORUN_METHOD_SIG_UnboxingStub` will cause PGO parsing to abort entirely when encountering older ReadyToRun images with such stubs, even though their instrumentation data could still be processed. To avoid regressing analysis of existing binaries, it would be better to either support these flags (with corresponding signature prefixes) or ignore them during PGO parsing instead of failing.

Code after:
            // Broken out into helper function in case we ever implement pgo data that isn't tied to a signature
            void GetPgoOffsetAndVersion(int offset, out int version, out int pgoDataOffset)
            {
                version = 0;
                // get the id of the entry point runtime function from the MethodEntryPoints NativeArray
                uint versionAndFlags = 0; // the RUNTIME_FUNCTIONS index
                offset = (int)ImageReader.DecodeUnsigned((uint)offset, ref versionAndFlags);

                switch (versionAndFlags & 3)
                {
                    case 3:
                        uint val = 0;
                        ImageReader.DecodeUnsigned((uint)offset, ref val);
                        offset -= (int)val;
                        break;
                    case 1:
                        // Offset already correct
                        break;
                    default:
                        throw new Exception("Invalid Pgo format");
                }

                version = (int)(versionAndFlags >> 2);
                pgoDataOffset = offset;
            }
        }

        private void CountRuntimeFunctions(bool[] isEntryPoint, IDictionary<int, int[]> dHotColdMap, int firstColdRuntimeFunction)
        {
            foreach (ReadyToRunMethod method in Methods)
            {
                int runtimeFunctionId = method.EntryPointRuntimeFunctionId;
                if (runtimeFunctionId == -1)
                {
                    continue;
                }
                int count = 0;
                int i = runtimeFunctionId;
                do
                {
                    count++;
                    i++;
                } while (i < isEntryPoint.Length && !isEntryPoint[i] && i < firstColdRuntimeFunction);

                if (dHotColdMap.ContainsKey(runtimeFunctionId))
                {
                    int coldSize = dHotColdMap[runtimeFunctionId].Length;
                    method.ColdRuntimeFunctionId = dHotColdMap[runtimeFunctionId][0];
                    method.RuntimeFunctionCount = count + coldSize;
                    method.ColdRuntimeFunctionCount = coldSize;
                }