diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/MIbcProfileParserTests.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/MIbcProfileParserTests.cs new file mode 100644 index 00000000000000..43b15aebb15ede --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/MIbcProfileParserTests.cs @@ -0,0 +1,128 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; + +using ILCompiler.ReadyToRun.Tests.TestCasesRunner; + +using Xunit; +using Xunit.Abstractions; + +namespace ILCompiler.ReadyToRun.Tests; + +public class MIbcProfileParserTests +{ + private const string AssemblyName = "MibcMethods"; + private readonly ITestOutputHelper _output; + + public MIbcProfileParserTests(ITestOutputHelper output) + { + _output = output; + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + public void PartialCompilationAfterUnresolvedMibcMethod(bool includeMissingMethod, bool composite) + { + string profilePath = Path.GetTempFileName(); + try + { + WriteMibc(profilePath, includeMissingMethod); + var assembly = new CompiledAssembly + { + AssemblyName = AssemblyName, + SourceResourceNames = ["Mibc/Methods.cs"], + }; + + new R2RTestRunner(_output).Run(new R2RTestCase( + nameof(PartialCompilationAfterUnresolvedMibcMethod), + [ + new(AssemblyName, [new CrossgenAssembly(assembly)]) + { + Options = composite ? [Crossgen2Option.Composite] : [], + AdditionalArgs = ["--partial", "--mibc", profilePath], + OutputFileExtension = TestPaths.IsWasmTarget ? ".wasm" : ".dll", + Validate = reader => + { + Assert.True(R2RAssert.HasCompiledMethod(reader, "ProfiledMethods", "AfterMissing", out string diag), diag); + Assert.True(R2RAssert.HasCompiledMethod(reader, "ProfiledMethods", "Control", out diag), diag); + Assert.False(R2RAssert.HasCompiledMethod(reader, "ProfiledMethods", "NotInProfile", out diag), diag); + }, + }, + ])); + } + finally + { + File.Delete(profilePath); + } + } + + private static void WriteMibc(string path, bool includeMissingMethod) + { + var metadata = new MetadataBuilder(); + metadata.AddModule(0, metadata.GetOrAddString("Profile.mibc"), default, default, default); + metadata.AddAssembly(metadata.GetOrAddString("Profile"), new Version(1, 0, 0, 0), default, default, default, default); + metadata.AddTypeDefinition( + TypeAttributes.NotPublic, default, metadata.GetOrAddString(""), default, + MetadataTokens.FieldDefinitionHandle(1), MetadataTokens.MethodDefinitionHandle(1)); + + AssemblyReferenceHandle assembly = metadata.AddAssemblyReference( + metadata.GetOrAddString(AssemblyName), new Version(0, 0, 0, 0), default, default, default, default); + TypeReferenceHandle type = metadata.AddTypeReference(assembly, default, metadata.GetOrAddString("ProfiledMethods")); + var methodSignature = new BlobBuilder(); + new BlobEncoder(methodSignature).MethodSignature().Parameters( + 0, returnType => returnType.Type().Int32(), parameters => { }); + var globalSignature = new BlobBuilder(); + new BlobEncoder(globalSignature).MethodSignature().Parameters( + 0, returnType => returnType.Void(), parameters => { }); + var methodBodies = new MethodBodyStreamEncoder(new BlobBuilder()); + + var groupBody = new InstructionEncoder(new BlobBuilder()); + if (includeMissingMethod) + { + EmitRecord("Missing"); + } + EmitRecord("AfterMissing"); + EmitRecord("Control"); + MethodDefinitionHandle group = AddMethod("Group", groupBody); + + var dictionaryBody = new InstructionEncoder(new BlobBuilder()); + dictionaryBody.LoadString(metadata.GetOrAddUserString(AssemblyName + ";")); + dictionaryBody.OpCode(ILOpCode.Ldtoken); + dictionaryBody.Token(group); + dictionaryBody.OpCode(ILOpCode.Pop); + AddMethod("AssemblyDictionary", dictionaryBody); + + var image = new BlobBuilder(); + new ManagedPEBuilder( + PEHeaderBuilder.CreateLibraryHeader(), new MetadataRootBuilder(metadata), methodBodies.Builder).Serialize(image); + using FileStream stream = File.Create(path); + image.WriteContentTo(stream); + + void EmitRecord(string methodName) + { + MemberReferenceHandle method = metadata.AddMemberReference( + type, metadata.GetOrAddString(methodName), metadata.GetOrAddBlob(methodSignature)); + groupBody.OpCode(ILOpCode.Ldtoken); + groupBody.Token(method); + groupBody.OpCode(ILOpCode.Pop); + } + + MethodDefinitionHandle AddMethod(string name, InstructionEncoder body) + { + body.OpCode(ILOpCode.Ret); + return metadata.AddMethodDefinition( + MethodAttributes.Public | MethodAttributes.Static, MethodImplAttributes.IL, + metadata.GetOrAddString(name), metadata.GetOrAddBlob(globalSignature), + methodBodies.AddMethodBody(body), MetadataTokens.ParameterHandle(1)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Mibc/Methods.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Mibc/Methods.cs new file mode 100644 index 00000000000000..68b56784d519d3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Mibc/Methods.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +public static class ProfiledMethods +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static int AfterMissing() => 1; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Control() => 2; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int NotInProfile() => 3; +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs index 4fdb38bc20efbf..66d5d6332489bd 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs @@ -208,6 +208,7 @@ public void Run(R2RTestCase testCase) // Step 2: Run each crossgen2 compilation and validate var driver = new R2RDriver(_output); List refPaths = BuildReferencePaths(); + refPaths.AddRange(assemblyPaths.Values); foreach(var compilation in testCase.Compilations) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs index a16b10060b4c54..9d711e42b6806b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs @@ -557,12 +557,12 @@ private static List ReadMIbcGroup(EcmaMethod method) // If the method being loaded didn't have meaningful input, skip methodProfileData.Add(new MethodProfileData((MethodDesc)methodInProgress, MethodProfilingDataFlags.ReadMethodCode, exclusiveWeight, weights, 0xFFFFFFFF, pgoSchemaData)); } - state = MibcGroupParseState.LookingForNextMethod; - exclusiveWeight = 0; - weights = null; - instrumentationDataLongs = null; - pgoSchemaData = null; } + state = MibcGroupParseState.LookingForNextMethod; + exclusiveWeight = 0; + weights = null; + instrumentationDataLongs = null; + pgoSchemaData = null; methodInProgress = null; break; default: