From a9357c4ed12d314da66ff9cd33bbf3c1e48a483e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 5 Nov 2026 15:10:22 -0800 Subject: [PATCH] Skip tracking stripped ReadyToRun inlining metadata Avoid inlinee hash-set tracking, nested-inline state, sorted arrays, and metadata-only module registration when --strip-inlining-info is set. Preserve callback fixups, module activation, and cross-module IL handling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dd6ed1f7-f085-4a84-9769-9cc1fab81e69 --- .../tools/Common/JitInterface/CorInfoImpl.cs | 23 ++++---- .../TestCases/R2RTestSuites.cs | 57 ++++++++++++++----- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 37 +++++++----- 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 254e0f432b6fd5..e0fc473ff24edb 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -564,18 +564,21 @@ private void PublishCode() _methodCodeNode.InitializeDebugLocInfos(_debugLocInfos); _methodCodeNode.InitializeDebugVarInfos(_debugVarInfos); #if READYTORUN - MethodDesc[] inlineeArray; - if (_inlinedMethods != null) + if (!_compilation.NodeFactory.OptimizationFlags.StripInliningInfo) { - inlineeArray = new MethodDesc[_inlinedMethods.Count]; - _inlinedMethods.CopyTo(inlineeArray); - Array.Sort(inlineeArray, TypeSystemComparer.Instance.Compare); - } - else - { - inlineeArray = Array.Empty(); + MethodDesc[] inlineeArray; + if (_inlinedMethods != null) + { + inlineeArray = new MethodDesc[_inlinedMethods.Count]; + _inlinedMethods.CopyTo(inlineeArray); + Array.Sort(inlineeArray, TypeSystemComparer.Instance.Compare); + } + else + { + inlineeArray = Array.Empty(); + } + _methodCodeNode.InitializeInliningInfo(inlineeArray, _compilation.NodeFactory); } - _methodCodeNode.InitializeInliningInfo(inlineeArray, _compilation.NodeFactory); if (RequiresInstructionSetSupportFixup()) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs index 10ef008d90c8f8..b88d88f67cc8f8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs @@ -505,8 +505,10 @@ static void Validate(ReadyToRunReader reader) } } - [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))] - public void BasicCrossModuleInlining() + [ConditionalTheory(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))] + [InlineData(false)] + [InlineData(true)] + public void BasicCrossModuleInlining(bool stripInliningInfo) { var inlineableLib = new CompiledAssembly { @@ -525,16 +527,26 @@ public void BasicCrossModuleInlining() new R2RTestRunner(_output).Run(new R2RTestCase( nameof(BasicCrossModuleInlining), - [new CrossgenCompilation(basicCrossModuleInlining.AssemblyName, [cgInlineableLib, cgBasicCrossModuleInlining]) { Validate = Validate }]) + [new CrossgenCompilation(basicCrossModuleInlining.AssemblyName, [cgInlineableLib, cgBasicCrossModuleInlining]) + { + AdditionalArgs = stripInliningInfo ? ["--strip-inlining-info"] : [], + Validate = Validate, + }]) ); - static void Validate(ReadyToRunReader reader) + void Validate(ReadyToRunReader reader) { string diag; Assert.True(R2RAssert.HasManifestRef(reader, "InlineableLib", out diag), diag); - Assert.True(R2RAssert.HasCrossModuleInlinedMethod(reader, "TestGetValue", "GetValue", out diag), diag); - Assert.True(R2RAssert.HasCrossModuleInlinedMethod(reader, "TestGetString", "GetString", out diag), diag); - Assert.True(R2RAssert.HasCrossModuleInliningInfo(reader, out diag), diag); + Assert.True(R2RAssert.HasFixupKindOnMethod(reader, ReadyToRunFixupKind.Check_IL_Body, "TestGetValue", out diag), diag); + Assert.True(R2RAssert.HasFixupKindOnMethod(reader, ReadyToRunFixupKind.Check_IL_Body, "TestGetString", out diag), diag); + Assert.Equal(!stripInliningInfo, reader.ReadyToRunHeader.Sections.ContainsKey(ReadyToRunSectionType.CrossModuleInlineInfo)); + if (!stripInliningInfo) + { + Assert.True(R2RAssert.HasCrossModuleInlinedMethod(reader, "TestGetValue", "GetValue", out diag), diag); + Assert.True(R2RAssert.HasCrossModuleInlinedMethod(reader, "TestGetString", "GetString", out diag), diag); + Assert.True(R2RAssert.HasCrossModuleInliningInfo(reader, out diag), diag); + } } } @@ -1681,8 +1693,10 @@ static void Validate(ReadyToRunReader reader) } } - [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))] - public void TransitiveReferences() + [ConditionalTheory(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))] + [InlineData(false)] + [InlineData(true)] + public void TransitiveReferences(bool stripInliningInfo) { var externalLib = new CompiledAssembly() { @@ -1713,12 +1727,18 @@ public void TransitiveReferences() }, ]) { + AdditionalArgs = stripInliningInfo ? ["--strip-inlining-info"] : [], Validate = reader => { string diag; Assert.True(R2RAssert.HasManifestRef(reader, "InlineableLibTransitive", out diag), diag); Assert.True(R2RAssert.HasManifestRef(reader, "ExternalLib", out diag), diag); - Assert.True(R2RAssert.HasCrossModuleInlinedMethod(reader, "TestTransitiveValue", "GetExternalValue", out diag), diag); + Assert.True(R2RAssert.HasFixupKindOnMethod(reader, ReadyToRunFixupKind.Check_IL_Body, "TestTransitiveValue", out diag), diag); + Assert.Equal(!stripInliningInfo, reader.ReadyToRunHeader.Sections.ContainsKey(ReadyToRunSectionType.CrossModuleInlineInfo)); + if (!stripInliningInfo) + { + Assert.True(R2RAssert.HasCrossModuleInlinedMethod(reader, "TestTransitiveValue", "GetExternalValue", out diag), diag); + } }, }, ])); @@ -2391,8 +2411,10 @@ static void Validate(ReadyToRunReader reader) /// Validates that inlining info (CrossModuleInlineInfo or InliningInfo2) is /// properly populated (CompositeBasic only validates ManifestRef). /// - [ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))] - public void CompositeCrossModuleInlining() + [ConditionalTheory(typeof(TestPaths), nameof(TestPaths.IsNotWasmTarget))] + [InlineData(false)] + [InlineData(true)] + public void CompositeCrossModuleInlining(bool stripInliningInfo) { var inlineableLib = new CompiledAssembly { @@ -2416,15 +2438,22 @@ public void CompositeCrossModuleInlining() ]) { Options = [Crossgen2Option.Composite, Crossgen2Option.Optimize], + AdditionalArgs = stripInliningInfo ? ["--strip-inlining-info"] : [], Validate = Validate, }, ])); - static void Validate(ReadyToRunReader reader) + void Validate(ReadyToRunReader reader) { string diag; Assert.True(R2RAssert.HasManifestRef(reader, "InlineableLib", out diag), diag); - Assert.True(R2RAssert.HasInlinedMethod(reader, "TestGetValue", "GetValue", out diag), diag); + Assert.NotEmpty(reader.ReadyToRunAssemblyHeaders); + Assert.All(reader.ReadyToRunAssemblyHeaders, header => + Assert.Equal(!stripInliningInfo, header.Sections.ContainsKey(ReadyToRunSectionType.InliningInfo2))); + if (!stripInliningInfo) + { + Assert.True(R2RAssert.HasInlinedMethod(reader, "TestGetValue", "GetValue", out diag), diag); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 6b593c675a5d18..72fd8765a4d171 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -3751,8 +3751,11 @@ private void beginInlining(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_METHOD_ST { _stashedPrecodeFixups.Push(_precodeFixups); _precodeFixups = null; - _stashedInlinedMethods.Push(_inlinedMethods); - _inlinedMethods = null; + if (!_compilation.NodeFactory.OptimizationFlags.StripInliningInfo) + { + _stashedInlinedMethods.Push(_inlinedMethods); + _inlinedMethods = null; + } } private void reportInliningDecision(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_METHOD_STRUCT_* inlineeHnd, CorInfoInline inlineResult, byte* reason) @@ -3787,20 +3790,21 @@ inlineeModule is not null && classMustBeLoadedBeforeCodeIsRun(inlineeModule.GetGlobalModuleType()); } - // If during inlining we found new inlinees, then if the inline was successful, add them to the set of fixups - // for the entire method. - HashSet previouslyStashedInlinees = _stashedInlinedMethods.Pop(); - if (_inlinedMethods != null) + if (!_compilation.NodeFactory.OptimizationFlags.StripInliningInfo) { - previouslyStashedInlinees = previouslyStashedInlinees ?? new HashSet(); - foreach (var inlineeInHashSet in _inlinedMethods) - previouslyStashedInlinees.Add(inlineeInHashSet); - } - _inlinedMethods = previouslyStashedInlinees; + // Propagate nested inlinees only when the enclosing inline succeeds. + HashSet previouslyStashedInlinees = _stashedInlinedMethods.Pop(); + if (_inlinedMethods != null) + { + previouslyStashedInlinees = previouslyStashedInlinees ?? new HashSet(); + foreach (var inlineeInHashSet in _inlinedMethods) + previouslyStashedInlinees.Add(inlineeInHashSet); + } + _inlinedMethods = previouslyStashedInlinees; - // Then add the fact we inlined this method. - _inlinedMethods = _inlinedMethods ?? new HashSet(); - _inlinedMethods.Add(inlinee); + _inlinedMethods = _inlinedMethods ?? new HashSet(); + _inlinedMethods.Add(inlinee); + } var typicalMethod = inlinee.GetTypicalMethodDefinition(); @@ -3852,7 +3856,10 @@ inlineeModule is not null && { // If we didn't succeed on the inline, just pop back to the state before inlining _precodeFixups = _stashedPrecodeFixups.Pop(); - _inlinedMethods = _stashedInlinedMethods.Pop(); + if (!_compilation.NodeFactory.OptimizationFlags.StripInliningInfo) + { + _inlinedMethods = _stashedInlinedMethods.Pop(); + } } }