diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index 7fcc1a9c3b4476..ffb7f9e789fd3a 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -13,6 +13,9 @@ namespace ILCompiler.DependencyAnalysis public abstract partial class SortableDependencyNode : DependencyNodeCore, ISortableNode { #if !SUPPORT_JIT + // Custom sort order. Used to override the default sorting mechanics. + public int CustomSort = int.MaxValue; + /// /// Allows grouping of instances such that all nodes in a lower phase /// will be ordered before nodes in a later phase. @@ -148,8 +151,6 @@ public int Compare(DependencyNodeCore x1, DependencyNodeCore> ApplyProfilerGuidedMethod foreach (var methodNode in sortedMethodsList) { methodNode.CustomSort = sortOrder; +#if READYTORUN MethodColdCodeNode methodColdCodeNode = methodNode.ColdCodeNode; if (methodColdCodeNode != null) { methodColdCodeNode.CustomSort = sortOrder + sortedMethodsList.Count; } +#endif sortOrder++; } @@ -164,9 +174,11 @@ int ComputeHotWarmColdRegion(MethodWithGCInfo method) }; break; +#if READYTORUN case ReadyToRunMethodLayoutAlgorithm.CallFrequency: methods = MethodCallFrequencySort(methods); break; +#endif case ReadyToRunMethodLayoutAlgorithm.PettisHansen: methods = PettisHansenSort(methods); @@ -216,6 +228,7 @@ public CallerCalleeCount(MethodDesc caller, MethodDesc callee, int count) } } +#if READYTORUN /// /// Use callchain profile information to generate method ordering. We place /// callers and callees by traversing the caller-callee pairs in the callchain @@ -268,6 +281,7 @@ private List MethodCallFrequencySort(List me Debug.Assert(outputMethods.Count == methodsToPlace.Count); return outputMethods; } +#endif /// /// Sort methods with Pettis-Hansen using call graph data from profile. @@ -303,7 +317,9 @@ private List PettisHansenSort(List methodsTo if (!any) { +#if READYTORUN _logger.Writer.WriteLine("Warning: no call graph data was found or a .mibc file was not specified. Skipping Pettis Hansen method ordering."); +#endif return methodsToPlace; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 25b03db8ad8246..a3478bf14ea5ae 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -242,7 +242,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs index de1d6490de306e..fb18f7d4dfa7d5 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -25,6 +25,7 @@ public sealed class RyuJitCompilation : Compilation private readonly ConditionalWeakTable _corinfos = new ConditionalWeakTable(); internal readonly RyuJitCompilationOptions _compilationOptions; private readonly ProfileDataManager _profileDataManager; + private readonly ReadyToRunFileLayoutOptimizer _fileLayoutOptimizer; private readonly MethodImportationErrorProvider _methodImportationErrorProvider; private readonly ReadOnlyFieldPolicy _readOnlyFieldPolicy; private readonly int _parallelism; @@ -44,6 +45,8 @@ internal RyuJitCompilation( MethodImportationErrorProvider errorProvider, ReadOnlyFieldPolicy readOnlyFieldPolicy, RyuJitCompilationOptions options, + ReadyToRunMethodLayoutAlgorithm methodLayoutAlgorithm, + ReadyToRunFileLayoutAlgorithm fileLayoutAlgorithm, int parallelism) : base(dependencyGraph, nodeFactory, roots, ilProvider, debugInformationProvider, inliningPolicy, logger) { @@ -57,6 +60,8 @@ internal RyuJitCompilation( _readOnlyFieldPolicy = readOnlyFieldPolicy; _parallelism = parallelism; + + _fileLayoutOptimizer = new ReadyToRunFileLayoutOptimizer(logger, methodLayoutAlgorithm, fileLayoutAlgorithm, profileDataManager, nodeFactory); } public ProfileDataManager ProfileData => _profileDataManager; @@ -89,6 +94,8 @@ protected override void CompileInternal(string outputFile, ObjectDumper dumper) _dependencyGraph.ComputeMarkedNodes(); var nodes = _dependencyGraph.MarkedNodeList; + nodes = _fileLayoutOptimizer.ApplyProfilerGuidedMethodSort(nodes); + NodeFactory.SetMarkingComplete(); ObjectWritingOptions options = default; diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs index 6456205cb795c0..b73b784d72240e 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs @@ -17,6 +17,8 @@ public sealed class RyuJitCompilationBuilder : CompilationBuilder // These need to provide reasonable defaults so that the user can optionally skip // calling the Use/Configure methods and still get something reasonable back. private KeyValuePair[] _ryujitOptions = Array.Empty>(); + private ReadyToRunMethodLayoutAlgorithm _methodLayoutAlgorithm; + private ReadyToRunFileLayoutAlgorithm _fileLayoutAlgorithm; private ILProvider _ilProvider = new NativeAotILProvider(); private ProfileDataManager _profileDataManager; private string _jitPath; @@ -39,6 +41,13 @@ public RyuJitCompilationBuilder UseJitPath(string jitPath) return this; } + public RyuJitCompilationBuilder FileLayoutAlgorithms(ReadyToRunMethodLayoutAlgorithm methodLayoutAlgorithm, ReadyToRunFileLayoutAlgorithm fileLayoutAlgorithm) + { + _methodLayoutAlgorithm = methodLayoutAlgorithm; + _fileLayoutAlgorithm = fileLayoutAlgorithm; + return this; + } + public override CompilationBuilder UseBackendOptions(IEnumerable options) { var builder = default(ArrayBuilder>); @@ -126,7 +135,21 @@ public override ICompilation ToCompilation() JitConfigProvider.Initialize(_context.Target, jitFlagBuilder.ToArray(), _ryujitOptions, _jitPath); DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(CompilerComparer.Instance)); - return new RyuJitCompilation(graph, factory, _compilationRoots, _ilProvider, _debugInformationProvider, _logger, _inliningPolicy ?? _compilationGroup, _instructionSetSupport, _profileDataManager, _methodImportationErrorProvider, _readOnlyFieldPolicy, options, _parallelism); + return new RyuJitCompilation(graph, + factory, + _compilationRoots, + _ilProvider, + _debugInformationProvider, + _logger, + _inliningPolicy ?? _compilationGroup, + _instructionSetSupport, + _profileDataManager, + _methodImportationErrorProvider, + _readOnlyFieldPolicy, + options, + _methodLayoutAlgorithm, + _fileLayoutAlgorithm, + _parallelism); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj index 4ba7b607e0bde2..b382de36f85d28 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj @@ -15,6 +15,7 @@ binaries are up to date and which are stale. --> false Debug;Release;Checked + false @@ -98,5 +99,9 @@ + + + + diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 60c2616d9f261c..42134d92f88ba9 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -27,6 +27,10 @@ internal sealed class ILCompilerRootCommand : RootCommand new("--optimize-time", "--Ot") { Description = "Enable optimizations, favor code speed" }; public Option MibcFilePaths { get; } = new("--mibc", "-m") { DefaultValueFactory = _ => Array.Empty(), Description = "Mibc file(s) for profile guided optimization" }; + public Option MethodLayout { get; } = + new("--method-layout") { CustomParser = MakeReadyToRunMethodLayoutAlgorithm, DefaultValueFactory = MakeReadyToRunMethodLayoutAlgorithm, Description = "Layout algorithm used by profile-driven optimization for arranging methods in a file.", HelpName = "arg" }; + public Option FileLayout { get; } = + new("--file-layout") { CustomParser = MakeReadyToRunFileLayoutAlgorithm, DefaultValueFactory = MakeReadyToRunFileLayoutAlgorithm, Description = "Layout algorithm used by profile-driven optimization for arranging non-method contents in a file.", HelpName = "arg" }; public Option SatelliteFilePaths { get; } = new("--satellite") { DefaultValueFactory = _ => Array.Empty(), Description = "Satellite assemblies associated with inputs/references" }; public Option EnableDebugInfo { get; } = @@ -187,6 +191,8 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") Options.Add(OptimizeSpace); Options.Add(OptimizeTime); Options.Add(MibcFilePaths); + Options.Add(MethodLayout); + Options.Add(FileLayout); Options.Add(SatelliteFilePaths); Options.Add(EnableDebugInfo); Options.Add(UseDwarf5); @@ -411,6 +417,36 @@ private static int MakeParallelism(ArgumentResult result) return parallelism; } + private static ReadyToRunMethodLayoutAlgorithm MakeReadyToRunMethodLayoutAlgorithm(ArgumentResult result) + { + if (result.Tokens.Count == 0) + return ReadyToRunMethodLayoutAlgorithm.DefaultSort; + + return result.Tokens[0].Value.ToLowerInvariant() switch + { + "defaultsort" => ReadyToRunMethodLayoutAlgorithm.DefaultSort, + "exclusiveweight" => ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight, + "hotcold" => ReadyToRunMethodLayoutAlgorithm.HotCold, + "hotwarmcold" => ReadyToRunMethodLayoutAlgorithm.HotWarmCold, + "pettishansen" => ReadyToRunMethodLayoutAlgorithm.PettisHansen, + "random" => ReadyToRunMethodLayoutAlgorithm.Random, + _ => throw new CommandLineException(result.Tokens[0].Value) + }; + } + + private static ReadyToRunFileLayoutAlgorithm MakeReadyToRunFileLayoutAlgorithm(ArgumentResult result) + { + if (result.Tokens.Count == 0) + return ReadyToRunFileLayoutAlgorithm.DefaultSort; + + return result.Tokens[0].Value.ToLowerInvariant() switch + { + "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort, + "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder, + _ => throw new CommandLineException(result.Tokens[0].Value) + }; + } + #if DEBUG private static bool DumpReproArguments(CodeGenerationFailedException ex) { diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 0420a555eb3c5f..c8ec8eda8e8c27 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -348,10 +348,10 @@ public int Run() // Compile // - var builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); - string compilationUnitPrefix = multiFile ? Path.GetFileNameWithoutExtension(outputFilePath) : ""; - builder.UseCompilationUnitPrefix(compilationUnitPrefix); + var builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup) + .FileLayoutAlgorithms(Get(_command.MethodLayout), Get(_command.FileLayout)) + .UseCompilationUnitPrefix(compilationUnitPrefix); string[] mibcFilePaths = Get(_command.MibcFilePaths); if (mibcFilePaths.Length > 0)