From a9a79d51f21fea4f001b3d8b5154a9d509f6ce0b Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Wed, 13 Jul 2022 05:21:59 +0300 Subject: [PATCH 01/12] Switch ilc to use System.CommandLine --- .../tools/Common/CommandLineHelpers.cs | 286 +++ .../Compiler/UsageBasedMetadataManager.cs | 2 + .../tools/aot/ILCompiler/ILCompiler.props | 21 +- .../aot/ILCompiler/ILCompilerRootCommand.cs | 363 ++++ src/coreclr/tools/aot/ILCompiler/Program.cs | 1501 ++++++--------- src/coreclr/tools/aot/crossgen2/Program.cs | 1623 +++++++---------- 6 files changed, 1918 insertions(+), 1878 deletions(-) create mode 100644 src/coreclr/tools/Common/CommandLineHelpers.cs create mode 100644 src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs new file mode 100644 index 0000000000000..e6641eaa48213 --- /dev/null +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -0,0 +1,286 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.CommandLine.Binding; +using System.CommandLine.Parsing; +using System.IO; +using System.IO.Compression; + +using Internal.TypeSystem; + +namespace System.CommandLine; + +internal class CommandLineException : Exception +{ + public CommandLineException(string message) : base(message) { } +} + +// +// Helpers for command line processing +// +internal static class Helpers +{ + public const string DefaultSystemModule = "System.Private.CoreLib"; + + public static Dictionary BuildPathDictionay(IReadOnlyList tokens, bool strict) + { + Dictionary dictionary = new(StringComparer.OrdinalIgnoreCase); + + foreach (Token token in tokens) + { + AppendExpandedPaths(dictionary, token.Value, strict); + } + + return dictionary; + } + + private static TargetOS GetTargetOS(string token) + { + if(string.IsNullOrEmpty(token)) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return Internal.TypeSystem.TargetOS.Windows; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + return Internal.TypeSystem.TargetOS.Linux; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return Internal.TypeSystem.TargetOS.OSX; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + return Internal.TypeSystem.TargetOS.FreeBSD; + + throw new NotImplementedException(); + } + + if (token.Equals("windows", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetOS.Windows; + else if (token.Equals("linux", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetOS.Linux; + else if (token.Equals("osx", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetOS.OSX; + + throw new CommandLineException($"Target OS '{token}' is not supported"); + } + + private static TargetArchitecture GetTargetArchitecture(string token) + { + if(string.IsNullOrEmpty(token)) + { + return RuntimeInformation.ProcessArchitecture switch + { + Architecture.X86 => Internal.TypeSystem.TargetArchitecture.X86, + Architecture.X64 => Internal.TypeSystem.TargetArchitecture.X64, + Architecture.Arm => Internal.TypeSystem.TargetArchitecture.ARM, + Architecture.Arm64 => Internal.TypeSystem.TargetArchitecture.ARM64, + _ => throw new NotImplementedException() + }; + } + + if (token.Equals("x86", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.X86; + else if (token.Equals("x64", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.X64; + else if (token.Equals("arm", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.ARM; + else if (token.Equals("arm64", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.ARM64; + + throw new CommandLineException($"Target architecture '{token}' is not supported"); + } + + public static void MakeReproPackage(string makeReproPath, string outputFilePath, string[] args, ParseResult res, IEnumerable inputOptions) + { + Directory.CreateDirectory(makeReproPath); + + List details = new List(); + details.Add("Tool version"); + try + { + details.Add(Environment.GetCommandLineArgs()[0]); + } + catch { } + try + { + details.Add(System.Diagnostics.FileVersionInfo.GetVersionInfo(Environment.GetCommandLineArgs()[0]).ToString()); + } + catch { } + + details.Add("------------------------"); + details.Add("Actual Command Line Args"); + details.Add("------------------------"); + details.AddRange(args); + foreach (string arg in args) + { + if (arg.StartsWith('@')) + { + string rspFileName = arg.Substring(1); + details.Add("------------------------"); + details.Add(rspFileName); + details.Add("------------------------"); + try + { + details.AddRange(File.ReadAllLines(rspFileName)); + } + catch { } + } + } + + HashCode hashCodeOfArgs = new HashCode(); + foreach (string s in details) + hashCodeOfArgs.Add(s); + + string zipFileName = ((uint)hashCodeOfArgs.ToHashCode()).ToString(); + + if (outputFilePath != null) + zipFileName = zipFileName + "_" + Path.GetFileName(outputFilePath); + + zipFileName = Path.Combine(makeReproPath, Path.ChangeExtension(zipFileName, ".zip")); + + Console.WriteLine($"Creating {zipFileName}"); + using (var archive = ZipFile.Open(zipFileName, ZipArchiveMode.Create)) + { + ZipArchiveEntry commandEntry = archive.CreateEntry("command.txt"); + using (StreamWriter writer = new StreamWriter(commandEntry.Open())) + { + foreach (string s in details) + writer.WriteLine(s); + } + + HashSet inputOptionNames = new HashSet(inputOptions); + Dictionary inputToReproPackageFileName = new(); + + List rspFile = new List(); + foreach (var option in res.CommandResult.Command.Options) + { + if (!res.HasOption(option) || option.Name == "make-repro-path") + { + continue; + } + + IValueDescriptor descriptor = option; + object val = res.CommandResult.GetValueForOption(option); + if (val is not null && !(descriptor.HasDefaultValue && descriptor.GetDefaultValue().Equals(val))) + { + if (val is IEnumerable values) + { + if (inputOptionNames.Contains(option.Name)) + { + Dictionary dictionary = new(); + foreach (string optInList in values) + { + Helpers.AppendExpandedPaths(dictionary, optInList, false); + } + foreach (string inputFile in dictionary.Values) + { + rspFile.Add($"--{option.Name}:{ConvertFromInputPathToReproPackagePath(inputFile)}"); + } + } + else + { + foreach (string optInList in values) + { + rspFile.Add($"--{option.Name}:{optInList}"); + } + } + } + else + { + rspFile.Add($"--{option.Name}:{val}"); + } + } + } + + foreach (var argument in res.CommandResult.Command.Arguments) + { + object val = res.CommandResult.GetValueForArgument(argument); + if (val is IEnumerable values) + { + foreach (string optInList in values) + { + rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)optInList)}"); + } + } + else + { + rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)val)}"); + } + } + + ZipArchiveEntry rspEntry = archive.CreateEntry("repro.rsp"); + using (StreamWriter writer = new StreamWriter(rspEntry.Open())) + { + foreach (string s in rspFile) + writer.WriteLine(s); + } + + string ConvertFromInputPathToReproPackagePath(string inputPath) + { + if (inputToReproPackageFileName.TryGetValue(inputPath, out string reproPackagePath)) + { + return reproPackagePath; + } + + try + { + string inputFileDir = inputToReproPackageFileName.Count.ToString(); + reproPackagePath = Path.Combine(inputFileDir, Path.GetFileName(inputPath)); + archive.CreateEntryFromFile(inputPath, reproPackagePath); + inputToReproPackageFileName.Add(inputPath, reproPackagePath); + + return reproPackagePath; + } + catch + { + return inputPath; + } + } + } + } + + // Helper to create a collection of paths unique in their simple names. + private static void AppendExpandedPaths(Dictionary dictionary, string pattern, bool strict) + { + bool empty = true; + string directoryName = Path.GetDirectoryName(pattern); + string searchPattern = Path.GetFileName(pattern); + + if (directoryName == "") + directoryName = "."; + + if (Directory.Exists(directoryName)) + { + foreach (string fileName in Directory.EnumerateFiles(directoryName, searchPattern)) + { + string fullFileName = Path.GetFullPath(fileName); + + string simpleName = Path.GetFileNameWithoutExtension(fileName); + + if (dictionary.ContainsKey(simpleName)) + { + if (strict) + { + throw new CommandLineException("Multiple input files matching same simple name " + + fullFileName + " " + dictionary[simpleName]); + } + } + else + { + dictionary.Add(simpleName, fullFileName); + } + + empty = false; + } + } + + if (empty) + { + if (strict) + { + throw new CommandLineException("No files matching " + pattern); + } + else + { + Console.WriteLine("Warning: No files matching " + pattern); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 77af00708d864..84784511f0a2c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -72,6 +72,7 @@ public UsageBasedMetadataManager( Logger logger, IEnumerable> featureSwitchValues, IEnumerable rootEntireAssembliesModules, + IEnumerable additionalRootedAssemblies, IEnumerable trimmedAssemblies) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy) { @@ -84,6 +85,7 @@ public UsageBasedMetadataManager( _featureSwitchHashtable = new FeatureSwitchHashtable(new Dictionary(featureSwitchValues)); _rootEntireAssembliesModules = new HashSet(rootEntireAssembliesModules); + _rootEntireAssembliesModules.UnionWith(additionalRootedAssemblies); _trimmedAssemblies = new HashSet(trimmedAssemblies); } diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompiler.props b/src/coreclr/tools/aot/ILCompiler/ILCompiler.props index 8c4fce523906e..51569f7d1b445 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompiler.props +++ b/src/coreclr/tools/aot/ILCompiler/ILCompiler.props @@ -82,28 +82,11 @@ - - - - - - - - - - - - - - - + - - true - Internal.CommandLine.Strings - + diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs new file mode 100644 index 0000000000000..3e0df10675099 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -0,0 +1,363 @@ +// 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.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Help; +using System.CommandLine.Parsing; +using System.IO; +using System.Runtime.InteropServices; + +using Internal.TypeSystem; + +namespace ILCompiler; + +internal class ILCompilerRootCommand : RootCommand +{ + public Argument> InputFilePaths { get; } = + new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; + public Option> ReferenceFiles { get; } = + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), false, "Reference file(s) for compilation"); + public Option OutputFilePath { get; } = + new(new[] { "--out", "-o" }, "Output file path"); + public Option Optimize { get; } = + new(new[] { "--optimize", "-O" }, "Enable optimizations"); + public Option OptimizeSpace { get; } = + new(new[] { "--optimize-space", "-Os" }, "Enable optimizations, favor code space"); + public Option OptimizeTime { get; } = + new(new[] { "--optimize-time", "-Ot" }, "Enable optimizations, favor code speed"); + public Option MibcFilePaths { get; } = + new(new[] { "--mibc", "-m" }, () => Array.Empty(), "Mibc file(s) for profile guided optimization"); + public Option EnableDebugInfo { get; } = + new(new[] { "--debug", "-g" }, "Emit debugging information"); + public Option UseDwarf5 { get; } = + new(new[] { "--gdwarf-5" }, "Generate source-level debug information with dwarf version 5"); + public Option NativeLib { get; } = + new(new[] { "--nativelib" }, "Compile as static or shared library"); + public Option ExportsFile { get; } = + new(new[] { "--exportsfile" }, "File to write exported method definitions"); + public Option DgmlLogFileName { get; } = + new(new[] { "--dgmllog" }, "Save result of dependency analysis as DGML"); + public Option GenerateFullDgmlLog { get; } = + new(new[] { "--fulllog" }, "Save detailed log of dependency analysis"); + public Option ScanDgmlLogFileName { get; } = + new(new[] { "--scandgmllog" }, "Save result of scanner dependency analysis as DGML"); + public Option GenerateFullScanDgmlLog { get; } = + new(new[] { "--scanfulllog" }, "Save detailed log of scanner dependency analysis"); + public Option IsVerbose { get; } = + new(new[] { "--verbose" }, "Enable verbose logging"); + public Option SystemModuleName { get; } = + new(new[] { "--systemmodule" }, () => Helpers.DefaultSystemModule, "System module name (default: System.Private.CoreLib)"); + public Option MultiFile { get; } = + new(new[] { "--multifile" }, "Compile only input files (do not compile referenced assemblies)"); + public Option WaitForDebugger { get; } = + new(new[] { "--waitfordebugger" }, "Pause to give opportunity to attach debugger"); + public Option Resilient { get; } = + new(new[] { "--resilient" }, "Ignore unresolved types, methods, and assemblies. Defaults to false"); + public Option CodegenOptions { get; } = + new(new[] { "--codegenopt" }, () => Array.Empty(), "Define a codegen option"); + public Option RdXmlFilePaths { get; } = + new(new[] { "--rdxml" }, () => Array.Empty(), "RD.XML file(s) for compilation"); + public Option MapFileName { get; } = + new(new[] { "--map" }, "Generate a map file"); + public Option MetadataLogFileName { get; } = + new(new[] { "--metadatalog" }, "Generate a metadata log file"); + public Option NoMetadataBlocking { get; } = + new(new[] { "--nometadatablocking" }, "Ignore metadata blocking for internal implementation details"); + public Option CompleteTypesMetadata { get; } = + new(new[] { "--completetypemetadata" }, "Generate complete metadata for types"); + public Option ReflectionData { get; } = + new(new[] { "--reflectiondata" }, "Reflection data to generate (one of: all, none)"); + public Option ScanReflection { get; } = + new(new[] { "--scanreflection" }, "Scan IL for reflection patterns"); + public Option UseScanner { get; } = + new(new[] { "--scan" }, "Use IL scanner to generate optimized code (implied by -O)"); + public Option NoScanner { get; } = + new(new[] { "--noscan" }, "Do not use IL scanner to generate optimized code"); + public Option IlDump { get; } = + new(new[] { "--ildump" }, "Dump IL assembly listing for compiler-generated IL"); + public Option EmitStackTraceData { get; } = + new(new[] { "--stacktracedata" }, "Emit data to support generating stack trace strings at runtime"); + public Option MethodBodyFolding { get; } = + new(new[] { "--methodbodyfolding" }, "Fold identical method bodies"); + public Option InitAssemblies { get; } = + new(new[] { "--initassembly" }, () => Array.Empty(), "Assembly(ies) with a library initializer"); + public Option AppContextSwitches { get; } = + new(new[] { "--appcontextswitch" }, () => Array.Empty(), "System.AppContext switches to set (format: 'Key=Value')"); + public Option FeatureSwitches { get; } = + new(new[] { "--feature" }, () => Array.Empty(), "Feature switches to apply (format: 'Namespace.Name=[true|false]'"); + public Option RuntimeOptions { get; } = + new(new[] { "--runtimeopt" }, () => Array.Empty(), "Runtime options to set"); + public Option Parallelism { get; } = + new(new[] { "--parallelism" }, () => Environment.ProcessorCount, "Maximum number of threads to use during compilation"); + public Option InstructionSet { get; } = + new(new[] { "--instructionset" }, "Instruction set to allow or disallow"); + public Option Guard { get; } = + new(new[] { "--guard" }, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); + public Option PreinitStatics { get; } = + new(new[] { "--preinitstatics" }, "Interpret static constructors at compile time if possible (implied by -O)"); + public Option NoPreinitStatics { get; } = + new(new[] { "--nopreinitstatics" }, "Do not interpret static constructors at compile time"); + public Option SuppressedWarnings { get; } = + new(new[] { "--nowarn" }, () => Array.Empty(), "Disable specific warning messages"); + public Option SingleWarn { get; } = + new(new[] { "--singlewarn" }, "Generate single AOT/trimming warning per assembly"); + public Option SingleWarnEnabledAssemblies { get; } = + new(new[] { "--singlewarnassembly" }, () => Array.Empty(), "Generate single AOT/trimming warning for given assembly"); + public Option SingleWarnDisabledAssemblies { get; } = + new(new[] { "--nosinglewarnassembly" }, () => Array.Empty(), "Expand AOT/trimming warnings for given assembly"); + public Option DirectPInvokes { get; } = + new(new[] { "--directpinvoke" }, () => Array.Empty(), "PInvoke to call directly"); + public Option DirectPInvokeLists { get; } = + new(new[] { "--directpinvokelist" }, () => Array.Empty(), "File with list of PInvokes to call directly"); + public Option MaxGenericCycle { get; } = + new(new[] { "--maxgenericcycle" }, () => CompilerTypeSystemContext.DefaultGenericCycleCutoffPoint, "Max depth of generic cycle"); + public Option RootedAssemblies { get; } = + new(new[] { "--root" }, () => Array.Empty(), "Fully generate given assembly"); + public Option> ConditionallyRootedAssemblies { get; } = + new(new[] { "--conditionalroot" }, result => ILLinkify(result.Tokens, true), true, "Fully generate given assembly if it's used"); + public Option> TrimmedAssemblies { get; } = + new(new[] { "--trim" }, result => ILLinkify(result.Tokens, true), true, "Trim the specified assembly"); + public Option RootDefaultAssemblies { get; } = + new(new[] { "--defaultrooting" }, "Root assemblies that are not marked [IsTrimmable]"); + public Option TargetArchitecture { get; } = + new(new[] { "--targetarch" }, result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, "Target architecture for cross compilation"); + public Option TargetOS { get; } = + new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, "Target OS for cross compilation"); + public Option JitPath { get; } = + new(new[] { "--jitpath" }, "Path to JIT compiler library"); + public Option SingleMethodTypeName { get; } = + new(new[] { "--singlemethodtypename" }, "Single method compilation: assembly-qualified name of the owning type"); + public Option SingleMethodName { get; } = + new(new[] { "--singlemethodname" }, "Single method compilation: name of the method"); + public Option SingleMethodGenericArgs { get; } = + new(new[] { "--singlemethodgenericarg" }, "Single method compilation: generic arguments to the method"); + public Option MakeReproPath { get; } = + new(new[] { "--make-repro-path" }, "Path where to place a repro package"); + + public OptimizationMode OptimizationMode { get; private set; } + public ParseResult Result; + + public ILCompilerRootCommand(string[] args) : base("Microsoft (R) .NET Native IL Compiler") + { + AddArgument(InputFilePaths); + AddOption(ReferenceFiles); + AddOption(OutputFilePath); + AddOption(Optimize); + AddOption(OptimizeSpace); + AddOption(OptimizeTime); + AddOption(MibcFilePaths); + AddOption(EnableDebugInfo); + AddOption(UseDwarf5); + AddOption(NativeLib); + AddOption(ExportsFile); + AddOption(DgmlLogFileName); + AddOption(GenerateFullDgmlLog); + AddOption(ScanDgmlLogFileName); + AddOption(GenerateFullScanDgmlLog); + AddOption(IsVerbose); + AddOption(SystemModuleName); + AddOption(MultiFile); + AddOption(WaitForDebugger); + AddOption(Resilient); + AddOption(CodegenOptions); + AddOption(RdXmlFilePaths); + AddOption(MapFileName); + AddOption(MetadataLogFileName); + AddOption(NoMetadataBlocking); + AddOption(CompleteTypesMetadata); + AddOption(ReflectionData); + AddOption(ScanReflection); + AddOption(UseScanner); + AddOption(NoScanner); + AddOption(IlDump); + AddOption(EmitStackTraceData); + AddOption(MethodBodyFolding); + AddOption(InitAssemblies); + AddOption(AppContextSwitches); + AddOption(FeatureSwitches); + AddOption(RuntimeOptions); + AddOption(Parallelism); + AddOption(InstructionSet); + AddOption(Guard); + AddOption(PreinitStatics); + AddOption(NoPreinitStatics); + AddOption(SuppressedWarnings); + AddOption(SingleWarn); + AddOption(SingleWarnEnabledAssemblies); + AddOption(SingleWarnDisabledAssemblies); + AddOption(DirectPInvokes); + AddOption(DirectPInvokeLists); + AddOption(MaxGenericCycle); + AddOption(RootedAssemblies); + AddOption(ConditionallyRootedAssemblies); + AddOption(TrimmedAssemblies); + AddOption(RootDefaultAssemblies); + AddOption(TargetArchitecture); + AddOption(TargetOS); + AddOption(JitPath); + AddOption(SingleMethodTypeName); + AddOption(SingleMethodName); + AddOption(SingleMethodGenericArgs); + AddOption(MakeReproPath); + + this.SetHandler(context => + { + Result = context.ParseResult; + + if (context.ParseResult.GetValueForOption(OptimizeSpace)) + { + OptimizationMode = OptimizationMode.PreferSize; + } + else if (context.ParseResult.GetValueForOption(OptimizeTime)) + { + OptimizationMode = OptimizationMode.PreferSpeed; + } + else if (context.ParseResult.GetValueForOption(Optimize)) + { + OptimizationMode = OptimizationMode.Blended; + } + else + { + OptimizationMode = OptimizationMode.None; + } + + try + { + string makeReproPath = context.ParseResult.GetValueForOption(MakeReproPath); + if (makeReproPath != null) + { + // Create a repro package in the specified path + // This package will have the set of input files needed for compilation + // + the original command line arguments + // + a rsp file that should work to directly run out of the zip file + + Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, + context.ParseResult, new[] { "r", "reference", "m", "mibc", "rdxml", "directpinvokelist" }); + } + + context.ExitCode = new Program(this).Run(); + } +#if DEBUG + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } +#else + catch (Exception e) + { + context.Console.ResetColor(); + context.Console.ForegroundColor = ConsoleColor.Red; + + context.Console.Error.WriteLine("Error: " + e.Message); + context.Console.Error.WriteLine(e.ToString()); + + context.Console.ResetColor(); + + context.ExitCode = 1; + } +#endif + }); + } + + public static IEnumerable GetExtendedHelp(HelpContext _) + { + foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) + yield return sectionDelegate; + + yield return _ => + { + Console.WriteLine("Options may be passed on the command line, or via response file. On the command line switch values may be specified by passing " + + "the option followed by a space followed by the value of the option, or by specifying a : between option and switch value. A response file " + + "is specified by passing the @ symbol before the response file name. In a response file all options must be specified on their own lines, and " + + "only the : syntax for switches is supported.\n"); + + Console.WriteLine("Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are " + + "considered to be input files. If no input files begin with '--' then this option is not necessary.\n"); + + string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64" }; + string[] ValidOS = new string[] { "windows", "linux", "osx" }; + + Console.WriteLine("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetos", string.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant()); + + Console.WriteLine(String.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetarch", String.Join("', '", ValidArchitectures), Helpers.GetTargetArchitecture(null).ToString().ToLowerInvariant())); + + Console.WriteLine("The allowable values for the --instruction-set option are described in the table below. Each architecture has a different set of valid " + + "instruction sets, and multiple instruction sets may be specified by separating the instructions sets by a ','. For example 'avx2,bmi,lzcnt'"); + + foreach (string arch in ValidArchitectures) + { + Console.Write(arch); + Console.Write(": "); + + TargetArchitecture targetArch = Helpers.GetTargetArchitecture(arch); + bool first = true; + foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) + { + // Only instruction sets with are specifiable should be printed to the help text + if (instructionSet.Specifiable) + { + if (first) + { + first = false; + } + else + { + Console.Write(", "); + } + Console.Write(instructionSet.Name); + } + + Console.WriteLine(); + } + } + + Console.WriteLine(); + Console.WriteLine("The following CPU names are predefined groups of instruction sets and can be used in --instruction-set too:"); + Console.WriteLine(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); + }; + } + + private static IEnumerable ILLinkify(IReadOnlyList tokens, bool setDefaultToEmpty) + { + if (tokens.Count == 0) + { + yield return string.Empty; + yield break; + } + + foreach(Token token in tokens) + { + string rootedAssembly = token.Value; + + // For compatibility with IL Linker, the parameter could be a file name or an assembly name. + // This is the logic IL Linker uses to decide how to interpret the string. Really. + string simpleName; + if (File.Exists(rootedAssembly)) + simpleName = Path.GetFileNameWithoutExtension(rootedAssembly); + else + simpleName = rootedAssembly; + yield return simpleName; + } + } + +#if DEBUG + private static bool DumpReproArguments(CodeGenerationFailedException ex) + { + Console.WriteLine("To repro, add following arguments to the command line:"); + + MethodDesc failingMethod = ex.Method; + + var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)failingMethod.Context.SystemModule); + + Console.Write($"--singlemethodtypename \"{formatter.FormatName(failingMethod.OwningType, true)}\""); + Console.Write($" --singlemethodname {failingMethod.Name}"); + + for (int i = 0; i < failingMethod.Instantiation.Length; i++) + Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); + + return false; + } +#endif +} diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 115ca29863cd2..8031885709ff3 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -3,6 +3,9 @@ using System; using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Help; +using System.CommandLine.Parsing; using System.IO; using System.Reflection; using System.Runtime.InteropServices; @@ -12,1102 +15,714 @@ using Internal.TypeSystem; using Internal.TypeSystem.Ecma; -using Internal.CommandLine; - using Debug = System.Diagnostics.Debug; using InstructionSet = Internal.JitInterface.InstructionSet; -namespace ILCompiler -{ - internal class Program - { - private const string DefaultSystemModule = "System.Private.CoreLib"; - - private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _referenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); - - private string _outputFilePath; - private bool _isVerbose; - - private string _dgmlLogFileName; - private bool _generateFullDgmlLog; - private string _scanDgmlLogFileName; - private bool _generateFullScanDgmlLog; - - private TargetArchitecture _targetArchitecture; - private string _targetArchitectureStr; - private TargetOS _targetOS; - private string _targetOSStr; - private OptimizationMode _optimizationMode; - private bool _enableDebugInfo; - private string _ilDump; - private string _systemModuleName = DefaultSystemModule; - private bool _multiFile; - private bool _nativeLib; - private string _exportsFile; - private bool _useScanner; - private bool _noScanner; - private bool _preinitStatics; - private bool _noPreinitStatics; - private bool _emitStackTraceData; - private string _mapFileName; - private string _metadataLogFileName; - private bool _noMetadataBlocking; - private string _reflectionData; - private bool _completeTypesMetadata; - private bool _scanReflection; - private bool _methodBodyFolding; - private int _parallelism = Environment.ProcessorCount; - private string _instructionSet; - private string _guard; - private int _maxGenericCycle = CompilerTypeSystemContext.DefaultGenericCycleCutoffPoint; - private bool _useDwarf5; - private string _jitPath; - - private string _singleMethodTypeName; - private string _singleMethodName; - private IReadOnlyList _singleMethodGenericArgs; - - private IReadOnlyList _codegenOptions = Array.Empty(); +namespace ILCompiler; - private IReadOnlyList _rdXmlFilePaths = Array.Empty(); - - private IReadOnlyList _initAssemblies = Array.Empty(); - - private IReadOnlyList _appContextSwitches = Array.Empty(); - - private IReadOnlyList _runtimeOptions = Array.Empty(); - - private IReadOnlyList _featureSwitches = Array.Empty(); +internal class Program +{ + private readonly ILCompilerRootCommand _command; - private IReadOnlyList _suppressedWarnings = Array.Empty(); + public Program(ILCompilerRootCommand command) + { + _command = command; - private IReadOnlyList _directPInvokes = Array.Empty(); + if (command.Result.GetValueForOption(command.WaitForDebugger)) + { + Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); + Console.ReadLine(); + } + } - private IReadOnlyList _directPInvokeLists = Array.Empty(); + private IReadOnlyCollection CreateInitializerList(CompilerTypeSystemContext context) + { + List assembliesWithInitializers = new List(); - private bool _resilient; + // Build a list of assemblies that have an initializer that needs to run before + // any user code runs. + foreach (string initAssemblyName in Get(_command.InitAssemblies)) + { + ModuleDesc assembly = context.ResolveAssembly(new AssemblyName(initAssemblyName), throwIfNotFound: true); + assembliesWithInitializers.Add(assembly); + } - private IReadOnlyList _rootedAssemblies = Array.Empty(); - private IReadOnlyList _conditionallyRootedAssemblies = Array.Empty(); - private IReadOnlyList _trimmedAssemblies = Array.Empty(); - private bool _rootDefaultAssemblies; + var libraryInitializers = new LibraryInitializers(context, assembliesWithInitializers); - public IReadOnlyList _mibcFilePaths = Array.Empty(); + List initializerList = new List(libraryInitializers.LibraryInitializerMethods); - private IReadOnlyList _singleWarnEnabledAssemblies = Array.Empty(); - private IReadOnlyList _singleWarnDisabledAssemblies = Array.Empty(); - private bool _singleWarn; + // If there are any AppContext switches the user wishes to enable, generate code that sets them. + string[] appContextSwitches = Get(_command.AppContextSwitches); + if (appContextSwitches.Length > 0) + { + MethodDesc appContextInitMethod = new Internal.IL.Stubs.StartupCode.AppContextInitializerMethod( + context.GeneratedAssembly.GetGlobalModuleType(), appContextSwitches); + initializerList.Add(appContextInitMethod); + } - private string _makeReproPath; + return initializerList; + } - private bool _help; + public int Run() + { + string outputFilePath = Get(_command.OutputFilePath); + if (outputFilePath == null) + throw new CommandLineException("Output filename must be specified (/out )"); - private Program() - { - } + TargetArchitecture targetArchitecture = Get(_command.TargetArchitecture); + InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); + TargetOS targetOS = Get(_command.TargetOS); - private void Help(string helpText) + // The runtime expects certain baselines that the codegen can assume as well. + if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) { - Console.WriteLine(); - Console.Write("Microsoft (R) .NET Native IL Compiler"); - Console.Write(" "); - Console.Write(typeof(Program).GetTypeInfo().Assembly.GetName().Version); - Console.WriteLine(); - Console.WriteLine(); - Console.WriteLine(helpText); + instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication } - - public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture arch) + else if (targetArchitecture == TargetArchitecture.ARM64) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - os = TargetOS.Windows; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - os = TargetOS.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - os = TargetOS.OSX; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) - os = TargetOS.FreeBSD; + if (targetOS == TargetOS.OSX) + { + // For osx-arm64 we know that apple-m1 is a baseline + instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); + } else - throw new NotImplementedException(); - - switch (RuntimeInformation.ProcessArchitecture) { - case Architecture.X86: - arch = TargetArchitecture.X86; - break; - case Architecture.X64: - arch = TargetArchitecture.X64; - break; - case Architecture.Arm: - arch = TargetArchitecture.ARM; - break; - case Architecture.Arm64: - arch = TargetArchitecture.ARM64; - break; - default: - throw new NotImplementedException(); + instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication } - - } - - private void InitializeDefaultOptions() - { - ComputeDefaultOptions(out _targetOS, out _targetArchitecture); } - private ArgumentSyntax ParseCommandLine(string[] args) + string instructionSetArg = Get(_command.InstructionSet); + if (instructionSetArg != null) { - var validReflectionDataOptions = new string[] { "all", "none" }; + var instructionSetParams = new List(); - IReadOnlyList inputFiles = Array.Empty(); - IReadOnlyList referenceFiles = Array.Empty(); - - bool optimize = false; - bool optimizeSpace = false; - bool optimizeTime = false; - - bool waitForDebugger = false; - AssemblyName name = typeof(Program).GetTypeInfo().Assembly.GetName(); - ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, syntax => - { - syntax.ApplicationName = name.Name.ToString(); - - // HandleHelp writes to error, fails fast with crash dialog and lacks custom formatting. - syntax.HandleHelp = false; - syntax.HandleErrors = true; - - syntax.DefineOption("h|help", ref _help, "Help message for ILC"); - syntax.DefineOptionList("r|reference", ref referenceFiles, "Reference file(s) for compilation"); - syntax.DefineOption("o|out", ref _outputFilePath, "Output file path"); - syntax.DefineOption("O", ref optimize, "Enable optimizations"); - syntax.DefineOption("Os", ref optimizeSpace, "Enable optimizations, favor code space"); - syntax.DefineOption("Ot", ref optimizeTime, "Enable optimizations, favor code speed"); - syntax.DefineOptionList("m|mibc", ref _mibcFilePaths, "Mibc file(s) for profile guided optimization"); ; - syntax.DefineOption("g", ref _enableDebugInfo, "Emit debugging information"); - syntax.DefineOption("gdwarf-5", ref _useDwarf5, "Generate source-level debug information with dwarf version 5"); - syntax.DefineOption("nativelib", ref _nativeLib, "Compile as static or shared library"); - syntax.DefineOption("exportsfile", ref _exportsFile, "File to write exported method definitions"); - syntax.DefineOption("dgmllog", ref _dgmlLogFileName, "Save result of dependency analysis as DGML"); - syntax.DefineOption("fulllog", ref _generateFullDgmlLog, "Save detailed log of dependency analysis"); - syntax.DefineOption("scandgmllog", ref _scanDgmlLogFileName, "Save result of scanner dependency analysis as DGML"); - syntax.DefineOption("scanfulllog", ref _generateFullScanDgmlLog, "Save detailed log of scanner dependency analysis"); - syntax.DefineOption("verbose", ref _isVerbose, "Enable verbose logging"); - syntax.DefineOption("systemmodule", ref _systemModuleName, "System module name (default: System.Private.CoreLib)"); - syntax.DefineOption("multifile", ref _multiFile, "Compile only input files (do not compile referenced assemblies)"); - syntax.DefineOption("waitfordebugger", ref waitForDebugger, "Pause to give opportunity to attach debugger"); - syntax.DefineOption("resilient", ref _resilient, "Ignore unresolved types, methods, and assemblies. Defaults to false"); - syntax.DefineOptionList("codegenopt", ref _codegenOptions, "Define a codegen option"); - syntax.DefineOptionList("rdxml", ref _rdXmlFilePaths, "RD.XML file(s) for compilation"); - syntax.DefineOption("map", ref _mapFileName, "Generate a map file"); - syntax.DefineOption("metadatalog", ref _metadataLogFileName, "Generate a metadata log file"); - syntax.DefineOption("nometadatablocking", ref _noMetadataBlocking, "Ignore metadata blocking for internal implementation details"); - syntax.DefineOption("completetypemetadata", ref _completeTypesMetadata, "Generate complete metadata for types"); - syntax.DefineOption("reflectiondata", ref _reflectionData, $"Reflection data to generate (one of: {string.Join(", ", validReflectionDataOptions)})"); - syntax.DefineOption("scanreflection", ref _scanReflection, "Scan IL for reflection patterns"); - syntax.DefineOption("scan", ref _useScanner, "Use IL scanner to generate optimized code (implied by -O)"); - syntax.DefineOption("noscan", ref _noScanner, "Do not use IL scanner to generate optimized code"); - syntax.DefineOption("ildump", ref _ilDump, "Dump IL assembly listing for compiler-generated IL"); - syntax.DefineOption("stacktracedata", ref _emitStackTraceData, "Emit data to support generating stack trace strings at runtime"); - syntax.DefineOption("methodbodyfolding", ref _methodBodyFolding, "Fold identical method bodies"); - syntax.DefineOptionList("initassembly", ref _initAssemblies, "Assembly(ies) with a library initializer"); - syntax.DefineOptionList("appcontextswitch", ref _appContextSwitches, "System.AppContext switches to set (format: 'Key=Value')"); - syntax.DefineOptionList("feature", ref _featureSwitches, "Feature switches to apply (format: 'Namespace.Name=[true|false]'"); - syntax.DefineOptionList("runtimeopt", ref _runtimeOptions, "Runtime options to set"); - syntax.DefineOption("parallelism", ref _parallelism, "Maximum number of threads to use during compilation"); - syntax.DefineOption("instructionset", ref _instructionSet, "Instruction set to allow or disallow"); - syntax.DefineOption("guard", ref _guard, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); - syntax.DefineOption("preinitstatics", ref _preinitStatics, "Interpret static constructors at compile time if possible (implied by -O)"); - syntax.DefineOption("nopreinitstatics", ref _noPreinitStatics, "Do not interpret static constructors at compile time"); - syntax.DefineOptionList("nowarn", ref _suppressedWarnings, "Disable specific warning messages"); - syntax.DefineOption("singlewarn", ref _singleWarn, "Generate single AOT/trimming warning per assembly"); - syntax.DefineOptionList("singlewarnassembly", ref _singleWarnEnabledAssemblies, "Generate single AOT/trimming warning for given assembly"); - syntax.DefineOptionList("nosinglewarnassembly", ref _singleWarnDisabledAssemblies, "Expand AOT/trimming warnings for given assembly"); - syntax.DefineOptionList("directpinvoke", ref _directPInvokes, "PInvoke to call directly"); - syntax.DefineOptionList("directpinvokelist", ref _directPInvokeLists, "File with list of PInvokes to call directly"); - syntax.DefineOption("maxgenericcycle", ref _maxGenericCycle, "Max depth of generic cycle"); - syntax.DefineOptionList("root", ref _rootedAssemblies, "Fully generate given assembly"); - syntax.DefineOptionList("conditionalroot", ref _conditionallyRootedAssemblies, "Fully generate given assembly if it's used"); - syntax.DefineOptionList("trim", ref _trimmedAssemblies, "Trim the specified assembly"); - syntax.DefineOption("defaultrooting", ref _rootDefaultAssemblies, "Root assemblies that are not marked [IsTrimmable]"); - - syntax.DefineOption("targetarch", ref _targetArchitectureStr, "Target architecture for cross compilation"); - syntax.DefineOption("targetos", ref _targetOSStr, "Target OS for cross compilation"); - syntax.DefineOption("jitpath", ref _jitPath, "Path to JIT compiler library"); - - syntax.DefineOption("singlemethodtypename", ref _singleMethodTypeName, "Single method compilation: assembly-qualified name of the owning type"); - syntax.DefineOption("singlemethodname", ref _singleMethodName, "Single method compilation: name of the method"); - syntax.DefineOptionList("singlemethodgenericarg", ref _singleMethodGenericArgs, "Single method compilation: generic arguments to the method"); - - syntax.DefineOption("make-repro-path", ref _makeReproPath, "Path where to place a repro package"); - - syntax.DefineParameterList("in", ref inputFiles, "Input file(s) to compile"); - }); - - if (_help) + // Normalize instruction set format to include implied +. + string[] instructionSetParamsInput = instructionSetArg.Split(','); + for (int i = 0; i < instructionSetParamsInput.Length; i++) { - List extraHelp = new List(); - - extraHelp.Add("Options may be passed on the command line, or via response file. On the command line switch values may be specified by passing " + - "the option followed by a space followed by the value of the option, or by specifying a : between option and switch value. A response file " + - "is specified by passing the @ symbol before the response file name. In a response file all options must be specified on their own lines, and " + - "only the : syntax for switches is supported."); - - extraHelp.Add(""); - - extraHelp.Add("Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are " + - "considered to be input files. If no input files begin with '--' then this option is not necessary."); - - extraHelp.Add(""); - - string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64" }; - string[] ValidOS = new string[] { "windows", "linux", "osx" }; - - Program.ComputeDefaultOptions(out TargetOS defaultOs, out TargetArchitecture defaultArch); + string instructionSet = instructionSetParamsInput[i]; - extraHelp.Add(String.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'", "--targetos", String.Join("', '", ValidOS), defaultOs.ToString().ToLowerInvariant())); + if (String.IsNullOrEmpty(instructionSet)) + throw new CommandLineException("Instruction set must not be empty"); - extraHelp.Add(""); - - extraHelp.Add(String.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'", "--targetarch", String.Join("', '", ValidArchitectures), defaultArch.ToString().ToLowerInvariant())); - - extraHelp.Add(""); - - extraHelp.Add("The allowable values for the --instruction-set option are described in the table below. Each architecture has a different set of valid " + - "instruction sets, and multiple instruction sets may be specified by separating the instructions sets by a ','. For example 'avx2,bmi,lzcnt'"); - - foreach (string arch in ValidArchitectures) + char firstChar = instructionSet[0]; + if ((firstChar != '+') && (firstChar != '-')) { - StringBuilder archString = new StringBuilder(); - - archString.Append(arch); - archString.Append(": "); - - TargetArchitecture targetArch = Program.GetTargetArchitectureFromArg(arch); - bool first = true; - foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) - { - // Only instruction sets with are specifiable should be printed to the help text - if (instructionSet.Specifiable) - { - if (first) - { - first = false; - } - else - { - archString.Append(", "); - } - archString.Append(instructionSet.Name); - } - } - - extraHelp.Add(archString.ToString()); + instructionSet = "+" + instructionSet; } - - extraHelp.Add(""); - extraHelp.Add("The following CPU names are predefined groups of instruction sets and can be used in --instruction-set too:"); - extraHelp.Add(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); - - argSyntax.ExtraHelpParagraphs = extraHelp; + instructionSetParams.Add(instructionSet); } - if (waitForDebugger) + Dictionary instructionSetSpecification = new Dictionary(); + foreach (string instructionSetSpecifier in instructionSetParams) { - Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); - Console.ReadLine(); - } + string instructionSet = instructionSetSpecifier.Substring(1); - _optimizationMode = OptimizationMode.None; - if (optimizeSpace) - { - if (optimizeTime) - Console.WriteLine("Warning: overriding -Ot with -Os"); - _optimizationMode = OptimizationMode.PreferSize; + bool enabled = instructionSetSpecifier[0] == '+' ? true : false; + if (enabled) + { + if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) + throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); + } + else + { + if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) + throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); + } } - else if (optimizeTime) - _optimizationMode = OptimizationMode.PreferSpeed; - else if (optimize) - _optimizationMode = OptimizationMode.Blended; - - foreach (var input in inputFiles) - Helpers.AppendExpandedPaths(_inputFilePaths, input, true); - - foreach (var reference in referenceFiles) - Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false); + } - if (_makeReproPath != null) - { - // Create a repro package in the specified path - // This package will have the set of input files needed for compilation - // + the original command line arguments - // + a rsp file that should work to directly run out of the zip file + instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, + (string specifiedInstructionSet, string impliedInstructionSet) => + throw new CommandLineException(String.Format("Unsupported combination of instruction sets: {0}/{1}", specifiedInstructionSet, impliedInstructionSet))); - Helpers.MakeReproPackage(_makeReproPath, _outputFilePath, args, argSyntax, new[] { "-r", "-m", "--rdxml", "--directpinvokelist" }); - } + InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); - if (_reflectionData != null && Array.IndexOf(validReflectionDataOptions, _reflectionData) < 0) - { - Console.WriteLine($"Warning: option '{_reflectionData}' not recognized"); + // Optimistically assume some instruction sets are present. + if (targetArchitecture == TargetArchitecture.X86 || targetArchitecture == TargetArchitecture.X64) + { + // We set these hardware features as opportunistically enabled as most of hardware in the wild supports them. + // Note that we do not indicate support for AVX, or any other instruction set which uses the VEX encodings as + // the presence of those makes otherwise acceptable code be unusable on hardware which does not support VEX encodings. + // + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("movbe"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); + + // If AVX was enabled, we can opportunistically enable instruction sets which use the VEX encodings + Debug.Assert(InstructionSet.X64_AVX == InstructionSet.X86_AVX); + if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX)) + { + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("fma"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); } - - return argSyntax; } - - private IReadOnlyCollection CreateInitializerList(CompilerTypeSystemContext context) + else if (targetArchitecture == TargetArchitecture.ARM64) { - List assembliesWithInitializers = new List(); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha1"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lse"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rcpc"); + } - // Build a list of assemblies that have an initializer that needs to run before - // any user code runs. - foreach (string initAssemblyName in _initAssemblies) + optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, + (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); + optimisticInstructionSet.Remove(unsupportedInstructionSet); + optimisticInstructionSet.Add(supportedInstructionSet); + + var instructionSetSupport = new InstructionSetSupport(supportedInstructionSet, + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(targetArchitecture), + targetArchitecture); + + string systemModuleName = Get(_command.SystemModuleName); + string reflectionData = Get(_command.ReflectionData); + bool supportsReflection = reflectionData != "none" && systemModuleName == Helpers.DefaultSystemModule; + + // + // Initialize type system context + // + + SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; + + var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); + var targetAbi = TargetAbi.NativeAot; + var targetDetails = new TargetDetails(targetArchitecture, targetOS, targetAbi, simdVectorLength); + CompilerTypeSystemContext typeSystemContext = + new CompilerTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0, Get(_command.MaxGenericCycle)); + + // + // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since + // some tests contain a mixture of both managed and native binaries. + // + // See: https://github.com/dotnet/corert/issues/2785 + // + // When we undo this hack, replace the foreach with + // typeSystemContext.InputFilePaths = _command.Result.GetValueForArgument(inputFilePaths); + // + Dictionary inputFilePaths = new Dictionary(); + foreach (var inputFile in _command.Result.GetValueForArgument(_command.InputFilePaths)) + { + try + { + var module = typeSystemContext.GetModuleFromPath(inputFile.Value); + inputFilePaths.Add(inputFile.Key, inputFile.Value); + } + catch (TypeSystemException.BadImageFormatException) { - ModuleDesc assembly = context.ResolveAssembly(new AssemblyName(initAssemblyName), throwIfNotFound: true); - assembliesWithInitializers.Add(assembly); + // Keep calm and carry on. } + } - var libraryInitializers = new LibraryInitializers(context, assembliesWithInitializers); + typeSystemContext.InputFilePaths = inputFilePaths; + typeSystemContext.ReferenceFilePaths = Get(_command.ReferenceFiles); + if (!typeSystemContext.InputFilePaths.ContainsKey(systemModuleName) + && !typeSystemContext.ReferenceFilePaths.ContainsKey(systemModuleName)) + throw new CommandLineException($"System module {systemModuleName} does not exists. Make sure that you specify --systemmodule"); - List initializerList = new List(libraryInitializers.LibraryInitializerMethods); + typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(systemModuleName)); - // If there are any AppContext switches the user wishes to enable, generate code that sets them. - if (_appContextSwitches.Count > 0) + if (typeSystemContext.InputFilePaths.Count == 0) + throw new CommandLineException("No input files specified"); + + SecurityMitigationOptions securityMitigationOptions = 0; + string guard = Get(_command.Guard); + if (StringComparer.OrdinalIgnoreCase.Equals(guard, "cf")) + { + if (targetOS != TargetOS.Windows) { - MethodDesc appContextInitMethod = new Internal.IL.Stubs.StartupCode.AppContextInitializerMethod( - context.GeneratedAssembly.GetGlobalModuleType(), _appContextSwitches); - initializerList.Add(appContextInitMethod); + throw new CommandLineException($"Control flow guard only available on Windows"); } - return initializerList; + securityMitigationOptions = SecurityMitigationOptions.ControlFlowGuardAnnotations; } - - private static TargetArchitecture GetTargetArchitectureFromArg(string archArg) + else if (!String.IsNullOrEmpty(guard)) { - if (archArg.Equals("x86", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.X86; - else if (archArg.Equals("x64", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.X64; - else if (archArg.Equals("arm", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.ARM; - else if (archArg.Equals("arm64", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.ARM64; - else - throw new CommandLineException("Target architecture is not supported"); + throw new CommandLineException($"Unrecognized mitigation option '{guard}'"); } - private static TargetOS GetTargetOSFromArg(string osArg) + // + // Initialize compilation group and compilation roots + // + + // Single method mode? + MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); + + CompilationModuleGroup compilationGroup; + List compilationRoots = new List(); + bool multiFile = Get(_command.MultiFile); + if (singleMethod != null) { - if (osArg.Equals("windows", StringComparison.OrdinalIgnoreCase)) - return TargetOS.Windows; - else if (osArg.Equals("linux", StringComparison.OrdinalIgnoreCase)) - return TargetOS.Linux; - else if (osArg.Equals("osx", StringComparison.OrdinalIgnoreCase)) - return TargetOS.OSX; - else - throw new CommandLineException("Target OS is not supported"); + // Compiling just a single method + compilationGroup = new SingleMethodCompilationModuleGroup(singleMethod); + compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); } - - private int Run(string[] args) + else { - InitializeDefaultOptions(); - - ArgumentSyntax syntax = ParseCommandLine(args); - if (_help) + // Either single file, or multifile library, or multifile consumption. + EcmaModule entrypointModule = null; + bool systemModuleIsInputModule = false; + foreach (var inputFile in typeSystemContext.InputFilePaths) { - Help(syntax.GetHelpText()); - return 1; - } - - if (_outputFilePath == null) - throw new CommandLineException("Output filename must be specified (/out )"); + EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); - // - // Set target Architecture and OS - // - if (_targetArchitectureStr != null) - { - _targetArchitecture = GetTargetArchitectureFromArg(_targetArchitectureStr); - } - if (_targetOSStr != null) - { - _targetOS = GetTargetOSFromArg(_targetOSStr); - } + if (module.PEReader.PEHeaders.IsExe) + { + if (entrypointModule != null) + throw new Exception("Multiple EXE modules"); + entrypointModule = module; + } - InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); + if (module == typeSystemContext.SystemModule) + systemModuleIsInputModule = true; - // The runtime expects certain baselines that the codegen can assume as well. - if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) - { - instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication + compilationRoots.Add(new ExportedMethodsRootProvider(module)); } - else if (_targetArchitecture == TargetArchitecture.ARM64) + + string[] runtimeOptions = Get(_command.RuntimeOptions); + if (entrypointModule != null) { - if (_targetOS == TargetOS.OSX) - { - // For osx-arm64 we know that apple-m1 is a baseline - instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); - } - else - { - instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication - } + compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext))); + compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); + compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); } - if (_instructionSet != null) + bool nativeLib = Get(_command.NativeLib); + if (multiFile) { - List instructionSetParams = new List(); + List inputModules = new List(); - // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = _instructionSet.Split(','); - for (int i = 0; i < instructionSetParamsInput.Length; i++) + foreach (var inputFile in typeSystemContext.InputFilePaths) { - string instructionSet = instructionSetParamsInput[i]; - - if (String.IsNullOrEmpty(instructionSet)) - throw new CommandLineException("Instruction set must not be empty"); + EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); - char firstChar = instructionSet[0]; - if ((firstChar != '+') && (firstChar != '-')) + if (entrypointModule == null) { - instructionSet = "+" + instructionSet; + // This is a multifile production build - we need to root all methods + compilationRoots.Add(new LibraryRootProvider(module)); } - instructionSetParams.Add(instructionSet); + inputModules.Add(module); } - Dictionary instructionSetSpecification = new Dictionary(); - foreach (string instructionSetSpecifier in instructionSetParams) - { - string instructionSet = instructionSetSpecifier.Substring(1); - - bool enabled = instructionSetSpecifier[0] == '+' ? true : false; - if (enabled) - { - if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) - throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); - } - else - { - if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) - throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); - } - } - } - - instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, - (string specifiedInstructionSet, string impliedInstructionSet) => - throw new CommandLineException(String.Format("Unsupported combination of instruction sets: {0}/{1}", specifiedInstructionSet, impliedInstructionSet))); - - InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); - - // Optimistically assume some instruction sets are present. - if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) - { - // We set these hardware features as opportunistically enabled as most of hardware in the wild supports them. - // Note that we do not indicate support for AVX, or any other instruction set which uses the VEX encodings as - // the presence of those makes otherwise acceptable code be unusable on hardware which does not support VEX encodings. - // - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("movbe"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); - - // If AVX was enabled, we can opportunistically enable instruction sets which use the VEX encodings - Debug.Assert(InstructionSet.X64_AVX == InstructionSet.X86_AVX); - if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX)) - { - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("fma"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi2"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); - } + compilationGroup = new MultiFileSharedCompilationModuleGroup(typeSystemContext, inputModules); } - else if (_targetArchitecture == TargetArchitecture.ARM64) + else { - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha1"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha2"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lse"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rcpc"); - } - - optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, - (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); - optimisticInstructionSet.Remove(unsupportedInstructionSet); - optimisticInstructionSet.Add(supportedInstructionSet); - - var instructionSetSupport = new InstructionSetSupport(supportedInstructionSet, - unsupportedInstructionSet, - optimisticInstructionSet, - InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), - _targetArchitecture); - - bool supportsReflection = _reflectionData != "none" && _systemModuleName == DefaultSystemModule; - - // - // Initialize type system context - // - - SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; + if (entrypointModule == null && !nativeLib) + throw new Exception("No entrypoint module"); - var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); - var targetAbi = TargetAbi.NativeAot; - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, targetAbi, simdVectorLength); - CompilerTypeSystemContext typeSystemContext = - new CompilerTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0, _maxGenericCycle); - - // - // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since - // some tests contain a mixture of both managed and native binaries. - // - // See: https://github.com/dotnet/corert/issues/2785 - // - // When we undo this hack, replace the foreach with - // typeSystemContext.InputFilePaths = _inputFilePaths; - // - Dictionary inputFilePaths = new Dictionary(); - foreach (var inputFile in _inputFilePaths) - { - try - { - var module = typeSystemContext.GetModuleFromPath(inputFile.Value); - inputFilePaths.Add(inputFile.Key, inputFile.Value); - } - catch (TypeSystemException.BadImageFormatException) - { - // Keep calm and carry on. - } + if (!systemModuleIsInputModule) + compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule)); + compilationGroup = new SingleFileCompilationModuleGroup(); } - typeSystemContext.InputFilePaths = inputFilePaths; - typeSystemContext.ReferenceFilePaths = _referenceFilePaths; - if (!typeSystemContext.InputFilePaths.ContainsKey(_systemModuleName) - && !typeSystemContext.ReferenceFilePaths.ContainsKey(_systemModuleName)) - throw new CommandLineException($"System module {_systemModuleName} does not exists. Make sure that you specify --systemmodule"); - - typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(_systemModuleName)); - - if (typeSystemContext.InputFilePaths.Count == 0) - throw new CommandLineException("No input files specified"); - - SecurityMitigationOptions securityMitigationOptions = 0; - if (StringComparer.OrdinalIgnoreCase.Equals(_guard, "cf")) - { - if (_targetOS != TargetOS.Windows) - { - throw new CommandLineException($"Control flow guard only available on Windows"); - } - - securityMitigationOptions = SecurityMitigationOptions.ControlFlowGuardAnnotations; - } - else if (!String.IsNullOrEmpty(_guard)) + if (nativeLib) { - throw new CommandLineException($"Unrecognized mitigation option '{_guard}'"); + // Set owning module of generated native library startup method to compiler generated module, + // to ensure the startup method is included in the object file during multimodule mode build + compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext))); + compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); + compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); } - // - // Initialize compilation group and compilation roots - // - - // Single method mode? - MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); - - CompilationModuleGroup compilationGroup; - List compilationRoots = new List(); - if (singleMethod != null) + foreach (var rdXmlFilePath in Get(_command.RdXmlFilePaths)) { - // Compiling just a single method - compilationGroup = new SingleMethodCompilationModuleGroup(singleMethod); - compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); + compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath)); } - else - { - // Either single file, or multifile library, or multifile consumption. - EcmaModule entrypointModule = null; - bool systemModuleIsInputModule = false; - foreach (var inputFile in typeSystemContext.InputFilePaths) - { - EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); + } - if (module.PEReader.PEHeaders.IsExe) - { - if (entrypointModule != null) - throw new Exception("Multiple EXE modules"); - entrypointModule = module; - } + // Root whatever assemblies were specified on the command line + string[] rootedAssemblies = Get(_command.RootedAssemblies); + foreach (var rootedAssembly in rootedAssemblies) + { + // For compatibility with IL Linker, the parameter could be a file name or an assembly name. + // This is the logic IL Linker uses to decide how to interpret the string. Really. + EcmaModule module = File.Exists(rootedAssembly) + ? typeSystemContext.GetModuleFromPath(rootedAssembly) + : typeSystemContext.GetModuleForSimpleName(rootedAssembly); + + // We only root the module type. The rest will fall out because we treat rootedAssemblies + // same as conditionally rooted ones and here we're fulfilling the condition ("something is used"). + compilationRoots.Add( + new GenericRootProvider(module, + (ModuleDesc module, IRootingServiceProvider rooter) => rooter.AddCompilationRoot(module.GetGlobalModuleType(), "Command line root"))); + } - if (module == typeSystemContext.SystemModule) - systemModuleIsInputModule = true; + // + // Compile + // - compilationRoots.Add(new ExportedMethodsRootProvider(module)); - } + CompilationBuilder builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); - if (entrypointModule != null) - { - compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext))); - compilationRoots.Add(new RuntimeConfigurationRootProvider(_runtimeOptions)); - compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); - } + string compilationUnitPrefix = multiFile ? Path.GetFileNameWithoutExtension(outputFilePath) : ""; + builder.UseCompilationUnitPrefix(compilationUnitPrefix); - if (_multiFile) - { - List inputModules = new List(); + string[] mibcFilePaths = Get(_command.MibcFilePaths); + if (mibcFilePaths.Length > 0) + ((RyuJitCompilationBuilder)builder).UseProfileData(mibcFilePaths); - foreach (var inputFile in typeSystemContext.InputFilePaths) - { - EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); - - if (entrypointModule == null) - { - // This is a multifile production build - we need to root all methods - compilationRoots.Add(new LibraryRootProvider(module)); - } - inputModules.Add(module); - } + string jitPath = Get(_command.JitPath); + if (!String.IsNullOrEmpty(jitPath)) + ((RyuJitCompilationBuilder)builder).UseJitPath(jitPath); - compilationGroup = new MultiFileSharedCompilationModuleGroup(typeSystemContext, inputModules); - } - else - { - if (entrypointModule == null && !_nativeLib) - throw new Exception("No entrypoint module"); + PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, + Get(_command.DirectPInvokes), Get(_command.DirectPInvokeLists)); - if (!systemModuleIsInputModule) - compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule)); - compilationGroup = new SingleFileCompilationModuleGroup(); - } + ILProvider ilProvider = new NativeAotILProvider(); - if (_nativeLib) - { - // Set owning module of generated native library startup method to compiler generated module, - // to ensure the startup method is included in the object file during multimodule mode build - compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext))); - compilationRoots.Add(new RuntimeConfigurationRootProvider(_runtimeOptions)); - compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); - } + List> featureSwitches = new List>(); + foreach (var switchPair in Get(_command.FeatureSwitches)) + { + string[] switchAndValue = switchPair.Split('='); + if (switchAndValue.Length != 2 + || !bool.TryParse(switchAndValue[1], out bool switchValue)) + throw new CommandLineException($"Unexpected feature switch pair '{switchPair}'"); + featureSwitches.Add(new KeyValuePair(switchAndValue[0], switchValue)); + } + ilProvider = new FeatureSwitchManager(ilProvider, featureSwitches); - foreach (var rdXmlFilePath in _rdXmlFilePaths) - { - compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath)); - } - } + var logger = new Logger(Console.Out, Get(_command.IsVerbose), ProcessWarningCodes(Get(_command.SuppressedWarnings)), + Get(_command.SingleWarn), Get(_command.SingleWarnEnabledAssemblies), Get(_command.SingleWarnDisabledAssemblies)); - _conditionallyRootedAssemblies = new List(_conditionallyRootedAssemblies.Select(a => ILLinkify(a))); - _trimmedAssemblies = new List(_trimmedAssemblies.Select(a => ILLinkify(a))); + var stackTracePolicy = Get(_command.EmitStackTraceData) ? + (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); - static string ILLinkify(string rootedAssembly) - { - // For compatibility with IL Linker, the parameter could be a file name or an assembly name. - // This is the logic IL Linker uses to decide how to interpret the string. Really. - string simpleName; - if (File.Exists(rootedAssembly)) - simpleName = Path.GetFileNameWithoutExtension(rootedAssembly); - else - simpleName = rootedAssembly; - return simpleName; - } + MetadataBlockingPolicy mdBlockingPolicy; + ManifestResourceBlockingPolicy resBlockingPolicy; + UsageBasedMetadataGenerationOptions metadataGenerationOptions = default; + if (supportsReflection) + { + mdBlockingPolicy = Get(_command.NoMetadataBlocking) ? + new NoMetadataBlockingPolicy() : new BlockedInternalsBlockingPolicy(typeSystemContext); + + resBlockingPolicy = new ManifestResourceBlockingPolicy(featureSwitches); + + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; + if (Get(_command.CompleteTypesMetadata)) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly; + if (Get(_command.ScanReflection)) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning; + if (reflectionData == "all") + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts; + if (Get(_command.RootDefaultAssemblies)) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.RootDefaultAssemblies; + } + else + { + mdBlockingPolicy = new FullyBlockedMetadataBlockingPolicy(); + resBlockingPolicy = new FullyBlockedManifestResourceBlockingPolicy(); + } - // Root whatever assemblies were specified on the command line - foreach (var rootedAssembly in _rootedAssemblies) - { - // For compatibility with IL Linker, the parameter could be a file name or an assembly name. - // This is the logic IL Linker uses to decide how to interpret the string. Really. - EcmaModule module = File.Exists(rootedAssembly) - ? typeSystemContext.GetModuleFromPath(rootedAssembly) - : typeSystemContext.GetModuleForSimpleName(rootedAssembly); - - // We only root the module type. The rest will fall out because we treat _rootedAssemblies - // same as conditionally rooted ones and here we're fulfilling the condition ("something is used"). - compilationRoots.Add( - new GenericRootProvider(module, - (ModuleDesc module, IRootingServiceProvider rooter) => rooter.AddCompilationRoot(module.GetGlobalModuleType(), "Command line root"))); - } + DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy(); + + var flowAnnotations = new ILLink.Shared.TrimAnalysis.FlowAnnotations(logger, ilProvider); + + MetadataManager metadataManager = new UsageBasedMetadataManager( + compilationGroup, + typeSystemContext, + mdBlockingPolicy, + resBlockingPolicy, + Get(_command.MetadataLogFileName), + stackTracePolicy, + invokeThunkGenerationPolicy, + flowAnnotations, + metadataGenerationOptions, + logger, + featureSwitches, + Get(_command.ConditionallyRootedAssemblies), + rootedAssemblies, + Get(_command.TrimmedAssemblies)); + + InteropStateManager interopStateManager = new InteropStateManager(typeSystemContext.GeneratedAssembly); + InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger); + + // Unless explicitly opted in at the command line, we enable scanner for retail builds by default. + // We also don't do this for multifile because scanner doesn't simulate inlining (this would be + // fixable by using a CompilationGroup for the scanner that has a bigger worldview, but + // let's cross that bridge when we get there). + bool useScanner = Get(_command.UseScanner) || + (_command.OptimizationMode != OptimizationMode.None && !multiFile); + + useScanner &= !Get(_command.NoScanner); + + // Enable static data preinitialization in optimized builds. + bool preinitStatics = Get(_command.PreinitStatics) || + (_command.OptimizationMode != OptimizationMode.None && !multiFile); + preinitStatics &= !Get(_command.NoPreinitStatics); + + var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics); + builder + .UseILProvider(ilProvider) + .UsePreinitializationManager(preinitManager); + + ILScanResults scanResults = null; + string scanDgmlLogFileName = Get(_command.ScanDgmlLogFileName); + int parallelism = Get(_command.Parallelism); + if (useScanner) + { + ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() + .UseCompilationRoots(compilationRoots) + .UseMetadataManager(metadataManager) + .UseParallelism(parallelism) + .UseInteropStubManager(interopStubManager) + .UseLogger(logger); - // - // Compile - // + if (scanDgmlLogFileName != null) + scannerBuilder.UseDependencyTracking(Get(_command.GenerateFullScanDgmlLog) ? + DependencyTrackingLevel.All : DependencyTrackingLevel.First); - CompilationBuilder builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); + IILScanner scanner = scannerBuilder.ToILScanner(); - string compilationUnitPrefix = _multiFile ? System.IO.Path.GetFileNameWithoutExtension(_outputFilePath) : ""; - builder.UseCompilationUnitPrefix(compilationUnitPrefix); + scanResults = scanner.Scan(); - if (_mibcFilePaths.Count > 0) - ((RyuJitCompilationBuilder)builder).UseProfileData(_mibcFilePaths); - if (!String.IsNullOrEmpty(_jitPath)) - ((RyuJitCompilationBuilder)builder).UseJitPath(_jitPath); + metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); - PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, _directPInvokes, _directPInvokeLists); + interopStubManager = scanResults.GetInteropStubManager(interopStateManager, pinvokePolicy); + } - ILProvider ilProvider = new NativeAotILProvider(); + string ilDump = Get(_command.IlDump); + DebugInformationProvider debugInfoProvider = Get(_command.EnableDebugInfo) ? + (ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(ilDump, new EcmaOnlyDebugInformationProvider())) : + new NullDebugInformationProvider(); + + string dgmlLogFileName = Get(_command.DgmlLogFileName); + DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? + DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? + DependencyTrackingLevel.All : DependencyTrackingLevel.First); + + compilationRoots.Add(metadataManager); + compilationRoots.Add(interopStubManager); + + builder + .UseInstructionSetSupport(instructionSetSupport) + .UseBackendOptions(Get(_command.CodegenOptions)) + .UseMethodBodyFolding(enable: Get(_command.MethodBodyFolding)) + .UseParallelism(parallelism) + .UseMetadataManager(metadataManager) + .UseInteropStubManager(interopStubManager) + .UseLogger(logger) + .UseDependencyTracking(trackingLevel) + .UseCompilationRoots(compilationRoots) + .UseOptimizationMode(_command.OptimizationMode) + .UseSecurityMitigationOptions(securityMitigationOptions) + .UseDebugInfoProvider(debugInfoProvider) + .UseDwarf5(Get(_command.UseDwarf5)); + + if (scanResults != null) + { + // If we have a scanner, feed the vtable analysis results to the compilation. + // This could be a command line switch if we really wanted to. + builder.UseVTableSliceProvider(scanResults.GetVTableLayoutInfo()); + + // If we have a scanner, feed the generic dictionary results to the compilation. + // This could be a command line switch if we really wanted to. + builder.UseGenericDictionaryLayoutProvider(scanResults.GetDictionaryLayoutInfo()); + + // If we have a scanner, we can drive devirtualization using the information + // we collected at scanning time (effectively sealing unsealed types if possible). + // This could be a command line switch if we really wanted to. + builder.UseDevirtualizationManager(scanResults.GetDevirtualizationManager()); + + // If we use the scanner's result, we need to consult it to drive inlining. + // This prevents e.g. devirtualizing and inlining methods on types that were + // never actually allocated. + builder.UseInliningPolicy(scanResults.GetInliningPolicy()); + + // Use an error provider that prevents us from re-importing methods that failed + // to import with an exception during scanning phase. We would see the same failure during + // compilation, but before RyuJIT gets there, it might ask questions that we don't + // have answers for because we didn't scan the entire method. + builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); + } - List> featureSwitches = new List>(); - foreach (var switchPair in _featureSwitches) - { - string[] switchAndValue = switchPair.Split('='); - if (switchAndValue.Length != 2 - || !bool.TryParse(switchAndValue[1], out bool switchValue)) - throw new CommandLineException($"Unexpected feature switch pair '{switchPair}'"); - featureSwitches.Add(new KeyValuePair(switchAndValue[0], switchValue)); - } - ilProvider = new FeatureSwitchManager(ilProvider, featureSwitches); + builder.UseResilience(Get(_command.Resilient)); - var logger = new Logger(Console.Out, _isVerbose, ProcessWarningCodes(_suppressedWarnings), _singleWarn, _singleWarnEnabledAssemblies, _singleWarnDisabledAssemblies); + ICompilation compilation = builder.ToCompilation(); - var stackTracePolicy = _emitStackTraceData ? - (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); + string mapFileName = Get(_command.MapFileName); + ObjectDumper dumper = mapFileName != null ? new ObjectDumper(mapFileName) : null; - MetadataBlockingPolicy mdBlockingPolicy; - ManifestResourceBlockingPolicy resBlockingPolicy; - UsageBasedMetadataGenerationOptions metadataGenerationOptions = default; - if (supportsReflection) - { - mdBlockingPolicy = _noMetadataBlocking - ? (MetadataBlockingPolicy)new NoMetadataBlockingPolicy() - : new BlockedInternalsBlockingPolicy(typeSystemContext); - - resBlockingPolicy = new ManifestResourceBlockingPolicy(featureSwitches); - - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; - if (_completeTypesMetadata) - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly; - if (_scanReflection) - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning; - if (_reflectionData == "all") - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts; - if (_rootDefaultAssemblies) - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.RootDefaultAssemblies; - } - else + CompilationResults compilationResults = compilation.Compile(outputFilePath, dumper); + string exportsFile = Get(_command.ExportsFile); + if (exportsFile != null) + { + ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile); + foreach (var compilationRoot in compilationRoots) { - mdBlockingPolicy = new FullyBlockedMetadataBlockingPolicy(); - resBlockingPolicy = new FullyBlockedManifestResourceBlockingPolicy(); + if (compilationRoot is ExportedMethodsRootProvider provider) + defFileWriter.AddExportedMethods(provider.ExportedMethods); } - DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy(); - - var flowAnnotations = new ILLink.Shared.TrimAnalysis.FlowAnnotations(logger, ilProvider); - - MetadataManager metadataManager = new UsageBasedMetadataManager( - compilationGroup, - typeSystemContext, - mdBlockingPolicy, - resBlockingPolicy, - _metadataLogFileName, - stackTracePolicy, - invokeThunkGenerationPolicy, - flowAnnotations, - metadataGenerationOptions, - logger, - featureSwitches, - _conditionallyRootedAssemblies.Concat(_rootedAssemblies), - _trimmedAssemblies); - - InteropStateManager interopStateManager = new InteropStateManager(typeSystemContext.GeneratedAssembly); - InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger); - - // Unless explicitly opted in at the command line, we enable scanner for retail builds by default. - // We also don't do this for multifile because scanner doesn't simulate inlining (this would be - // fixable by using a CompilationGroup for the scanner that has a bigger worldview, but - // let's cross that bridge when we get there). - bool useScanner = _useScanner || - (_optimizationMode != OptimizationMode.None && !_multiFile); - - useScanner &= !_noScanner; - - // Enable static data preinitialization in optimized builds. - bool preinitStatics = _preinitStatics || - (_optimizationMode != OptimizationMode.None && !_multiFile); - preinitStatics &= !_noPreinitStatics; - - var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics); - builder - .UseILProvider(ilProvider) - .UsePreinitializationManager(preinitManager); - - ILScanResults scanResults = null; - if (useScanner) - { - ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() - .UseCompilationRoots(compilationRoots) - .UseMetadataManager(metadataManager) - .UseParallelism(_parallelism) - .UseInteropStubManager(interopStubManager) - .UseLogger(logger); - - if (_scanDgmlLogFileName != null) - scannerBuilder.UseDependencyTracking(_generateFullScanDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); - - IILScanner scanner = scannerBuilder.ToILScanner(); - - scanResults = scanner.Scan(); + defFileWriter.EmitExportedMethods(); + } - metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); + typeSystemContext.LogWarnings(logger); - interopStubManager = scanResults.GetInteropStubManager(interopStateManager, pinvokePolicy); - } + if (dgmlLogFileName != null) + compilationResults.WriteDependencyLog(dgmlLogFileName); - DebugInformationProvider debugInfoProvider = _enableDebugInfo ? - (_ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(_ilDump, new EcmaOnlyDebugInformationProvider())) : - new NullDebugInformationProvider(); + if (scanResults != null) + { + if (scanDgmlLogFileName != null) + scanResults.WriteDependencyLog(scanDgmlLogFileName); - DependencyTrackingLevel trackingLevel = _dgmlLogFileName == null ? - DependencyTrackingLevel.None : (_generateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + // If the scanner and compiler don't agree on what to compile, the outputs of the scanner might not actually be usable. + // We are going to check this two ways: + // 1. The methods and types generated during compilation are a subset of method and types scanned + // 2. The methods and types scanned are a subset of methods and types compiled (this has a chance to hold for unoptimized builds only). - compilationRoots.Add(metadataManager); - compilationRoots.Add(interopStubManager); + // Check that methods and types generated during compilation are a subset of method and types scanned + bool scanningFail = false; + DiffCompilationResults(ref scanningFail, compilationResults.CompiledMethodBodies, scanResults.CompiledMethodBodies, + "Methods", "compiled", "scanned", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || IsRelatedToInvalidInput(method)); + DiffCompilationResults(ref scanningFail, compilationResults.ConstructedEETypes, scanResults.ConstructedEETypes, + "EETypes", "compiled", "scanned", type => !(type.GetTypeDefinition() is EcmaType)); - builder - .UseInstructionSetSupport(instructionSetSupport) - .UseBackendOptions(_codegenOptions) - .UseMethodBodyFolding(enable: _methodBodyFolding) - .UseParallelism(_parallelism) - .UseMetadataManager(metadataManager) - .UseInteropStubManager(interopStubManager) - .UseLogger(logger) - .UseDependencyTracking(trackingLevel) - .UseCompilationRoots(compilationRoots) - .UseOptimizationMode(_optimizationMode) - .UseSecurityMitigationOptions(securityMitigationOptions) - .UseDebugInfoProvider(debugInfoProvider) - .UseDwarf5(_useDwarf5); - - if (scanResults != null) + static bool IsRelatedToInvalidInput(MethodDesc method) { - // If we have a scanner, feed the vtable analysis results to the compilation. - // This could be a command line switch if we really wanted to. - builder.UseVTableSliceProvider(scanResults.GetVTableLayoutInfo()); - - // If we have a scanner, feed the generic dictionary results to the compilation. - // This could be a command line switch if we really wanted to. - builder.UseGenericDictionaryLayoutProvider(scanResults.GetDictionaryLayoutInfo()); - - // If we have a scanner, we can drive devirtualization using the information - // we collected at scanning time (effectively sealing unsealed types if possible). - // This could be a command line switch if we really wanted to. - builder.UseDevirtualizationManager(scanResults.GetDevirtualizationManager()); - - // If we use the scanner's result, we need to consult it to drive inlining. - // This prevents e.g. devirtualizing and inlining methods on types that were - // never actually allocated. - builder.UseInliningPolicy(scanResults.GetInliningPolicy()); - - // Use an error provider that prevents us from re-importing methods that failed - // to import with an exception during scanning phase. We would see the same failure during - // compilation, but before RyuJIT gets there, it might ask questions that we don't - // have answers for because we didn't scan the entire method. - builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); + // RyuJIT is more sensitive to invalid input and might detect cases that the scanner didn't have trouble with. + // If we find logic related to compiling fallback method bodies (methods that just throw) that got compiled + // but not scanned, it's usually fine. If it wasn't fine, we would probably crash before getting here. + return method.OwningType is MetadataType mdType + && mdType.Module == method.Context.SystemModule + && (mdType.Name.EndsWith("Exception") || mdType.Namespace.StartsWith("Internal.Runtime")); } - builder.UseResilience(_resilient); - - ICompilation compilation = builder.ToCompilation(); - - ObjectDumper dumper = _mapFileName != null ? new ObjectDumper(_mapFileName) : null; - - CompilationResults compilationResults = compilation.Compile(_outputFilePath, dumper); - if (_exportsFile != null) + // If optimizations are enabled, the results will for sure not match in the other direction due to inlining, etc. + // But there's at least some value in checking the scanner doesn't expand the universe too much in debug. + if (_command.OptimizationMode == OptimizationMode.None) { - ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, _exportsFile); - foreach (var compilationRoot in compilationRoots) - { - if (compilationRoot is ExportedMethodsRootProvider provider) - defFileWriter.AddExportedMethods(provider.ExportedMethods); - } - - defFileWriter.EmitExportedMethods(); - } + // Check that methods and types scanned are a subset of methods and types compiled - typeSystemContext.LogWarnings(logger); + // If we find diffs here, they're not critical, but still might be causing a Size on Disk regression. + bool dummy = false; - if (_dgmlLogFileName != null) - compilationResults.WriteDependencyLog(_dgmlLogFileName); - - if (scanResults != null) - { - if (_scanDgmlLogFileName != null) - scanResults.WriteDependencyLog(_scanDgmlLogFileName); - - // If the scanner and compiler don't agree on what to compile, the outputs of the scanner might not actually be usable. - // We are going to check this two ways: - // 1. The methods and types generated during compilation are a subset of method and types scanned - // 2. The methods and types scanned are a subset of methods and types compiled (this has a chance to hold for unoptimized builds only). - - // Check that methods and types generated during compilation are a subset of method and types scanned - bool scanningFail = false; - DiffCompilationResults(ref scanningFail, compilationResults.CompiledMethodBodies, scanResults.CompiledMethodBodies, - "Methods", "compiled", "scanned", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || IsRelatedToInvalidInput(method)); - DiffCompilationResults(ref scanningFail, compilationResults.ConstructedEETypes, scanResults.ConstructedEETypes, - "EETypes", "compiled", "scanned", type => !(type.GetTypeDefinition() is EcmaType)); - - static bool IsRelatedToInvalidInput(MethodDesc method) - { - // RyuJIT is more sensitive to invalid input and might detect cases that the scanner didn't have trouble with. - // If we find logic related to compiling fallback method bodies (methods that just throw) that got compiled - // but not scanned, it's usually fine. If it wasn't fine, we would probably crash before getting here. - return method.OwningType is MetadataType mdType - && mdType.Module == method.Context.SystemModule - && (mdType.Name.EndsWith("Exception") || mdType.Namespace.StartsWith("Internal.Runtime")); - } + // We additionally skip methods in SIMD module because there's just too many intrisics to handle and IL scanner + // doesn't expand them. They would show up as noisy diffs. + DiffCompilationResults(ref dummy, scanResults.CompiledMethodBodies, compilationResults.CompiledMethodBodies, + "Methods", "scanned", "compiled", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || method.OwningType.IsIntrinsic); + DiffCompilationResults(ref dummy, scanResults.ConstructedEETypes, compilationResults.ConstructedEETypes, + "EETypes", "scanned", "compiled", type => !(type.GetTypeDefinition() is EcmaType)); + } - // If optimizations are enabled, the results will for sure not match in the other direction due to inlining, etc. - // But there's at least some value in checking the scanner doesn't expand the universe too much in debug. - if (_optimizationMode == OptimizationMode.None) - { - // Check that methods and types scanned are a subset of methods and types compiled + if (scanningFail) + throw new Exception("Scanning failure"); + } - // If we find diffs here, they're not critical, but still might be causing a Size on Disk regression. - bool dummy = false; + if (debugInfoProvider is IDisposable) + ((IDisposable)debugInfoProvider).Dispose(); - // We additionally skip methods in SIMD module because there's just too many intrisics to handle and IL scanner - // doesn't expand them. They would show up as noisy diffs. - DiffCompilationResults(ref dummy, scanResults.CompiledMethodBodies, compilationResults.CompiledMethodBodies, - "Methods", "scanned", "compiled", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || method.OwningType.IsIntrinsic); - DiffCompilationResults(ref dummy, scanResults.ConstructedEETypes, compilationResults.ConstructedEETypes, - "EETypes", "scanned", "compiled", type => !(type.GetTypeDefinition() is EcmaType)); - } + preinitManager.LogStatistics(logger); - if (scanningFail) - throw new Exception("Scanning failure"); - } - - if (debugInfoProvider is IDisposable) - ((IDisposable)debugInfoProvider).Dispose(); + return 0; + } - preinitManager.LogStatistics(logger); + [System.Diagnostics.Conditional("DEBUG")] + private void DiffCompilationResults(ref bool result, IEnumerable set1, IEnumerable set2, string prefix, + string set1name, string set2name, Predicate filter) + { + HashSet diff = new HashSet(set1); + diff.ExceptWith(set2); - return 0; - } + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + diff.RemoveWhere(filter); - [System.Diagnostics.Conditional("DEBUG")] - private void DiffCompilationResults(ref bool result, IEnumerable set1, IEnumerable set2, string prefix, - string set1name, string set2name, Predicate filter) + if (diff.Count > 0) { - HashSet diff = new HashSet(set1); - diff.ExceptWith(set2); + result = true; - // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. - // https://github.com/dotnet/corert/issues/3873 - diff.RemoveWhere(filter); + Console.WriteLine($"*** {prefix} {set1name} but not {set2name}:"); - if (diff.Count > 0) + foreach (var d in diff) { - result = true; - - Console.WriteLine($"*** {prefix} {set1name} but not {set2name}:"); - - foreach (var d in diff) - { - Console.WriteLine(d.ToString()); - } + Console.WriteLine(d.ToString()); } } + } - private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) - { - ModuleDesc systemModule = context.SystemModule; - - TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => - { - return (MetadataType)context.GetCanonType(typeDefName) - ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); - }); - if (foundType == null) - throw new CommandLineException($"Type '{typeName}' not found"); - - return foundType; - } + private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) + { + ModuleDesc systemModule = context.SystemModule; - private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) + TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => { - if (_singleMethodName == null && _singleMethodTypeName == null && _singleMethodGenericArgs == null) - return null; + return (MetadataType)context.GetCanonType(typeDefName) + ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); + }); + if (foundType == null) + throw new CommandLineException($"Type '{typeName}' not found"); - if (_singleMethodName == null || _singleMethodTypeName == null) - throw new CommandLineException("Both method name and type name are required parameters for single method mode"); + return foundType; + } - TypeDesc owningType = FindType(context, _singleMethodTypeName); + private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) + { + string singleMethodName = Get(_command.SingleMethodName); + string singleMethodTypeName = Get(_command.SingleMethodTypeName); + string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); - // TODO: allow specifying signature to distinguish overloads - MethodDesc method = owningType.GetMethod(_singleMethodName, null); - if (method == null) - throw new CommandLineException($"Method '{_singleMethodName}' not found in '{_singleMethodTypeName}'"); + if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) + return null; - if (method.HasInstantiation != (_singleMethodGenericArgs != null) || - (method.HasInstantiation && (method.Instantiation.Length != _singleMethodGenericArgs.Count))) - { - throw new CommandLineException( - $"Expected {method.Instantiation.Length} generic arguments for method '{_singleMethodName}' on type '{_singleMethodTypeName}'"); - } + if (singleMethodName == null || singleMethodTypeName == null) + throw new CommandLineException("Both method name and type name are required parameters for single method mode"); - if (method.HasInstantiation) - { - List genericArguments = new List(); - foreach (var argString in _singleMethodGenericArgs) - genericArguments.Add(FindType(context, argString)); - method = method.MakeInstantiatedMethod(genericArguments.ToArray()); - } + TypeDesc owningType = FindType(context, singleMethodTypeName); - return method; - } + // TODO: allow specifying signature to distinguish overloads + MethodDesc method = owningType.GetMethod(singleMethodName, null); + if (method == null) + throw new CommandLineException($"Method '{singleMethodName}' not found in '{singleMethodTypeName}'"); - private static bool DumpReproArguments(CodeGenerationFailedException ex) + if (method.HasInstantiation != (singleMethodGenericArgs != null) || + (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) { - Console.WriteLine("To repro, add following arguments to the command line:"); - - MethodDesc failingMethod = ex.Method; - - var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)failingMethod.Context.SystemModule); - - Console.Write($"--singlemethodtypename \"{formatter.FormatName(failingMethod.OwningType, true)}\""); - Console.Write($" --singlemethodname {failingMethod.Name}"); - - for (int i = 0; i < failingMethod.Instantiation.Length; i++) - Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); - - return false; + throw new CommandLineException( + $"Expected {method.Instantiation.Length} generic arguments for method '{singleMethodName}' on type '{singleMethodTypeName}'"); } - private static IEnumerable ProcessWarningCodes(IEnumerable warningCodes) + if (method.HasInstantiation) { - foreach (string value in warningCodes) - { - string[] values = value.Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); - foreach (string id in values) - { - if (!id.StartsWith("IL", StringComparison.Ordinal) || !ushort.TryParse(id.Substring(2), out ushort code)) - continue; - - yield return code; - } - } + List genericArguments = new List(); + foreach (var argString in singleMethodGenericArgs) + genericArguments.Add(FindType(context, argString)); + method = method.MakeInstantiatedMethod(genericArguments.ToArray()); } - private static int Main(string[] args) + return method; + } + + private static IEnumerable ProcessWarningCodes(IEnumerable warningCodes) + { + foreach (string value in warningCodes) { -#if DEBUG - try - { - return new Program().Run(args); - } - catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + string[] values = value.Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string id in values) { - throw new NotSupportedException(); // Unreachable - } -#else - try - { - return new Program().Run(args); - } - catch (Exception e) - { - Console.Error.WriteLine("Error: " + e.Message); - Console.Error.WriteLine(e.ToString()); - return 1; + if (!id.StartsWith("IL", StringComparison.Ordinal) || !ushort.TryParse(id.Substring(2), out ushort code)) + continue; + + yield return code; } -#endif } } + + private T Get(Option option) => _command.Result.GetValueForOption(option); + + private static int Main(string[] args) => + new CommandLineBuilder(new ILCompilerRootCommand(args)) + .UseVersionOption() + .UseHelp(context => context.HelpBuilder.CustomizeLayout(ILCompilerRootCommand.GetExtendedHelp)) + .UseParseErrorReporting() + .Build() + .Invoke(args); } diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 81bdb61ae09ef..6004275cced5c 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -12,7 +12,6 @@ using System.Runtime.InteropServices; using System.Text; -using Internal.CommandLine; using Internal.IL; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -20,1156 +19,948 @@ using ILCompiler.Reflection.ReadyToRun; using ILCompiler.DependencyAnalysis; using ILCompiler.IBC; -using System.Diagnostics; -namespace ILCompiler -{ - internal class Program - { - private const string DefaultSystemModule = "System.Private.CoreLib"; +namespace ILCompiler; - private CommandLineOptions _commandLineOptions; - public TargetOS _targetOS; - public TargetArchitecture _targetArchitecture; - private bool _armelAbi = false; - public OptimizationMode _optimizationMode; - private ulong _imageBase; - - // File names as strings in args - private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _unrootedInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _referenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); +internal class Program +{ + private readonly Crossgen2RootCommand _command; - // Modules and their names after loading - private Dictionary _allInputFilePaths = new Dictionary(); - private List _referenceableModules = new List(); + public Program(Crossgen2RootCommand command) + { + _command = command; - private Dictionary _inputbubblereferenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (command.Result.GetValueForOption(command.WaitForDebugger)) + { + Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); + Console.ReadLine(); + } + } - private CompilerTypeSystemContext _typeSystemContext; - private ReadyToRunMethodLayoutAlgorithm _methodLayout; - private ReadyToRunFileLayoutAlgorithm _fileLayout; + private InstructionSetSupport ConfigureInstructionSetSupport() + { + InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); - private Program() + // Ready to run images are built with certain instruction set baselines + if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) { + instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication } - - public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture arch) + else if (_targetArchitecture == TargetArchitecture.ARM64) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - os = TargetOS.Windows; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - os = TargetOS.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - os = TargetOS.OSX; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) - os = TargetOS.FreeBSD; + if (_targetOS == TargetOS.OSX) + { + // For osx-arm64 we know that apple-m1 is a baseline + instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); + } else - throw new NotImplementedException(); - - switch (RuntimeInformation.ProcessArchitecture) { - case Architecture.X86: - arch = TargetArchitecture.X86; - break; - case Architecture.X64: - arch = TargetArchitecture.X64; - break; - case Architecture.Arm: - arch = TargetArchitecture.ARM; - break; - case Architecture.Arm64: - arch = TargetArchitecture.ARM64; - break; - default: - throw new NotImplementedException(); + instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication } - } - private void InitializeDefaultOptions() + if (_command.InstructionSet != null) { - ComputeDefaultOptions(out _targetOS, out _targetArchitecture); - } + List instructionSetParams = new List(); - private void ProcessCommandLine(string[] args) - { - PerfEventSource.StartStopEvents.CommandLineProcessingStart(); - _commandLineOptions = new CommandLineOptions(args); - PerfEventSource.StartStopEvents.CommandLineProcessingStop(); - - if (_commandLineOptions.Help || _commandLineOptions.Version) + // Normalize instruction set format to include implied +. + string[] instructionSetParamsInput = _command.InstructionSet.Split(","); + for (int i = 0; i < instructionSetParamsInput.Length; i++) { - return; - } + string instructionSet = instructionSetParamsInput[i]; - if (_commandLineOptions.WaitForDebugger) - { - Console.WriteLine(SR.WaitingForDebuggerAttach); - Console.ReadLine(); - } + if (String.IsNullOrEmpty(instructionSet)) + throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, "")); - if (_commandLineOptions.CompileBubbleGenerics) - { - if (!_commandLineOptions.CompositeOrInputBubble) + char firstChar = instructionSet[0]; + if ((firstChar != '+') && (firstChar != '-')) { - Console.WriteLine(SR.WarningIgnoringBubbleGenerics); - _commandLineOptions.CompileBubbleGenerics = false; + instructionSet = "+" + instructionSet; } + instructionSetParams.Add(instructionSet); } - _optimizationMode = OptimizationMode.None; - if (_commandLineOptions.OptimizeDisabled) + Dictionary instructionSetSpecification = new Dictionary(); + foreach (string instructionSetSpecifier in instructionSetParams) { - if (_commandLineOptions.Optimize || _commandLineOptions.OptimizeSpace || _commandLineOptions.OptimizeTime) - Console.WriteLine(SR.WarningOverridingOptimize); - } - else if (_commandLineOptions.OptimizeSpace) - { - if (_commandLineOptions.OptimizeTime) - Console.WriteLine(SR.WarningOverridingOptimizeSpace); - _optimizationMode = OptimizationMode.PreferSize; - } - else if (_commandLineOptions.OptimizeTime) - _optimizationMode = OptimizationMode.PreferSpeed; - else if (_commandLineOptions.Optimize) - _optimizationMode = OptimizationMode.Blended; - - foreach (var input in _commandLineOptions.InputFilePaths) - Helpers.AppendExpandedPaths(_inputFilePaths, input, true); + string instructionSet = instructionSetSpecifier.Substring(1); - foreach (var input in _commandLineOptions.UnrootedInputFilePaths) - Helpers.AppendExpandedPaths(_unrootedInputFilePaths, input, true); - - foreach (var reference in _commandLineOptions.ReferenceFilePaths) - Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false); - - foreach (var reference in _commandLineOptions.InputBubbleReferenceFilePaths) - Helpers.AppendExpandedPaths(_inputbubblereferenceFilePaths, reference, false); - - - int alignment = _commandLineOptions.CustomPESectionAlignment; - if (alignment != 0) - { - // Must be a power of two and >= 4096 - if (alignment < 4096 || (alignment & (alignment - 1)) != 0) - throw new CommandLineException(SR.InvalidCustomPESectionAlignment); - } - - if (_commandLineOptions.MethodLayout != null) - { - _methodLayout = _commandLineOptions.MethodLayout.ToLowerInvariant() switch + bool enabled = instructionSetSpecifier[0] == '+' ? true : false; + if (enabled) { - "defaultsort" => ReadyToRunMethodLayoutAlgorithm.DefaultSort, - "exclusiveweight" => ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight, - "hotcold" => ReadyToRunMethodLayoutAlgorithm.HotCold, - "hotwarmcold" => ReadyToRunMethodLayoutAlgorithm.HotWarmCold, - "callfrequency" => ReadyToRunMethodLayoutAlgorithm.CallFrequency, - "pettishansen" => ReadyToRunMethodLayoutAlgorithm.PettisHansen, - "random" => ReadyToRunMethodLayoutAlgorithm.Random, - _ => throw new CommandLineException(SR.InvalidMethodLayout) - }; - } - - if (_commandLineOptions.FileLayout != null) - { - _fileLayout = _commandLineOptions.FileLayout.ToLowerInvariant() switch + if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) + throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet)); + } + else { - "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort, - "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder, - _ => throw new CommandLineException(SR.InvalidFileLayout) - }; + if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) + throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet)); + } } - } - private string GetCompilerVersion() - { - return Assembly - .GetExecutingAssembly() - .GetCustomAttribute() - .InformationalVersion; - } + instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, + (string specifiedInstructionSet, string impliedInstructionSet) => + throw new CommandLineException(String.Format(SR.InstructionSetInvalidImplication, specifiedInstructionSet, impliedInstructionSet))); - public static TargetArchitecture GetTargetArchitectureFromArg(string archArg, out bool armelAbi) - { - armelAbi = false; - if (archArg.Equals("x86", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.X86; - else if (archArg.Equals("x64", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.X64; - else if (archArg.Equals("arm", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.ARM; - else if (archArg.Equals("armel", StringComparison.OrdinalIgnoreCase)) - { - armelAbi = true; - return TargetArchitecture.ARM; - } - else if (archArg.Equals("arm64", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.ARM64; - else - throw new CommandLineException(SR.TargetArchitectureUnsupported); - } + InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); - private void ConfigureTarget() + // Ready to run images are built with certain instruction sets that are optimistically assumed to be present + if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) { + // For ReadyToRun we set these hardware features as enabled always, as most + // of hardware in the wild supports them. Note that we do not indicate support for AVX, or any other + // instruction set which uses the VEX encodings as the presence of those makes otherwise acceptable + // code be unusable on hardware which does not support VEX encodings, as well as emulators that do not + // support AVX instructions. As the jit generates logic that depends on these features it will call + // notifyInstructionSetUsage, which will result in generation of a fixup to verify the behavior of + // code. // - // Set target Architecture and OS - // - if (_commandLineOptions.TargetArch != null) - { - _targetArchitecture = GetTargetArchitectureFromArg(_commandLineOptions.TargetArch, out _armelAbi); - } - if (_commandLineOptions.TargetOS != null) - { - if (_commandLineOptions.TargetOS.Equals("windows", StringComparison.OrdinalIgnoreCase)) - _targetOS = TargetOS.Windows; - else if (_commandLineOptions.TargetOS.Equals("linux", StringComparison.OrdinalIgnoreCase)) - _targetOS = TargetOS.Linux; - else if (_commandLineOptions.TargetOS.Equals("osx", StringComparison.OrdinalIgnoreCase)) - _targetOS = TargetOS.OSX; - else if (_commandLineOptions.TargetOS.Equals("freebsd", StringComparison.OrdinalIgnoreCase)) - _targetOS = TargetOS.FreeBSD; - else - throw new CommandLineException(SR.TargetOSUnsupported); - } + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); } - private InstructionSetSupport ConfigureInstructionSetSupport() - { - InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); - - // Ready to run images are built with certain instruction set baselines - if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) - { - instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication - } - else if (_targetArchitecture == TargetArchitecture.ARM64) - { - if (_targetOS == TargetOS.OSX) - { - // For osx-arm64 we know that apple-m1 is a baseline - instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); - } - else - { - instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication - } - } - - if (_commandLineOptions.InstructionSet != null) - { - List instructionSetParams = new List(); - - // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = _commandLineOptions.InstructionSet.Split(","); - for (int i = 0; i < instructionSetParamsInput.Length; i++) - { - string instructionSet = instructionSetParamsInput[i]; - - if (String.IsNullOrEmpty(instructionSet)) - throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, "")); - - char firstChar = instructionSet[0]; - if ((firstChar != '+') && (firstChar != '-')) - { - instructionSet = "+" + instructionSet; - } - instructionSetParams.Add(instructionSet); - } - - Dictionary instructionSetSpecification = new Dictionary(); - foreach (string instructionSetSpecifier in instructionSetParams) - { - string instructionSet = instructionSetSpecifier.Substring(1); + optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, + (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); + optimisticInstructionSet.Remove(unsupportedInstructionSet); + optimisticInstructionSet.Add(supportedInstructionSet); - bool enabled = instructionSetSpecifier[0] == '+' ? true : false; - if (enabled) - { - if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) - throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet)); - } - else - { - if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) - throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet)); - } - } - } + return new InstructionSetSupport(supportedInstructionSet, + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), + _targetArchitecture); + } - instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, - (string specifiedInstructionSet, string impliedInstructionSet) => - throw new CommandLineException(String.Format(SR.InstructionSetInvalidImplication, specifiedInstructionSet, impliedInstructionSet))); + private ulong ConfigureImageBase(TargetDetails targetDetails) + { + bool is64BitTarget = targetDetails.PointerSize == sizeof(long); - InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); + if (_command.ImageBase != null) + return is64BitTarget ? Convert.ToUInt64(_command.ImageBase, 16) : Convert.ToUInt32(_command.ImageBase, 16); - // Ready to run images are built with certain instruction sets that are optimistically assumed to be present - if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) - { - // For ReadyToRun we set these hardware features as enabled always, as most - // of hardware in the wild supports them. Note that we do not indicate support for AVX, or any other - // instruction set which uses the VEX encodings as the presence of those makes otherwise acceptable - // code be unusable on hardware which does not support VEX encodings, as well as emulators that do not - // support AVX instructions. As the jit generates logic that depends on these features it will call - // notifyInstructionSetUsage, which will result in generation of a fixup to verify the behavior of - // code. - // - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); - } + return is64BitTarget ? PEWriter.PE64HeaderConstants.DllImageBase : PEWriter.PE32HeaderConstants.ImageBase; + } - optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, - (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); - optimisticInstructionSet.Remove(unsupportedInstructionSet); - optimisticInstructionSet.Add(supportedInstructionSet); + private int Run(string[] args) + { + InitializeDefaultOptions(); - return new InstructionSetSupport(supportedInstructionSet, - unsupportedInstructionSet, - optimisticInstructionSet, - InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), - _targetArchitecture); - } + ProcessCommandLine(args); - private void ConfigureImageBase(TargetDetails targetDetails) + if (_command.Help) { - bool is64BitTarget = targetDetails.PointerSize == sizeof(long); - - if (_commandLineOptions.ImageBase != null) - _imageBase = is64BitTarget ? Convert.ToUInt64(_commandLineOptions.ImageBase, 16) : Convert.ToUInt32(_commandLineOptions.ImageBase, 16); - else - _imageBase = is64BitTarget ? PEWriter.PE64HeaderConstants.DllImageBase : PEWriter.PE32HeaderConstants.ImageBase; + Console.WriteLine(_command.HelpText); + return 1; } - private int Run(string[] args) + if (_command.Version) { - InitializeDefaultOptions(); - - ProcessCommandLine(args); - - if (_commandLineOptions.Help) - { - Console.WriteLine(_commandLineOptions.HelpText); - return 1; - } - - if (_commandLineOptions.Version) - { - string version = GetCompilerVersion(); - Console.WriteLine(version); - return 0; - } - - if (_commandLineOptions.OutputFilePath == null && !_commandLineOptions.OutNearInput) - throw new CommandLineException(SR.MissingOutputFile); + string version = GetCompilerVersion(); + Console.WriteLine(version); + return 0; + } - if (_commandLineOptions.SingleFileCompilation && !_commandLineOptions.OutNearInput) - throw new CommandLineException(SR.MissingOutNearInput); + if (_command.OutputFilePath == null && !_command.OutNearInput) + throw new CommandLineException(SR.MissingOutputFile); - ConfigureTarget(); - InstructionSetSupport instructionSetSupport = ConfigureInstructionSetSupport(); + if (_command.SingleFileCompilation && !_command.OutNearInput) + throw new CommandLineException(SR.MissingOutNearInput); - SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; + ConfigureTarget(); + InstructionSetSupport instructionSetSupport = ConfigureInstructionSetSupport(); - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); + SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - ConfigureImageBase(targetDetails); + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); - bool versionBubbleIncludesCoreLib = false; - if (_commandLineOptions.InputBubble) - { - versionBubbleIncludesCoreLib = true; - } - else + bool versionBubbleIncludesCoreLib = false; + if (_command.InputBubble) + { + versionBubbleIncludesCoreLib = true; + } + else + { + if (!_command.SingleFileCompilation) { - if (!_commandLineOptions.SingleFileCompilation) + foreach (var inputFile in _inputFilePaths) { - foreach (var inputFile in _inputFilePaths) + if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) { - if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) - { - versionBubbleIncludesCoreLib = true; - break; - } + versionBubbleIncludesCoreLib = true; + break; } } - if (!versionBubbleIncludesCoreLib) + } + if (!versionBubbleIncludesCoreLib) + { + foreach (var inputFile in _unrootedInputFilePaths) { - foreach (var inputFile in _unrootedInputFilePaths) + if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) { - if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) - { - versionBubbleIncludesCoreLib = true; - break; - } + versionBubbleIncludesCoreLib = true; + break; } } } + } - // - // Initialize type system context - // - _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib); + // + // Initialize type system context + // + _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib); - string compositeRootPath = _commandLineOptions.CompositeRootPath; + string compositeRootPath = _command.CompositeRootPath; - // Collections for already loaded modules - Dictionary inputFilePaths = new Dictionary(); - Dictionary unrootedInputFilePaths = new Dictionary(); - HashSet versionBubbleModulesHash = new HashSet(); + // Collections for already loaded modules + Dictionary inputFilePaths = new Dictionary(); + Dictionary unrootedInputFilePaths = new Dictionary(); + HashSet versionBubbleModulesHash = new HashSet(); - using (PerfEventSource.StartStopEvents.LoadingEvents()) - { - // - // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since - // some tests contain a mixture of both managed and native binaries. - // - // See: https://github.com/dotnet/corert/issues/2785 - // - // When we undo this hack, replace the foreach with - // typeSystemContext.InputFilePaths = inFilePaths; - // + using (PerfEventSource.StartStopEvents.LoadingEvents()) + { + // + // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since + // some tests contain a mixture of both managed and native binaries. + // + // See: https://github.com/dotnet/corert/issues/2785 + // + // When we undo this hack, replace the foreach with + // typeSystemContext.InputFilePaths = inFilePaths; + // - foreach (var inputFile in _inputFilePaths) + foreach (var inputFile in _inputFilePaths) + { + try { - try + var module = _typeSystemContext.GetModuleFromPath(inputFile.Value); + if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0 + && module.PEReader.TryGetReadyToRunHeader(out int _)) { - var module = _typeSystemContext.GetModuleFromPath(inputFile.Value); - if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0 - && module.PEReader.TryGetReadyToRunHeader(out int _)) - { - Console.WriteLine(SR.IgnoringCompositeImage, inputFile.Value); - continue; - } - _allInputFilePaths.Add(inputFile.Key, inputFile.Value); - inputFilePaths.Add(inputFile.Key, inputFile.Value); - _referenceableModules.Add(module); - if (compositeRootPath == null) - { - compositeRootPath = Path.GetDirectoryName(inputFile.Value); - } + Console.WriteLine(SR.IgnoringCompositeImage, inputFile.Value); + continue; } - catch (TypeSystemException.BadImageFormatException) + _allInputFilePaths.Add(inputFile.Key, inputFile.Value); + inputFilePaths.Add(inputFile.Key, inputFile.Value); + _referenceableModules.Add(module); + if (compositeRootPath == null) { - // Keep calm and carry on. + compositeRootPath = Path.GetDirectoryName(inputFile.Value); } } + catch (TypeSystemException.BadImageFormatException) + { + // Keep calm and carry on. + } + } - foreach (var unrootedInputFile in _unrootedInputFilePaths) + foreach (var unrootedInputFile in _unrootedInputFilePaths) + { + try { - try + var module = _typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); + if (!_allInputFilePaths.ContainsKey(unrootedInputFile.Key)) { - var module = _typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); - if (!_allInputFilePaths.ContainsKey(unrootedInputFile.Key)) + _allInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); + unrootedInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); + _referenceableModules.Add(module); + if (compositeRootPath == null) { - _allInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); - unrootedInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); - _referenceableModules.Add(module); - if (compositeRootPath == null) - { - compositeRootPath = Path.GetDirectoryName(unrootedInputFile.Value); - } + compositeRootPath = Path.GetDirectoryName(unrootedInputFile.Value); } } - catch (TypeSystemException.BadImageFormatException) - { - // Keep calm and carry on. - } } + catch (TypeSystemException.BadImageFormatException) + { + // Keep calm and carry on. + } + } + + CheckManagedCppInputFiles(_allInputFilePaths.Values); - CheckManagedCppInputFiles(_allInputFilePaths.Values); + _typeSystemContext.InputFilePaths = _allInputFilePaths; + _typeSystemContext.ReferenceFilePaths = _referenceFilePaths; - _typeSystemContext.InputFilePaths = _allInputFilePaths; - _typeSystemContext.ReferenceFilePaths = _referenceFilePaths; + if (_typeSystemContext.InputFilePaths.Count == 0) + { + if (_command.InputFilePaths.Count > 0) + { + Console.WriteLine(SR.InputWasNotLoadable); + return 2; + } + throw new CommandLineException(SR.NoInputFiles); + } - if (_typeSystemContext.InputFilePaths.Count == 0) + foreach (var referenceFile in _referenceFilePaths.Values) + { + try { - if (_commandLineOptions.InputFilePaths.Count > 0) + EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile, throwOnFailureToLoad: false); + if (module == null) + continue; + + _referenceableModules.Add(module); + if (_command.InputBubble && _inputbubblereferenceFilePaths.Count == 0) { - Console.WriteLine(SR.InputWasNotLoadable); - return 2; + // In large version bubble mode add reference paths to the compilation group + // Consider bubble as large if no explicit bubble references were passed + versionBubbleModulesHash.Add(module); } - throw new CommandLineException(SR.NoInputFiles); } + catch { } // Ignore non-managed pe files + } - foreach (var referenceFile in _referenceFilePaths.Values) + if (_command.InputBubble) + { + foreach (var referenceFile in _inputbubblereferenceFilePaths.Values) { try { EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile, throwOnFailureToLoad: false); + if (module == null) continue; - _referenceableModules.Add(module); - if (_commandLineOptions.InputBubble && _inputbubblereferenceFilePaths.Count == 0) - { - // In large version bubble mode add reference paths to the compilation group - // Consider bubble as large if no explicit bubble references were passed - versionBubbleModulesHash.Add(module); - } + versionBubbleModulesHash.Add(module); } catch { } // Ignore non-managed pe files } - - if (_commandLineOptions.InputBubble) - { - foreach (var referenceFile in _inputbubblereferenceFilePaths.Values) - { - try - { - EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile, throwOnFailureToLoad: false); - - if (module == null) - continue; - - versionBubbleModulesHash.Add(module); - } - catch { } // Ignore non-managed pe files - } - } } + } - string systemModuleName = _commandLineOptions.SystemModule ?? DefaultSystemModule; - _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName)); - CompilerTypeSystemContext typeSystemContext = _typeSystemContext; + string systemModuleName = _command.SystemModule ?? Helpers.DefaultSystemModule; + _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName)); + CompilerTypeSystemContext typeSystemContext = _typeSystemContext; - if (_commandLineOptions.SingleFileCompilation) + if (_command.SingleFileCompilation) + { + var singleCompilationInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var inputFile in inputFilePaths) { - var singleCompilationInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + var singleCompilationVersionBubbleModulesHash = new HashSet(versionBubbleModulesHash); - foreach (var inputFile in inputFilePaths) + singleCompilationInputFilePaths.Clear(); + singleCompilationInputFilePaths.Add(inputFile.Key, inputFile.Value); + typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; + + if (!_command.InputBubble) { - var singleCompilationVersionBubbleModulesHash = new HashSet(versionBubbleModulesHash); + bool singleCompilationVersionBubbleIncludesCoreLib = versionBubbleIncludesCoreLib || (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0); - singleCompilationInputFilePaths.Clear(); - singleCompilationInputFilePaths.Add(inputFile.Key, inputFile.Value); + typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext); typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; - - if (!_commandLineOptions.InputBubble) - { - bool singleCompilationVersionBubbleIncludesCoreLib = versionBubbleIncludesCoreLib || (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0); - - typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext); - typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; - typeSystemContext.ReferenceFilePaths = _referenceFilePaths; - typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName)); - } - - RunSingleCompilation(singleCompilationInputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, singleCompilationVersionBubbleModulesHash, typeSystemContext); + typeSystemContext.ReferenceFilePaths = _referenceFilePaths; + typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName)); } - // In case of inputbubble ni.dll are created as ni.dll.tmp in order to not interfere with crossgen2, move them all to ni.dll - // See https://github.com/dotnet/runtime/issues/55663#issuecomment-898161751 for more details - if (_commandLineOptions.InputBubble) - { - foreach (var inputFile in inputFilePaths) - { - var tmpOutFile = inputFile.Value.Replace(".dll", ".ni.dll.tmp"); - var outFile = inputFile.Value.Replace(".dll", ".ni.dll"); - Console.WriteLine($@"Moving R2R PE file: {tmpOutFile} to {outFile}"); - System.IO.File.Move(tmpOutFile, outFile); - } - } + RunSingleCompilation(singleCompilationInputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, singleCompilationVersionBubbleModulesHash, typeSystemContext); } - else + + // In case of inputbubble ni.dll are created as ni.dll.tmp in order to not interfere with crossgen2, move them all to ni.dll + // See https://github.com/dotnet/runtime/issues/55663#issuecomment-898161751 for more details + if (_command.InputBubble) { - RunSingleCompilation(inputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, versionBubbleModulesHash, typeSystemContext); + foreach (var inputFile in inputFilePaths) + { + var tmpOutFile = inputFile.Value.Replace(".dll", ".ni.dll.tmp"); + var outFile = inputFile.Value.Replace(".dll", ".ni.dll"); + Console.WriteLine($@"Moving R2R PE file: {tmpOutFile} to {outFile}"); + System.IO.File.Move(tmpOutFile, outFile); + } } - - return 0; + } + else + { + RunSingleCompilation(inputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, versionBubbleModulesHash, typeSystemContext); } - private void RunSingleCompilation(Dictionary inFilePaths, InstructionSetSupport instructionSetSupport, string compositeRootPath, Dictionary unrootedInputFilePaths, HashSet versionBubbleModulesHash, CompilerTypeSystemContext typeSystemContext) + return 0; + } + + private void RunSingleCompilation(Dictionary inFilePaths, InstructionSetSupport instructionSetSupport, string compositeRootPath, Dictionary unrootedInputFilePaths, HashSet versionBubbleModulesHash, CompilerTypeSystemContext typeSystemContext) + { + // + // Initialize output filename + // + string inFilePath = inFilePaths.First().Value; + string inputFileExtension = Path.GetExtension(inFilePath); + string nearOutFilePath = inputFileExtension switch { - // - // Initialize output filename - // - string inFilePath = inFilePaths.First().Value; - string inputFileExtension = Path.GetExtension(inFilePath); - string nearOutFilePath = inputFileExtension switch - { - ".dll" => Path.ChangeExtension(inFilePath, - _commandLineOptions.SingleFileCompilation && _commandLineOptions.InputBubble - ? ".ni.dll.tmp" - : ".ni.dll"), - ".exe" => Path.ChangeExtension(inFilePath, - _commandLineOptions.SingleFileCompilation && _commandLineOptions.InputBubble - ? ".ni.exe.tmp" - : ".ni.exe"), - _ => throw new CommandLineException(string.Format(SR.UnsupportedInputFileExtension, inputFileExtension)) - }; - string outFile = _commandLineOptions.OutNearInput ? nearOutFilePath : _commandLineOptions.OutputFilePath; - - using (PerfEventSource.StartStopEvents.CompilationEvents()) + ".dll" => Path.ChangeExtension(inFilePath, + _command.SingleFileCompilation && _command.InputBubble + ? ".ni.dll.tmp" + : ".ni.dll"), + ".exe" => Path.ChangeExtension(inFilePath, + _command.SingleFileCompilation && _command.InputBubble + ? ".ni.exe.tmp" + : ".ni.exe"), + _ => throw new CommandLineException(string.Format(SR.UnsupportedInputFileExtension, inputFileExtension)) + }; + string outFile = _command.OutNearInput ? nearOutFilePath : _command.OutputFilePath; + + using (PerfEventSource.StartStopEvents.CompilationEvents()) + { + ICompilation compilation; + using (PerfEventSource.StartStopEvents.LoadingEvents()) { - ICompilation compilation; - using (PerfEventSource.StartStopEvents.LoadingEvents()) - { - List inputModules = new List(); - List rootingModules = new List(); - HashSet crossModuleInlineableCode = new HashSet(); - - foreach (var inputFile in inFilePaths) - { - EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); - inputModules.Add(module); - rootingModules.Add(module); - versionBubbleModulesHash.Add(module); + List inputModules = new List(); + List rootingModules = new List(); + HashSet crossModuleInlineableCode = new HashSet(); + foreach (var inputFile in inFilePaths) + { + EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); + inputModules.Add(module); + rootingModules.Add(module); + versionBubbleModulesHash.Add(module); - if (!_commandLineOptions.CompositeOrInputBubble) - { - break; - } - } - foreach (var unrootedInputFile in unrootedInputFilePaths) + if (!_command.CompositeOrInputBubble) { - EcmaModule module = typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); - inputModules.Add(module); - versionBubbleModulesHash.Add(module); + break; } + } + + foreach (var unrootedInputFile in unrootedInputFilePaths) + { + EcmaModule module = typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); + inputModules.Add(module); + versionBubbleModulesHash.Add(module); + } - if (_commandLineOptions.CrossModuleInlining != null) + if (_command.CrossModuleInlining != null) + { + foreach (var crossModulePgoAssemblyName in _command.CrossModuleInlining) { - foreach (var crossModulePgoAssemblyName in _commandLineOptions.CrossModuleInlining) + foreach (var module in _referenceableModules) { - foreach (var module in _referenceableModules) + if (!versionBubbleModulesHash.Contains(module)) { - if (!versionBubbleModulesHash.Contains(module)) + if (crossModulePgoAssemblyName == "*" || + (String.Compare(crossModulePgoAssemblyName, module.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase) == 0)) { - if (crossModulePgoAssemblyName == "*" || - (String.Compare(crossModulePgoAssemblyName, module.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase) == 0)) - { - crossModuleInlineableCode.Add((EcmaModule)module); - } + crossModuleInlineableCode.Add((EcmaModule)module); } } } } + } - // - // Initialize compilation group and compilation roots - // + // + // Initialize compilation group and compilation roots + // - // Single method mode? - MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); + // Single method mode? + MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); - var logger = new Logger(Console.Out, _commandLineOptions.Verbose); + var logger = new Logger(Console.Out, _command.Verbose); - List mibcFiles = new List(); - foreach (var file in _commandLineOptions.MibcFilePaths) - { - mibcFiles.Add(file); - } + List mibcFiles = new List(); + foreach (var file in _command.MibcFilePaths) + { + mibcFiles.Add(file); + } - List versionBubbleModules = new List(versionBubbleModulesHash); + List versionBubbleModules = new List(versionBubbleModulesHash); - if (!_commandLineOptions.Composite && inputModules.Count != 1) - { - throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); - } + if (!_command.Composite && inputModules.Count != 1) + { + throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); + } - ReadyToRunCompilationModuleGroupBase compilationGroup; - List compilationRoots = new List(); - ReadyToRunCompilationModuleGroupConfig groupConfig = new ReadyToRunCompilationModuleGroupConfig(); - groupConfig.Context = typeSystemContext; - groupConfig.IsCompositeBuildMode = _commandLineOptions.Composite; - groupConfig.IsInputBubble = _commandLineOptions.InputBubble; - groupConfig.CompilationModuleSet = inputModules; - groupConfig.VersionBubbleModuleSet = versionBubbleModules; - groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = _commandLineOptions.CompileBubbleGenerics; - groupConfig.CrossModuleGenericCompilation = crossModuleInlineableCode.Count > 0; - groupConfig.CrossModuleInlining = groupConfig.CrossModuleGenericCompilation; // Currently we set these flags to the same values - groupConfig.CrossModuleInlineable = crossModuleInlineableCode; - groupConfig.CompileAllPossibleCrossModuleCode = false; - - // Handle non-local generics command line option - ModuleDesc nonLocalGenericsHome = _commandLineOptions.CompileBubbleGenerics ? inputModules[0] : null; - if (_commandLineOptions.NonLocalGenericsModule == "*") - { - groupConfig.CompileAllPossibleCrossModuleCode = true; - nonLocalGenericsHome = inputModules[0]; - } - else if (_commandLineOptions.NonLocalGenericsModule == "") + ReadyToRunCompilationModuleGroupBase compilationGroup; + List compilationRoots = new List(); + ReadyToRunCompilationModuleGroupConfig groupConfig = new ReadyToRunCompilationModuleGroupConfig(); + groupConfig.Context = typeSystemContext; + groupConfig.IsCompositeBuildMode = _command.Composite; + groupConfig.IsInputBubble = _command.InputBubble; + groupConfig.CompilationModuleSet = inputModules; + groupConfig.VersionBubbleModuleSet = versionBubbleModules; + groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = _command.CompileBubbleGenerics; + groupConfig.CrossModuleGenericCompilation = crossModuleInlineableCode.Count > 0; + groupConfig.CrossModuleInlining = groupConfig.CrossModuleGenericCompilation; // Currently we set these flags to the same values + groupConfig.CrossModuleInlineable = crossModuleInlineableCode; + groupConfig.CompileAllPossibleCrossModuleCode = false; + + // Handle non-local generics command line option + ModuleDesc nonLocalGenericsHome = _command.CompileBubbleGenerics ? inputModules[0] : null; + if (_command.NonLocalGenericsModule == "*") + { + groupConfig.CompileAllPossibleCrossModuleCode = true; + nonLocalGenericsHome = inputModules[0]; + } + else if (_command.NonLocalGenericsModule == "") + { + // Nothing was specified + } + else + { + bool matchFound = false; + + // Allow module to be specified by assembly name or by filename + if (_command.NonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + _command.NonLocalGenericsModule = Path.GetFileNameWithoutExtension(_command.NonLocalGenericsModule); + foreach (var module in inputModules) { - // Nothing was specified + if (String.Compare(module.Assembly.GetName().Name, _command.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + { + matchFound = true; + nonLocalGenericsHome = module; + groupConfig.CompileAllPossibleCrossModuleCode = true; + break; + } } - else - { - bool matchFound = false; - // Allow module to be specified by assembly name or by filename - if (_commandLineOptions.NonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) - _commandLineOptions.NonLocalGenericsModule = Path.GetFileNameWithoutExtension(_commandLineOptions.NonLocalGenericsModule); - foreach (var module in inputModules) + if (!matchFound) + { + foreach (var module in _referenceableModules) { - if (String.Compare(module.Assembly.GetName().Name, _commandLineOptions.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + if (String.Compare(module.Assembly.GetName().Name, _command.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) { matchFound = true; - nonLocalGenericsHome = module; - groupConfig.CompileAllPossibleCrossModuleCode = true; break; } } if (!matchFound) { - foreach (var module in _referenceableModules) - { - if (String.Compare(module.Assembly.GetName().Name, _commandLineOptions.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) - { - matchFound = true; - break; - } - } - - if (!matchFound) - { - throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, _commandLineOptions.NonLocalGenericsModule)); - } + throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, _command.NonLocalGenericsModule)); } } + } - if (singleMethod != null) - { - // Compiling just a single method - compilationGroup = new SingleMethodCompilationModuleGroup( - groupConfig, - singleMethod); - compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); - } - else if (_commandLineOptions.CompileNoMethods) - { - compilationGroup = new NoMethodsCompilationModuleGroup(groupConfig); - } - else - { - // Single assembly compilation. - compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(groupConfig); - } + if (singleMethod != null) + { + // Compiling just a single method + compilationGroup = new SingleMethodCompilationModuleGroup( + groupConfig, + singleMethod); + compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); + } + else if (_command.CompileNoMethods) + { + compilationGroup = new NoMethodsCompilationModuleGroup(groupConfig); + } + else + { + // Single assembly compilation. + compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(groupConfig); + } - // Load any profiles generated by method call chain analyis - CallChainProfile jsonProfile = null; + // Load any profiles generated by method call chain analyis + CallChainProfile jsonProfile = null; - if (!string.IsNullOrEmpty(_commandLineOptions.CallChainProfileFile)) - { - jsonProfile = new CallChainProfile(_commandLineOptions.CallChainProfileFile, typeSystemContext, _referenceableModules); - } + if (!string.IsNullOrEmpty(_command.CallChainProfileFile)) + { + jsonProfile = new CallChainProfile(_command.CallChainProfileFile, typeSystemContext, _referenceableModules); + } - // Examine profile guided information as appropriate - MIbcProfileParser.MibcGroupParseRules parseRule; - if (nonLocalGenericsHome != null) - { - parseRule = MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule2; - } - else - { - parseRule = MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule1; - } + // Examine profile guided information as appropriate + MIbcProfileParser.MibcGroupParseRules parseRule; + if (nonLocalGenericsHome != null) + { + parseRule = MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule2; + } + else + { + parseRule = MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule1; + } - ProfileDataManager profileDataManager = - new ProfileDataManager(logger, - _referenceableModules, - inputModules, - versionBubbleModules, - crossModuleInlineableCode, - nonLocalGenericsHome, - mibcFiles, - parseRule, - jsonProfile, - typeSystemContext, - compilationGroup, - _commandLineOptions.EmbedPgoData, - crossModuleInlineableCode.Count == 0 ? compilationGroup.VersionsWithMethodBody : compilationGroup.CrossModuleInlineable); - - compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, _commandLineOptions.Partial); - - if ((singleMethod == null) && !_commandLineOptions.CompileNoMethods) + ProfileDataManager profileDataManager = + new ProfileDataManager(logger, + _referenceableModules, + inputModules, + versionBubbleModules, + crossModuleInlineableCode, + nonLocalGenericsHome, + mibcFiles, + parseRule, + jsonProfile, + typeSystemContext, + compilationGroup, + _command.EmbedPgoData, + crossModuleInlineableCode.Count == 0 ? compilationGroup.VersionsWithMethodBody : compilationGroup.CrossModuleInlineable); + + compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, _command.Partial); + + if ((singleMethod == null) && !_command.CompileNoMethods) + { + // For normal compilations add compilation roots. + foreach (var module in rootingModules) { - // For normal compilations add compilation roots. - foreach (var module in rootingModules) - { - compilationRoots.Add(new ReadyToRunRootProvider( - module, - profileDataManager, - profileDrivenPartialNGen: _commandLineOptions.Partial)); + compilationRoots.Add(new ReadyToRunRootProvider( + module, + profileDataManager, + profileDrivenPartialNGen: _command.Partial)); - if (!_commandLineOptions.CompositeOrInputBubble) - { - break; - } + if (!_command.CompositeOrInputBubble) + { + break; } } - // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize - // or produce debuggable code if an explicit optimization level was not specified on the command line - OptimizationMode optimizationMode = _optimizationMode; - if (optimizationMode == OptimizationMode.None && !_commandLineOptions.OptimizeDisabled && !_commandLineOptions.Composite) - { - System.Diagnostics.Debug.Assert(inputModules.Count == 1); - optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended; - } + } + // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize + // or produce debuggable code if an explicit optimization level was not specified on the command line + OptimizationMode optimizationMode = _optimizationMode; + if (optimizationMode == OptimizationMode.None && !_command.OptimizeDisabled && !_command.Composite) + { + System.Diagnostics.Debug.Assert(inputModules.Count == 1); + optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended; + } - CompositeImageSettings compositeImageSettings = new CompositeImageSettings(); + CompositeImageSettings compositeImageSettings = new CompositeImageSettings(); - if (_commandLineOptions.CompositeKeyFile != null) + if (_command.CompositeKeyFile != null) + { + byte[] compositeStrongNameKey = File.ReadAllBytes(_command.CompositeKeyFile); + if (!IsValidPublicKey(compositeStrongNameKey)) { - byte[] compositeStrongNameKey = File.ReadAllBytes(_commandLineOptions.CompositeKeyFile); - if (!IsValidPublicKey(compositeStrongNameKey)) - { - throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey)); - } - - compositeImageSettings.PublicKey = compositeStrongNameKey.ToImmutableArray(); + throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey)); } - // - // Compile - // - - ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder( - typeSystemContext, compilationGroup, _allInputFilePaths.Values, compositeRootPath); - string compilationUnitPrefix = ""; - builder.UseCompilationUnitPrefix(compilationUnitPrefix); - - ILProvider ilProvider = new ReadyToRunILProvider(compilationGroup); - - DependencyTrackingLevel trackingLevel = _commandLineOptions.DgmlLogFileName == null ? - DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); - - NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); - nodeFactoryFlags.OptimizeAsyncMethods = _commandLineOptions.AsyncMethodOptimization; - - builder - .UseMapFile(_commandLineOptions.Map) - .UseMapCsvFile(_commandLineOptions.MapCsv) - .UsePdbFile(_commandLineOptions.Pdb, _commandLineOptions.PdbPath) - .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath, _commandLineOptions.PerfMapFormatVersion) - .UseProfileFile(jsonProfile != null) - .UseProfileData(profileDataManager) - .UseNodeFactoryOptimizationFlags(nodeFactoryFlags) - .FileLayoutAlgorithms(_methodLayout, _fileLayout) - .UseCompositeImageSettings(compositeImageSettings) - .UseJitPath(_commandLineOptions.JitPath) - .UseInstructionSetSupport(instructionSetSupport) - .UseCustomPESectionAlignment(_commandLineOptions.CustomPESectionAlignment) - .UseVerifyTypeAndFieldLayout(_commandLineOptions.VerifyTypeAndFieldLayout) - .GenerateOutputFile(outFile) - .UseImageBase(_imageBase) - .UseILProvider(ilProvider) - .UseBackendOptions(_commandLineOptions.CodegenOptions) - .UseLogger(logger) - .UseParallelism(_commandLineOptions.Parallelism) - .UseResilience(_commandLineOptions.Resilient) - .UseDependencyTracking(trackingLevel) - .UseCompilationRoots(compilationRoots) - .UseOptimizationMode(optimizationMode); - - if (_commandLineOptions.PrintReproInstructions) - builder.UsePrintReproInstructions(CreateReproArgumentString); - - compilation = builder.ToCompilation(); - + compositeImageSettings.PublicKey = compositeStrongNameKey.ToImmutableArray(); } - compilation.Compile(outFile); - if (_commandLineOptions.DgmlLogFileName != null) - compilation.WriteDependencyLog(_commandLineOptions.DgmlLogFileName); + // + // Compile + // - compilation.Dispose(); - } + ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder( + typeSystemContext, compilationGroup, _allInputFilePaths.Values, compositeRootPath); + string compilationUnitPrefix = ""; + builder.UseCompilationUnitPrefix(compilationUnitPrefix); + + ILProvider ilProvider = new ReadyToRunILProvider(compilationGroup); + + DependencyTrackingLevel trackingLevel = _command.DgmlLogFileName == null ? + DependencyTrackingLevel.None : (_command.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + + NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); + nodeFactoryFlags.OptimizeAsyncMethods = _command.AsyncMethodOptimization; + + builder + .UseMapFile(_command.Map) + .UseMapCsvFile(_command.MapCsv) + .UsePdbFile(_command.Pdb, _command.PdbPath) + .UsePerfMapFile(_command.PerfMap, _command.PerfMapPath, _command.PerfMapFormatVersion) + .UseProfileFile(jsonProfile != null) + .UseProfileData(profileDataManager) + .UseNodeFactoryOptimizationFlags(nodeFactoryFlags) + .FileLayoutAlgorithms(_methodLayout, _fileLayout) + .UseCompositeImageSettings(compositeImageSettings) + .UseJitPath(_command.JitPath) + .UseInstructionSetSupport(instructionSetSupport) + .UseCustomPESectionAlignment(_command.CustomPESectionAlignment) + .UseVerifyTypeAndFieldLayout(_command.VerifyTypeAndFieldLayout) + .GenerateOutputFile(outFile) + .UseImageBase(ConfigureImageBase(targetDetails)) + .UseILProvider(ilProvider) + .UseBackendOptions(_command.CodegenOptions) + .UseLogger(logger) + .UseParallelism(_command.Parallelism) + .UseResilience(_command.Resilient) + .UseDependencyTracking(trackingLevel) + .UseCompilationRoots(compilationRoots) + .UseOptimizationMode(optimizationMode); + + if (_command.PrintReproInstructions) + builder.UsePrintReproInstructions(CreateReproArgumentString); + + compilation = builder.ToCompilation(); + + } + compilation.Compile(outFile); + + if (_command.DgmlLogFileName != null) + compilation.WriteDependencyLog(_command.DgmlLogFileName); + + compilation.Dispose(); } + } - private void CheckManagedCppInputFiles(IEnumerable inputPaths) + private void CheckManagedCppInputFiles(IEnumerable inputPaths) + { + foreach (string inputFilePath in inputPaths) { - foreach (string inputFilePath in inputPaths) + EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFilePath); + if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0) { - EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFilePath); - if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0) - { - throw new CommandLineException(string.Format(SR.ManagedCppNotSupported, inputFilePath)); - } + throw new CommandLineException(string.Format(SR.ManagedCppNotSupported, inputFilePath)); } } + } + + private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) + { + ModuleDesc systemModule = context.SystemModule; - private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) + TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => { - ModuleDesc systemModule = context.SystemModule; + return (MetadataType)context.GetCanonType(typeDefName) + ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); + }); + if (foundType == null) + throw new CommandLineException(string.Format(SR.TypeNotFound, typeName)); - TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => - { - return (MetadataType)context.GetCanonType(typeDefName) - ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); - }); - if (foundType == null) - throw new CommandLineException(string.Format(SR.TypeNotFound, typeName)); + return foundType; + } - return foundType; - } + private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) + { + if (_command.SingleMethodName == null && _command.SingleMethodTypeName == null && _command.SingleMethodGenericArg == null) + return null; - private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) - { - if (_commandLineOptions.SingleMethodName == null && _commandLineOptions.SingleMethodTypeName == null && _commandLineOptions.SingleMethodGenericArg == null) - return null; + if (_command.SingleMethodName == null || _command.SingleMethodTypeName == null) + throw new CommandLineException(SR.TypeAndMethodNameNeeded); - if (_commandLineOptions.SingleMethodName == null || _commandLineOptions.SingleMethodTypeName == null) - throw new CommandLineException(SR.TypeAndMethodNameNeeded); + TypeDesc owningType = FindType(context, _command.SingleMethodTypeName); - TypeDesc owningType = FindType(context, _commandLineOptions.SingleMethodTypeName); + // TODO: allow specifying signature to distinguish overloads + MethodDesc method = null; + bool printMethodList = false; + int curIndex = 0; + foreach (var searchMethod in owningType.GetMethods()) + { + if (searchMethod.Name != _command.SingleMethodName) + continue; - // TODO: allow specifying signature to distinguish overloads - MethodDesc method = null; - bool printMethodList = false; - int curIndex = 0; - foreach (var searchMethod in owningType.GetMethods()) + curIndex++; + if (_command.SingleMethodIndex != 0) { - if (searchMethod.Name != _commandLineOptions.SingleMethodName) - continue; - - curIndex++; - if (_commandLineOptions.SingleMethodIndex != 0) - { - if (curIndex == _commandLineOptions.SingleMethodIndex) - { - method = searchMethod; - break; - } - } - else + if (curIndex == _command.SingleMethodIndex) { - if (method == null) - { - method = searchMethod; - } - else - { - printMethodList = true; - } + method = searchMethod; + break; } } - - if (printMethodList) + else { - curIndex = 0; - foreach (var searchMethod in owningType.GetMethods()) + if (method == null) { - if (searchMethod.Name != _commandLineOptions.SingleMethodName) - continue; - - curIndex++; - Console.WriteLine($"{curIndex} - {searchMethod}"); + method = searchMethod; + } + else + { + printMethodList = true; } - throw new CommandLineException(SR.SingleMethodIndexNeeded); - } - - if (method == null) - throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName)); - - if (method.HasInstantiation != (_commandLineOptions.SingleMethodGenericArg != null) || - (method.HasInstantiation && (method.Instantiation.Length != _commandLineOptions.SingleMethodGenericArg.Count))) - { - throw new CommandLineException( - string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName)); - } - - if (method.HasInstantiation) - { - List genericArguments = new List(); - foreach (var argString in _commandLineOptions.SingleMethodGenericArg) - genericArguments.Add(FindType(context, argString)); - method = method.MakeInstantiatedMethod(genericArguments.ToArray()); } - - return method; } - private static string CreateReproArgumentString(MethodDesc method) + if (printMethodList) { - StringBuilder sb = new StringBuilder(); - - var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)method.Context.SystemModule); - - sb.Append($"--singlemethodtypename \"{formatter.FormatName(method.OwningType, true)}\""); - sb.Append($" --singlemethodname \"{method.Name}\""); + curIndex = 0; + foreach (var searchMethod in owningType.GetMethods()) { - int curIndex = 0; - foreach (var searchMethod in method.OwningType.GetMethods()) - { - if (searchMethod.Name != method.Name) - continue; + if (searchMethod.Name != _command.SingleMethodName) + continue; - curIndex++; - if (searchMethod == method.GetMethodDefinition()) - { - sb.Append($" --singlemethodindex {curIndex}"); - } - } + curIndex++; + Console.WriteLine($"{curIndex} - {searchMethod}"); } - - for (int i = 0; i < method.Instantiation.Length; i++) - sb.Append($" --singlemethodgenericarg \"{formatter.FormatName(method.Instantiation[i], true)}\""); - - return sb.ToString(); + throw new CommandLineException(SR.SingleMethodIndexNeeded); } - private static bool DumpReproArguments(CodeGenerationFailedException ex) - { - Console.WriteLine(SR.DumpReproInstructions); - - MethodDesc failingMethod = ex.Method; - Console.WriteLine(CreateReproArgumentString(failingMethod)); - return false; - } + if (method == null) + throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, _command.SingleMethodName, _command.SingleMethodTypeName)); - private enum AlgorithmClass + if (method.HasInstantiation != (_command.SingleMethodGenericArg != null) || + (method.HasInstantiation && (method.Instantiation.Length != _command.SingleMethodGenericArg.Count))) { - Signature = 1, - Hash = 4, + throw new CommandLineException( + string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, _command.SingleMethodName, _command.SingleMethodTypeName)); } - private enum AlgorithmSubId + if (method.HasInstantiation) { - Sha1Hash = 4, - MacHash = 5, - RipeMdHash = 6, - RipeMd160Hash = 7, - Ssl3ShaMD5Hash = 8, - HmacHash = 9, - Tls1PrfHash = 10, - HashReplacOwfHash = 11, - Sha256Hash = 12, - Sha384Hash = 13, - Sha512Hash = 14, + List genericArguments = new List(); + foreach (var argString in _command.SingleMethodGenericArg) + genericArguments.Add(FindType(context, argString)); + method = method.MakeInstantiatedMethod(genericArguments.ToArray()); } - private struct AlgorithmId - { - // From wincrypt.h - private const int AlgorithmClassOffset = 13; - private const int AlgorithmClassMask = 0x7; - private const int AlgorithmSubIdOffset = 0; - private const int AlgorithmSubIdMask = 0x1ff; + return method; + } - private readonly uint _flags; + private static string CreateReproArgumentString(MethodDesc method) + { + StringBuilder sb = new StringBuilder(); - public const int RsaSign = 0x00002400; - public const int Sha = 0x00008004; + var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)method.Context.SystemModule); - public bool IsSet + sb.Append($"--singlemethodtypename \"{formatter.FormatName(method.OwningType, true)}\""); + sb.Append($" --singlemethodname \"{method.Name}\""); + { + int curIndex = 0; + foreach (var searchMethod in method.OwningType.GetMethods()) { - get { return _flags != 0; } - } + if (searchMethod.Name != method.Name) + continue; - public AlgorithmClass Class - { - get { return (AlgorithmClass)((_flags >> AlgorithmClassOffset) & AlgorithmClassMask); } + curIndex++; + if (searchMethod == method.GetMethodDefinition()) + { + sb.Append($" --singlemethodindex {curIndex}"); + } } + } - public AlgorithmSubId SubId - { - get { return (AlgorithmSubId)((_flags >> AlgorithmSubIdOffset) & AlgorithmSubIdMask); } - } + for (int i = 0; i < method.Instantiation.Length; i++) + sb.Append($" --singlemethodgenericarg \"{formatter.FormatName(method.Instantiation[i], true)}\""); - public AlgorithmId(uint flags) - { - _flags = flags; - } - } + return sb.ToString(); + } - private static ReadOnlySpan s_ecmaKey => new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 }; + private static bool DumpReproArguments(CodeGenerationFailedException ex) + { + Console.WriteLine(SR.DumpReproInstructions); - private const int SnPublicKeyBlobSize = 13; + MethodDesc failingMethod = ex.Method; + Console.WriteLine(CreateReproArgumentString(failingMethod)); + return false; + } + private enum AlgorithmClass + { + Signature = 1, + Hash = 4, + } + + private enum AlgorithmSubId + { + Sha1Hash = 4, + MacHash = 5, + RipeMdHash = 6, + RipeMd160Hash = 7, + Ssl3ShaMD5Hash = 8, + HmacHash = 9, + Tls1PrfHash = 10, + HashReplacOwfHash = 11, + Sha256Hash = 12, + Sha384Hash = 13, + Sha512Hash = 14, + } + + private struct AlgorithmId + { // From wincrypt.h - private const byte PublicKeyBlobId = 0x06; - private const byte PrivateKeyBlobId = 0x07; + private const int AlgorithmClassOffset = 13; + private const int AlgorithmClassMask = 0x7; + private const int AlgorithmSubIdOffset = 0; + private const int AlgorithmSubIdMask = 0x1ff; + + private readonly uint _flags; - // internal for testing - internal const int s_publicKeyHeaderSize = SnPublicKeyBlobSize - 1; + public const int RsaSign = 0x00002400; + public const int Sha = 0x00008004; - // From StrongNameInternal.cpp - // Checks to see if a public key is a valid instance of a PublicKeyBlob as - // defined in StongName.h - internal static bool IsValidPublicKey(byte[] blob) + public bool IsSet { - // The number of public key bytes must be at least large enough for the header and one byte of data. - if (blob.Length < s_publicKeyHeaderSize + 1) - { - return false; - } + get { return _flags != 0; } + } - // Check for the ECMA key, which does not obey the invariants checked below. - if (blob.AsSpan().SequenceEqual(s_ecmaKey)) - { - return true; - } + public AlgorithmClass Class + { + get { return (AlgorithmClass)((_flags >> AlgorithmClassOffset) & AlgorithmClassMask); } + } + + public AlgorithmSubId SubId + { + get { return (AlgorithmSubId)((_flags >> AlgorithmSubIdOffset) & AlgorithmSubIdMask); } + } - var blobReader = new BinaryReader(new MemoryStream(blob, writable: false)); + public AlgorithmId(uint flags) + { + _flags = flags; + } + } - // Signature algorithm ID - var sigAlgId = blobReader.ReadUInt32(); - // Hash algorithm ID - var hashAlgId = blobReader.ReadUInt32(); - // Size of public key data in bytes, not including the header - var publicKeySize = blobReader.ReadUInt32(); - // publicKeySize bytes of public key data - var publicKey = blobReader.ReadByte(); + private static ReadOnlySpan s_ecmaKey => new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 }; - // The number of public key bytes must be the same as the size of the header plus the size of the public key data. - if (blob.Length != s_publicKeyHeaderSize + publicKeySize) - { - return false; - } + private const int SnPublicKeyBlobSize = 13; - // The public key must be in the wincrypto PUBLICKEYBLOB format - if (publicKey != PublicKeyBlobId) - { - return false; - } + // From wincrypt.h + private const byte PublicKeyBlobId = 0x06; + private const byte PrivateKeyBlobId = 0x07; - var signatureAlgorithmId = new AlgorithmId(sigAlgId); - if (signatureAlgorithmId.IsSet && signatureAlgorithmId.Class != AlgorithmClass.Signature) - { - return false; - } + // internal for testing + internal const int s_publicKeyHeaderSize = SnPublicKeyBlobSize - 1; - var hashAlgorithmId = new AlgorithmId(hashAlgId); - if (hashAlgorithmId.IsSet && (hashAlgorithmId.Class != AlgorithmClass.Hash || hashAlgorithmId.SubId < AlgorithmSubId.Sha1Hash)) - { - return false; - } + // From StrongNameInternal.cpp + // Checks to see if a public key is a valid instance of a PublicKeyBlob as + // defined in StongName.h + internal static bool IsValidPublicKey(byte[] blob) + { + // The number of public key bytes must be at least large enough for the header and one byte of data. + if (blob.Length < s_publicKeyHeaderSize + 1) + { + return false; + } + // Check for the ECMA key, which does not obey the invariants checked below. + if (blob.AsSpan().SequenceEqual(s_ecmaKey)) + { return true; } + var blobReader = new BinaryReader(new MemoryStream(blob, writable: false)); - private static int Main(string[] args) + // Signature algorithm ID + var sigAlgId = blobReader.ReadUInt32(); + // Hash algorithm ID + var hashAlgId = blobReader.ReadUInt32(); + // Size of public key data in bytes, not including the header + var publicKeySize = blobReader.ReadUInt32(); + // publicKeySize bytes of public key data + var publicKey = blobReader.ReadByte(); + + // The number of public key bytes must be the same as the size of the header plus the size of the public key data. + if (blob.Length != s_publicKeyHeaderSize + publicKeySize) { -#if DEBUG - try - { - return new Program().Run(args); - } - catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) - { - throw new NotSupportedException(); // Unreachable - } -#else - try - { - return new Program().Run(args); - } - catch (Exception e) - { - Console.Error.WriteLine(string.Format(SR.ProgramError, e.Message)); - Console.Error.WriteLine(e.ToString()); - return 1; - } -#endif + return false; + } + + // The public key must be in the wincrypto PUBLICKEYBLOB format + if (publicKey != PublicKeyBlobId) + { + return false; + } + + var signatureAlgorithmId = new AlgorithmId(sigAlgId); + if (signatureAlgorithmId.IsSet && signatureAlgorithmId.Class != AlgorithmClass.Signature) + { + return false; + } + var hashAlgorithmId = new AlgorithmId(hashAlgId); + if (hashAlgorithmId.IsSet && (hashAlgorithmId.Class != AlgorithmClass.Hash || hashAlgorithmId.SubId < AlgorithmSubId.Sha1Hash)) + { + return false; } + + return true; } + + private static int Main(string[] args) => + new CommandLineBuilder(new Crossgen2RootCommand(args)) + .UseVersionOption() + .UseHelp(context => context.HelpBuilder.CustomizeLayout(GetExtendedHelp)) + .UseParseErrorReporting() + .Build() + .Invoke(args); + + + private T Get(Option option) => _command.Result.GetValueForOption(option); + + private static int Main(string[] args) => + new CommandLineBuilder(new Crossgen2RootCommand(args)) + .UseVersionOption() + .UseHelp(context => context.HelpBuilder.CustomizeLayout(Crossgen2RootCommand.GetExtendedHelp)) + .UseParseErrorReporting() + .Build() + .Invoke(args); } From c5e20e4454269e0897a0cc9c8b709fc05be4190f Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Wed, 13 Jul 2022 11:35:24 +0300 Subject: [PATCH 02/12] Switch cg2 to use System.CommandLine --- .../tools/Common/CommandLineHelpers.cs | 7 +- .../TypeSystem/Common/TargetArchitecture.cs | 1 + .../tools/aot/crossgen2/CommandLineOptions.cs | 264 ------------- .../aot/crossgen2/Crossgen2RootCommand.cs | 346 ++++++++++++++++++ src/coreclr/tools/aot/crossgen2/Program.cs | 267 +++++++------- .../aot/crossgen2/Properties/Resources.resx | 5 +- .../tools/aot/crossgen2/crossgen2.props | 20 +- 7 files changed, 502 insertions(+), 408 deletions(-) delete mode 100644 src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs create mode 100644 src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs index e6641eaa48213..e9f17b35f4dce 100644 --- a/src/coreclr/tools/Common/CommandLineHelpers.cs +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -6,6 +6,7 @@ using System.CommandLine.Parsing; using System.IO; using System.IO.Compression; +using System.Runtime.InteropServices; using Internal.TypeSystem; @@ -35,7 +36,7 @@ public static Dictionary BuildPathDictionay(IReadOnlyList return dictionary; } - private static TargetOS GetTargetOS(string token) + public static TargetOS GetTargetOS(string token) { if(string.IsNullOrEmpty(token)) { @@ -61,7 +62,7 @@ private static TargetOS GetTargetOS(string token) throw new CommandLineException($"Target OS '{token}' is not supported"); } - private static TargetArchitecture GetTargetArchitecture(string token) + public static TargetArchitecture GetTargetArchitecture(string token) { if(string.IsNullOrEmpty(token)) { @@ -83,6 +84,8 @@ private static TargetArchitecture GetTargetArchitecture(string token) return Internal.TypeSystem.TargetArchitecture.ARM; else if (token.Equals("arm64", StringComparison.OrdinalIgnoreCase)) return Internal.TypeSystem.TargetArchitecture.ARM64; + else if (token.Equals("armel", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.ARMEL; throw new CommandLineException($"Target architecture '{token}' is not supported"); } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs index b3b587d1432f4..4d16b7250ffc7 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs @@ -14,6 +14,7 @@ public enum TargetArchitecture Unknown, ARM, ARM64, + ARMEL, X64, X86, Wasm32, diff --git a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs deleted file mode 100644 index 86a5340faa89f..0000000000000 --- a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs +++ /dev/null @@ -1,264 +0,0 @@ -// 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.Collections.Generic; -using System.Text; - -using Internal.CommandLine; -using Internal.TypeSystem; - -namespace ILCompiler -{ - internal class CommandLineOptions - { - public const int DefaultPerfMapFormatVersion = 0; - - public bool Help; - public string HelpText; - public bool Version; - - public IReadOnlyList InputFilePaths; - public IReadOnlyList InputBubbleReferenceFilePaths; - public IReadOnlyList UnrootedInputFilePaths; - public IReadOnlyList ReferenceFilePaths; - public IReadOnlyList MibcFilePaths; - public IReadOnlyList CrossModuleInlining; - public string InstructionSet; - public string OutputFilePath; - - public string CompositeRootPath; - public bool Optimize; - public bool OptimizeDisabled; - public bool OptimizeSpace; - public bool OptimizeTime; - public bool AsyncMethodOptimization; - public string NonLocalGenericsModule; - public bool InputBubble; - public bool CompileBubbleGenerics; - public bool Verbose; - public bool Composite; - public string CompositeKeyFile; - public bool CompileNoMethods; - public bool EmbedPgoData; - public bool OutNearInput; - public bool SingleFileCompilation; - - public string DgmlLogFileName; - public bool GenerateFullDgmlLog; - - public string TargetArch; - public string TargetOS; - public string JitPath; - public string SystemModule; - public bool WaitForDebugger; - public bool Partial; - public bool Resilient; - public bool Map; - public bool MapCsv; - public bool PrintReproInstructions; - public bool Pdb; - public string PdbPath; - public bool PerfMap; - public string PerfMapPath; - public int PerfMapFormatVersion; - public int Parallelism; - public int CustomPESectionAlignment; - public string MethodLayout; - public string FileLayout; - public bool VerifyTypeAndFieldLayout; - public string CallChainProfileFile; - public string ImageBase; - - public string SingleMethodTypeName; - public string SingleMethodName; - public int SingleMethodIndex; - public IReadOnlyList SingleMethodGenericArg; - - public IReadOnlyList CodegenOptions; - - public string MakeReproPath; - - public bool CompositeOrInputBubble => Composite || InputBubble; - - public CommandLineOptions(string[] args) - { - InputFilePaths = Array.Empty(); - InputBubbleReferenceFilePaths = Array.Empty(); - UnrootedInputFilePaths = Array.Empty(); - ReferenceFilePaths = Array.Empty(); - MibcFilePaths = Array.Empty(); - CodegenOptions = Array.Empty(); - NonLocalGenericsModule = ""; - - PerfMapFormatVersion = DefaultPerfMapFormatVersion; - Parallelism = Environment.ProcessorCount; - SingleMethodGenericArg = null; - - // These behaviors default to enabled - AsyncMethodOptimization = true; - - bool forceHelp = false; - if (args.Length == 0) - { - forceHelp = true; - } - - foreach (string arg in args) - { - if (arg == "-?") - forceHelp = true; - } - - if (forceHelp) - { - args = new string[] {"--help"}; - } - - ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, syntax => - { - syntax.ApplicationName = typeof(Program).Assembly.GetName().Name.ToString(); - - // HandleHelp writes to error, fails fast with crash dialog and lacks custom formatting. - syntax.HandleHelp = false; - syntax.HandleErrors = true; - - syntax.DefineOptionList("u|unrooted-input-file-paths", ref UnrootedInputFilePaths, SR.UnrootedInputFilesToCompile); - syntax.DefineOptionList("r|reference", ref ReferenceFilePaths, SR.ReferenceFiles); - syntax.DefineOption("instruction-set", ref InstructionSet, SR.InstructionSets); - syntax.DefineOptionList("m|mibc", ref MibcFilePaths, SR.MibcFiles); - syntax.DefineOption("o|out|outputfilepath", ref OutputFilePath, SR.OutputFilePath); - syntax.DefineOption("crp|compositerootpath", ref CompositeRootPath, SR.CompositeRootPath); - syntax.DefineOption("O|optimize", ref Optimize, SR.EnableOptimizationsOption); - syntax.DefineOption("Od|optimize-disabled", ref OptimizeDisabled, SR.DisableOptimizationsOption); - syntax.DefineOption("Os|optimize-space", ref OptimizeSpace, SR.OptimizeSpaceOption); - syntax.DefineOption("Ot|optimize-time", ref OptimizeTime, SR.OptimizeSpeedOption); - syntax.DefineOption("inputbubble", ref InputBubble, SR.InputBubbleOption); - syntax.DefineOptionList("inputbubbleref", ref InputBubbleReferenceFilePaths, SR.InputBubbleReferenceFiles); - syntax.DefineOption("composite", ref Composite, SR.CompositeBuildMode); - syntax.DefineOption("compositekeyfile", ref CompositeKeyFile, SR.CompositeKeyFile); - syntax.DefineOption("compile-no-methods", ref CompileNoMethods, SR.CompileNoMethodsOption); - syntax.DefineOption("out-near-input", ref OutNearInput, SR.OutNearInputOption); - syntax.DefineOption("single-file-compilation", ref SingleFileCompilation, SR.SingleFileCompilationOption); - syntax.DefineOption("partial", ref Partial, SR.PartialImageOption); - syntax.DefineOption("compilebubblegenerics", ref CompileBubbleGenerics, SR.BubbleGenericsOption); - syntax.DefineOption("embed-pgo-data", ref EmbedPgoData, SR.EmbedPgoDataOption); - syntax.DefineOption("dgmllog|dgml-log-file-name", ref DgmlLogFileName, SR.SaveDependencyLogOption); - syntax.DefineOption("fulllog|generate-full-dmgl-log", ref GenerateFullDgmlLog, SR.SaveDetailedLogOption); - syntax.DefineOption("verbose", ref Verbose, SR.VerboseLoggingOption); - syntax.DefineOption("systemmodule", ref SystemModule, SR.SystemModuleOverrideOption); - syntax.DefineOption("waitfordebugger", ref WaitForDebugger, SR.WaitForDebuggerOption); - syntax.DefineOptionList("codegenopt|codegen-options", ref CodegenOptions, SR.CodeGenOptions); - syntax.DefineOption("resilient", ref Resilient, SR.ResilientOption); - syntax.DefineOption("imagebase", ref ImageBase, SR.ImageBase); - - syntax.DefineOption("targetarch", ref TargetArch, SR.TargetArchOption); - syntax.DefineOption("targetos", ref TargetOS, SR.TargetOSOption); - syntax.DefineOption("jitpath", ref JitPath, SR.JitPathOption); - - syntax.DefineOption("print-repro-instructions", ref PrintReproInstructions, SR.PrintReproInstructionsOption); - syntax.DefineOption("singlemethodtypename", ref SingleMethodTypeName, SR.SingleMethodTypeName); - syntax.DefineOption("singlemethodname", ref SingleMethodName, SR.SingleMethodMethodName); - syntax.DefineOption("singlemethodindex", ref SingleMethodIndex, SR.SingleMethodIndex); - syntax.DefineOptionList("singlemethodgenericarg", ref SingleMethodGenericArg, SR.SingleMethodGenericArgs); - - syntax.DefineOption("parallelism", ref Parallelism, SR.ParalellismOption); - syntax.DefineOption("custom-pe-section-alignment", ref CustomPESectionAlignment, SR.CustomPESectionAlignmentOption); - syntax.DefineOption("map", ref Map, SR.MapFileOption); - syntax.DefineOption("mapcsv", ref MapCsv, SR.MapCsvFileOption); - syntax.DefineOption("pdb", ref Pdb, SR.PdbFileOption); - syntax.DefineOption("pdb-path", ref PdbPath, SR.PdbFilePathOption); - syntax.DefineOption("perfmap", ref PerfMap, SR.PerfMapFileOption); - syntax.DefineOption("perfmap-path", ref PerfMapPath, SR.PerfMapFilePathOption); - syntax.DefineOption("perfmap-format-version", ref PerfMapFormatVersion, SR.PerfMapFormatVersionOption); - - syntax.DefineOptionList("opt-cross-module", ref this.CrossModuleInlining, SR.CrossModuleInlining); - syntax.DefineOption("opt-async-methods", ref AsyncMethodOptimization, SR.AsyncModuleOptimization); - syntax.DefineOption("non-local-generics-module", ref NonLocalGenericsModule, SR.NonLocalGenericsModule); - - syntax.DefineOption("method-layout", ref MethodLayout, SR.MethodLayoutOption); - syntax.DefineOption("file-layout", ref FileLayout, SR.FileLayoutOption); - syntax.DefineOption("verify-type-and-field-layout", ref VerifyTypeAndFieldLayout, SR.VerifyTypeAndFieldLayoutOption); - syntax.DefineOption("callchain-profile", ref CallChainProfileFile, SR.CallChainProfileFile); - - syntax.DefineOption("make-repro-path", ref MakeReproPath, SR.MakeReproPathHelp); - - syntax.DefineOption("h|help", ref Help, SR.HelpOption); - syntax.DefineOption("v|version", ref Version, SR.VersionOption); - - syntax.DefineParameterList("in", ref InputFilePaths, SR.InputFilesToCompile); - }); - - if (Help) - { - List extraHelp = new List(); - extraHelp.Add(SR.OptionPassingHelp); - extraHelp.Add(""); - extraHelp.Add(SR.DashDashHelp); - extraHelp.Add(""); - - string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"}; - string[] ValidOS = new string[] {"windows", "linux", "osx"}; - TargetOS defaultOs; - TargetArchitecture defaultArch; - Program.ComputeDefaultOptions(out defaultOs, out defaultArch); - - extraHelp.Add(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), defaultOs.ToString().ToLowerInvariant())); - - extraHelp.Add(""); - - extraHelp.Add(String.Format(SR.SwitchWithDefaultHelp, "--targetarch", String.Join("', '", ValidArchitectures), defaultArch.ToString().ToLowerInvariant())); - - extraHelp.Add(""); - - extraHelp.Add(SR.InstructionSetHelp); - foreach (string arch in ValidArchitectures) - { - StringBuilder archString = new StringBuilder(); - - archString.Append(arch); - archString.Append(": "); - - TargetArchitecture targetArch = Program.GetTargetArchitectureFromArg(arch, out _); - bool first = true; - foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) - { - // Only instruction sets with are specifiable should be printed to the help text - if (instructionSet.Specifiable) - { - if (first) - { - first = false; - } - else - { - archString.Append(", "); - } - archString.Append(instructionSet.Name); - } - } - - extraHelp.Add(archString.ToString()); - } - - extraHelp.Add(""); - extraHelp.Add(SR.CpuFamilies); - extraHelp.Add(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); - - argSyntax.ExtraHelpParagraphs = extraHelp; - - HelpText = argSyntax.GetHelpText(); - } - - if (MakeReproPath != null) - { - // Create a repro package in the specified path - // This package will have the set of input files needed for compilation - // + the original command line arguments - // + a rsp file that should work to directly run out of the zip file - - Helpers.MakeReproPackage(MakeReproPath, OutputFilePath, args, argSyntax, new[] { "-r", "-u", "-m", "--inputbubbleref" }); - } - } - } -} diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs new file mode 100644 index 0000000000000..1af04c2632d70 --- /dev/null +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -0,0 +1,346 @@ +// 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.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Help; +using System.CommandLine.Parsing; +using System.IO; +using System.Runtime.InteropServices; + +using Internal.TypeSystem; + +namespace ILCompiler; + +internal class Crossgen2RootCommand : RootCommand +{ + public Argument> InputFilePaths { get; } = + new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; + public Option> UnrootedInputFilePaths { get; } = + new(new[] { "--unrooted-input-file-paths", "-u" }, result => Helpers.BuildPathDictionay(result.Tokens, true), true, SR.UnrootedInputFilesToCompile); + public Option> ReferenceFilePaths { get; } = + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), false, SR.ReferenceFiles); + public Option InstructionSet { get; } = + new(new[] { "--instructionset" }, SR.InstructionSets); + public Option MibcFilePaths { get; } = + new(new[] { "--mibc", "-m" }, () => Array.Empty(), SR.MibcFiles); + public Option OutputFilePath { get; } = + new(new[] { "--out", "-o" }, SR.OutputFilePath); + public Option CompositeRootPath { get; } = + new(new[] { "--compositerootpath", "-crp" }, SR.CompositeRootPath); + public Option Optimize { get; } = + new(new[] { "--optimize", "-O" }, SR.EnableOptimizationsOption); + public Option OptimizeDisabled { get; } = + new(new[] { "--optimize-disabled", "-Od" }, SR.DisableOptimizationsOption); + public Option OptimizeSpace { get; } = + new(new[] { "--optimize-space", "-Os" }, SR.OptimizeSpaceOption); + public Option OptimizeTime { get; } = + new(new[] { "--optimize-time", "-Ot" }, SR.OptimizeSpeedOption); + public Option InputBubble { get; } = + new(new[] { "--inputbubble" }, SR.InputBubbleOption); + public Option> InputBubbleReferenceFilePaths { get; } = + new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionay(result.Tokens, false), false, SR.InputBubbleReferenceFiles); + public Option Composite { get; } = + new(new[] { "--composite" }, SR.CompositeBuildMode); + public Option CompositeKeyFile { get; } = + new(new[] { "--compositekeyfile" }, SR.CompositeKeyFile); + public Option CompileNoMethods { get; } = + new(new[] { "--compile-no-methods" }, SR.CompileNoMethodsOption); + public Option OutNearInput { get; } = + new(new[] { "--out-near-input" }, SR.OutNearInputOption); + public Option SingleFileCompilation { get; } = + new(new[] { "--single-file-compilation" }, SR.SingleFileCompilationOption); + public Option Partial { get; } = + new(new[] { "--partial" }, SR.PartialImageOption); + public Option CompileBubbleGenerics { get; } = + new(new[] { "--compilebubblegenerics" }, SR.BubbleGenericsOption); + public Option EmbedPgoData { get; } = + new(new[] { "--embed-pgo-data" }, SR.EmbedPgoDataOption); + public Option DgmlLogFileName { get; } = + new(new[] { "--dgmllog" }, SR.SaveDependencyLogOption); + public Option GenerateFullDgmlLog { get; } = + new(new[] { "--fulllog" }, SR.SaveDetailedLogOption); + public Option IsVerbose { get; } = + new(new[] { "--verbose" }, SR.VerboseLoggingOption); + public Option SystemModuleName { get; } = + new(new[] { "--systemmodule" }, () => Helpers.DefaultSystemModule, SR.SystemModuleOverrideOption); + public Option WaitForDebugger { get; } = + new(new[] { "--waitfordebugger" }, SR.WaitForDebuggerOption); + public Option CodegenOptions { get; } = + new(new[] { "--codegenopt" }, () => Array.Empty(), SR.CodeGenOptions); + public Option Resilient { get; } = + new(new[] { "--resilient" }, SR.ResilientOption); + public Option ImageBase { get; } = + new(new[] { "--imagebase" }, SR.ImageBase); + public Option TargetArchitecture { get; } = + new(new[] { "--targetarch" }, result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetArchOption); + public Option TargetOS { get; } = + new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetOSOption); + public Option JitPath { get; } = + new(new[] { "--jitpath" }, SR.JitPathOption); + public Option PrintReproInstructions { get; } = + new(new[] { "--print-repro-instructions" }, SR.PrintReproInstructionsOption); + public Option SingleMethodTypeName { get; } = + new(new[] { "--singlemethodtypename" }, SR.SingleMethodTypeName); + public Option SingleMethodName { get; } = + new(new[] { "--singlemethodname" }, SR.SingleMethodMethodName); + public Option SingleMethodIndex { get; } = + new(new[] { "--singlemethodindex" }, SR.SingleMethodIndex); + public Option SingleMethodGenericArgs { get; } = + new(new[] { "--singlemethodgenericarg" }, SR.SingleMethodGenericArgs); + public Option Parallelism { get; } = + new(new[] { "--parallelism" }, () => Environment.ProcessorCount, SR.ParalellismOption); + public Option CustomPESectionAlignment { get; } = + new(new[] { "--custom-pe-section-alignment" }, SR.CustomPESectionAlignmentOption); + public Option Map { get; } = + new(new[] { "--map" }, SR.MapFileOption); + public Option MapCsv { get; } = + new(new[] { "--mapcsv" }, SR.MapCsvFileOption); + public Option Pdb { get; } = + new(new[] { "--pdb" }, SR.PdbFileOption); + public Option PdbPath { get; } = + new(new[] { "--pdb-path" }, SR.PdbFilePathOption); + public Option PerfMap { get; } = + new(new[] { "--perfmap" }, SR.PerfMapFileOption); + public Option PerfMapPath { get; } = + new(new[] { "--perfmap-path" }, SR.PerfMapFilePathOption); + public Option PerfMapFormatVersion { get; } = + new(new[] { "--perfmap-format-version" }, () => 0, SR.PerfMapFormatVersionOption); + public Option CrossModuleInlining { get; } = + new(new[] { "--opt-cross-module" }, SR.CrossModuleInlining); + public Option AsyncMethodOptimization { get; } = + new(new[] { "--opt-async-methods" }, SR.AsyncModuleOptimization); + public Option NonLocalGenericsModule { get; } = + new(new[] { "--non-local-generics-module" }, () => string.Empty, SR.NonLocalGenericsModule); + public Option MethodLayout { get; } = + new(new[] { "--method-layout" }, 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, + "callfrequency" => ReadyToRunMethodLayoutAlgorithm.CallFrequency, + "pettishansen" => ReadyToRunMethodLayoutAlgorithm.PettisHansen, + "random" => ReadyToRunMethodLayoutAlgorithm.Random, + _ => throw new CommandLineException(SR.InvalidMethodLayout) + }; + }, true, SR.MethodLayoutOption); + public Option FileLayout { get; } = + new(new[] { "--file-layout" }, 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(SR.InvalidFileLayout) + }; + }, true, SR.FileLayoutOption); + public Option VerifyTypeAndFieldLayout { get; } = + new(new[] { "--verify-type-and-field-layout" }, SR.VerifyTypeAndFieldLayoutOption); + public Option CallChainProfileFile { get; } = + new(new[] { "--callchain-profile" }, SR.CallChainProfileFile); + public Option MakeReproPath { get; } = + new(new[] { "--make-repro-path" }, "Path where to place a repro package"); + + public bool CompositeOrInputBubble { get; private set; } + public OptimizationMode OptimizationMode { get; private set; } + public ParseResult Result { get; private set; } + + public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) + { + AddArgument(InputFilePaths); + AddOption(UnrootedInputFilePaths); + AddOption(ReferenceFilePaths); + AddOption(InstructionSet); + AddOption(MibcFilePaths); + AddOption(OutputFilePath); + AddOption(CompositeRootPath); + AddOption(Optimize); + AddOption(OptimizeDisabled); + AddOption(OptimizeSpace); + AddOption(OptimizeTime); + AddOption(InputBubble); + AddOption(InputBubbleReferenceFilePaths); + AddOption(Composite); + AddOption(CompositeKeyFile); + AddOption(CompileNoMethods); + AddOption(OutNearInput); + AddOption(SingleFileCompilation); + AddOption(Partial); + AddOption(CompileBubbleGenerics); + AddOption(EmbedPgoData); + AddOption(DgmlLogFileName); + AddOption(GenerateFullDgmlLog); + AddOption(IsVerbose); + AddOption(SystemModuleName); + AddOption(WaitForDebugger); + AddOption(CodegenOptions); + AddOption(Resilient); + AddOption(ImageBase); + AddOption(TargetArchitecture); + AddOption(TargetOS); + AddOption(JitPath); + AddOption(PrintReproInstructions); + AddOption(SingleMethodTypeName); + AddOption(SingleMethodName); + AddOption(SingleMethodIndex); + AddOption(SingleMethodGenericArgs); + AddOption(Parallelism); + AddOption(CustomPESectionAlignment); + AddOption(Map); + AddOption(MapCsv); + AddOption(Pdb); + AddOption(PdbPath); + AddOption(PerfMap); + AddOption(PerfMapPath); + AddOption(PerfMapFormatVersion); + AddOption(CrossModuleInlining); + AddOption(AsyncMethodOptimization); + AddOption(NonLocalGenericsModule); + AddOption(MethodLayout); + AddOption(FileLayout); + AddOption(VerifyTypeAndFieldLayout); + AddOption(CallChainProfileFile); + AddOption(MakeReproPath); + + this.SetHandler(context => + { + Result = context.ParseResult; + CompositeOrInputBubble = context.ParseResult.GetValueForOption(Composite) | context.ParseResult.GetValueForOption(InputBubble); + if (context.ParseResult.GetValueForOption(OptimizeSpace)) + { + OptimizationMode = OptimizationMode.PreferSize; + } + else if (context.ParseResult.GetValueForOption(OptimizeTime)) + { + OptimizationMode = OptimizationMode.PreferSpeed; + } + else if (context.ParseResult.GetValueForOption(Optimize)) + { + OptimizationMode = OptimizationMode.Blended; + } + else + { + OptimizationMode = OptimizationMode.None; + } + + try + { + int alignment = context.ParseResult.GetValueForOption(CustomPESectionAlignment); + if (alignment != 0) + { + // Must be a power of two and >= 4096 + if (alignment < 4096 || (alignment & (alignment - 1)) != 0) + throw new CommandLineException(SR.InvalidCustomPESectionAlignment); + } + + string makeReproPath = context.ParseResult.GetValueForOption(MakeReproPath); + if (makeReproPath != null) + { + // Create a repro package in the specified path + // This package will have the set of input files needed for compilation + // + the original command line arguments + // + a rsp file that should work to directly run out of the zip file + + Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, + context.ParseResult, new[] { "r", "reference", "m", "mibc", "rdxml", "directpinvokelist" }); + } + + context.ExitCode = new Program(this).Run(); + } +#if DEBUG + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } +#else + catch (Exception e) + { + context.Console.ResetColor(); + context.Console.ForegroundColor = ConsoleColor.Red; + + context.Console.Error.WriteLine("Error: " + e.Message); + context.Console.Error.WriteLine(e.ToString()); + + context.Console.ResetColor(); + + context.ExitCode = 1; + } +#endif + }); + } + + public static IEnumerable GetExtendedHelp(HelpContext _) + { + foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) + yield return sectionDelegate; + + yield return _ => + { + Console.WriteLine(SR.OptionPassingHelp); + Console.WriteLine(); + Console.WriteLine(SR.DashDashHelp); + Console.WriteLine(); + + string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"}; + string[] ValidOS = new string[] {"windows", "linux", "osx"}; + + Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant())); + Console.WriteLine(); + Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetarch", String.Join("', '", ValidArchitectures), Helpers.GetTargetArchitecture(null).ToString().ToLowerInvariant())); + Console.WriteLine(); + + Console.WriteLine(SR.InstructionSetHelp); + foreach (string arch in ValidArchitectures) + { + Console.Write(arch); + Console.Write(": "); + + TargetArchitecture targetArch = Helpers.GetTargetArchitecture(arch); + bool first = true; + foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) + { + // Only instruction sets with are specifiable should be printed to the help text + if (instructionSet.Specifiable) + { + if (first) + { + first = false; + } + else + { + Console.Write(", "); + } + Console.Write(instructionSet.Name); + } + } + + Console.WriteLine(); + } + + Console.WriteLine(); + Console.WriteLine(SR.CpuFamilies); + Console.WriteLine(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); + }; + } + +#if DEBUG + private static bool DumpReproArguments(CodeGenerationFailedException ex) + { + Console.WriteLine(SR.DumpReproInstructions); + + MethodDesc failingMethod = ex.Method; + Console.WriteLine(Program.CreateReproArgumentString(failingMethod)); + return false; + } +#endif +} diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 6004275cced5c..dbf262653b08c 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -3,6 +3,8 @@ using System; using System.Buffers.Binary; +using System.CommandLine; +using System.CommandLine.Parsing; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -26,9 +28,29 @@ internal class Program { private readonly Crossgen2RootCommand _command; + // Modules and their names after loading + private Dictionary _allInputFilePaths = new(); + private List _referenceableModules = new(); + + private ReadyToRunCompilerContext _typeSystemContext; + private ulong _imageBase; + + private readonly bool _inputBubble; + private readonly bool _singleFileCompilation; + private readonly bool _outNearInput; + private readonly string _outputFilePath; + private readonly TargetArchitecture _targetArchitecture; + private readonly TargetOS _targetOS; + public Program(Crossgen2RootCommand command) { _command = command; + _inputBubble = Get(command.InputBubble); + _singleFileCompilation = Get(command.SingleFileCompilation); + _outNearInput = Get(command.OutNearInput); + _outputFilePath = Get(command.OutputFilePath); + _targetOS = Get(command.TargetOS); + _targetArchitecture = Get(command.TargetArchitecture); if (command.Result.GetValueForOption(command.WaitForDebugger)) { @@ -59,12 +81,13 @@ private InstructionSetSupport ConfigureInstructionSetSupport() } } - if (_command.InstructionSet != null) + string instructionSetArg = Get(_command.InstructionSet); + if (instructionSetArg != null) { List instructionSetParams = new List(); // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = _command.InstructionSet.Split(","); + string[] instructionSetParamsInput = instructionSetArg.Split(","); for (int i = 0; i < instructionSetParamsInput.Length; i++) { string instructionSet = instructionSetParamsInput[i]; @@ -129,64 +152,52 @@ private InstructionSetSupport ConfigureInstructionSetSupport() optimisticInstructionSet.Add(supportedInstructionSet); return new InstructionSetSupport(supportedInstructionSet, - unsupportedInstructionSet, - optimisticInstructionSet, - InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), - _targetArchitecture); + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), + _targetArchitecture); } - private ulong ConfigureImageBase(TargetDetails targetDetails) + private void ConfigureImageBase(TargetDetails targetDetails) { bool is64BitTarget = targetDetails.PointerSize == sizeof(long); - if (_command.ImageBase != null) - return is64BitTarget ? Convert.ToUInt64(_command.ImageBase, 16) : Convert.ToUInt32(_command.ImageBase, 16); - - return is64BitTarget ? PEWriter.PE64HeaderConstants.DllImageBase : PEWriter.PE32HeaderConstants.ImageBase; + string imageBaseArg = Get(_command.ImageBase); + if (imageBaseArg != null) + _imageBase = is64BitTarget ? Convert.ToUInt64(imageBaseArg, 16) : Convert.ToUInt32(imageBaseArg, 16); + else + _imageBase = is64BitTarget ? PEWriter.PE64HeaderConstants.DllImageBase : PEWriter.PE32HeaderConstants.ImageBase; } - private int Run(string[] args) + public int Run() { - InitializeDefaultOptions(); - - ProcessCommandLine(args); - - if (_command.Help) - { - Console.WriteLine(_command.HelpText); - return 1; - } - - if (_command.Version) - { - string version = GetCompilerVersion(); - Console.WriteLine(version); - return 0; - } - - if (_command.OutputFilePath == null && !_command.OutNearInput) + if (_outputFilePath == null && !_outNearInput) throw new CommandLineException(SR.MissingOutputFile); - if (_command.SingleFileCompilation && !_command.OutNearInput) + if (_singleFileCompilation && !_outNearInput) throw new CommandLineException(SR.MissingOutNearInput); - ConfigureTarget(); InstructionSetSupport instructionSetSupport = ConfigureInstructionSetSupport(); SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _targetArchitecture == TargetArchitecture.ARMEL ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); + + ConfigureImageBase(targetDetails); bool versionBubbleIncludesCoreLib = false; - if (_command.InputBubble) + Dictionary inputFilePathsArg = _command.Result.GetValueForArgument(_command.InputFilePaths); + Dictionary unrootedInputFilePathsArg = Get(_command.UnrootedInputFilePaths); + + if (_inputBubble) { versionBubbleIncludesCoreLib = true; } else { - if (!_command.SingleFileCompilation) + if (!_singleFileCompilation) { - foreach (var inputFile in _inputFilePaths) + foreach (var inputFile in inputFilePathsArg) { if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) { @@ -197,7 +208,7 @@ private int Run(string[] args) } if (!versionBubbleIncludesCoreLib) { - foreach (var inputFile in _unrootedInputFilePaths) + foreach (var inputFile in unrootedInputFilePathsArg) { if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) { @@ -213,12 +224,13 @@ private int Run(string[] args) // _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib); - string compositeRootPath = _command.CompositeRootPath; + string compositeRootPath = Get(_command.CompositeRootPath); // Collections for already loaded modules Dictionary inputFilePaths = new Dictionary(); Dictionary unrootedInputFilePaths = new Dictionary(); HashSet versionBubbleModulesHash = new HashSet(); + Dictionary referenceFilePaths = Get(_command.ReferenceFilePaths); using (PerfEventSource.StartStopEvents.LoadingEvents()) { @@ -232,7 +244,7 @@ private int Run(string[] args) // typeSystemContext.InputFilePaths = inFilePaths; // - foreach (var inputFile in _inputFilePaths) + foreach (var inputFile in inputFilePathsArg) { try { @@ -257,7 +269,7 @@ private int Run(string[] args) } } - foreach (var unrootedInputFile in _unrootedInputFilePaths) + foreach (var unrootedInputFile in unrootedInputFilePathsArg) { try { @@ -282,11 +294,11 @@ private int Run(string[] args) CheckManagedCppInputFiles(_allInputFilePaths.Values); _typeSystemContext.InputFilePaths = _allInputFilePaths; - _typeSystemContext.ReferenceFilePaths = _referenceFilePaths; + _typeSystemContext.ReferenceFilePaths = referenceFilePaths; if (_typeSystemContext.InputFilePaths.Count == 0) { - if (_command.InputFilePaths.Count > 0) + if (inputFilePathsArg.Count > 0) { Console.WriteLine(SR.InputWasNotLoadable); return 2; @@ -294,7 +306,8 @@ private int Run(string[] args) throw new CommandLineException(SR.NoInputFiles); } - foreach (var referenceFile in _referenceFilePaths.Values) + Dictionary inputBubbleReferenceFilePaths = Get(_command.InputBubbleReferenceFilePaths); + foreach (var referenceFile in referenceFilePaths.Values) { try { @@ -303,7 +316,7 @@ private int Run(string[] args) continue; _referenceableModules.Add(module); - if (_command.InputBubble && _inputbubblereferenceFilePaths.Count == 0) + if (_inputBubble && inputBubbleReferenceFilePaths.Count == 0) { // In large version bubble mode add reference paths to the compilation group // Consider bubble as large if no explicit bubble references were passed @@ -313,9 +326,9 @@ private int Run(string[] args) catch { } // Ignore non-managed pe files } - if (_command.InputBubble) + if (_inputBubble) { - foreach (var referenceFile in _inputbubblereferenceFilePaths.Values) + foreach (var referenceFile in inputBubbleReferenceFilePaths.Values) { try { @@ -331,11 +344,11 @@ private int Run(string[] args) } } - string systemModuleName = _command.SystemModule ?? Helpers.DefaultSystemModule; + string systemModuleName = Get(_command.SystemModuleName) ?? Helpers.DefaultSystemModule; _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName)); CompilerTypeSystemContext typeSystemContext = _typeSystemContext; - if (_command.SingleFileCompilation) + if (_singleFileCompilation) { var singleCompilationInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -347,13 +360,13 @@ private int Run(string[] args) singleCompilationInputFilePaths.Add(inputFile.Key, inputFile.Value); typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; - if (!_command.InputBubble) + if (!_inputBubble) { bool singleCompilationVersionBubbleIncludesCoreLib = versionBubbleIncludesCoreLib || (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0); typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext); typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; - typeSystemContext.ReferenceFilePaths = _referenceFilePaths; + typeSystemContext.ReferenceFilePaths = referenceFilePaths; typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName)); } @@ -362,7 +375,7 @@ private int Run(string[] args) // In case of inputbubble ni.dll are created as ni.dll.tmp in order to not interfere with crossgen2, move them all to ni.dll // See https://github.com/dotnet/runtime/issues/55663#issuecomment-898161751 for more details - if (_command.InputBubble) + if (_inputBubble) { foreach (var inputFile in inputFilePaths) { @@ -386,21 +399,25 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru // // Initialize output filename // - string inFilePath = inFilePaths.First().Value; + var e = inFilePaths.GetEnumerator(); + e.MoveNext(); + string inFilePath = e.Current.Value; string inputFileExtension = Path.GetExtension(inFilePath); string nearOutFilePath = inputFileExtension switch { ".dll" => Path.ChangeExtension(inFilePath, - _command.SingleFileCompilation && _command.InputBubble + _singleFileCompilation&& _inputBubble ? ".ni.dll.tmp" : ".ni.dll"), ".exe" => Path.ChangeExtension(inFilePath, - _command.SingleFileCompilation && _command.InputBubble + _singleFileCompilation && _inputBubble ? ".ni.exe.tmp" : ".ni.exe"), _ => throw new CommandLineException(string.Format(SR.UnsupportedInputFileExtension, inputFileExtension)) }; - string outFile = _command.OutNearInput ? nearOutFilePath : _command.OutputFilePath; + + string outFile = _outNearInput ? nearOutFilePath : _outputFilePath; + string dgmlLogFileName = Get(_command.DgmlLogFileName); using (PerfEventSource.StartStopEvents.CompilationEvents()) { @@ -432,9 +449,10 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru versionBubbleModulesHash.Add(module); } - if (_command.CrossModuleInlining != null) + string[] crossModuleInlining = Get(_command.CrossModuleInlining); + if (crossModuleInlining.Length > 0) { - foreach (var crossModulePgoAssemblyName in _command.CrossModuleInlining) + foreach (var crossModulePgoAssemblyName in crossModuleInlining) { foreach (var module in _referenceableModules) { @@ -457,44 +475,45 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru // Single method mode? MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); - var logger = new Logger(Console.Out, _command.Verbose); + var logger = new Logger(Console.Out, Get(_command.IsVerbose)); List mibcFiles = new List(); - foreach (var file in _command.MibcFilePaths) + foreach (var file in Get(_command.MibcFilePaths)) { mibcFiles.Add(file); } List versionBubbleModules = new List(versionBubbleModulesHash); - - if (!_command.Composite && inputModules.Count != 1) + bool composite = Get(_command.Composite); + if (!composite && inputModules.Count != 1) { throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); } - + bool compileBubbleGenerics = Get(_command.CompileBubbleGenerics); ReadyToRunCompilationModuleGroupBase compilationGroup; List compilationRoots = new List(); ReadyToRunCompilationModuleGroupConfig groupConfig = new ReadyToRunCompilationModuleGroupConfig(); groupConfig.Context = typeSystemContext; - groupConfig.IsCompositeBuildMode = _command.Composite; - groupConfig.IsInputBubble = _command.InputBubble; + groupConfig.IsCompositeBuildMode = composite; + groupConfig.IsInputBubble = _inputBubble; groupConfig.CompilationModuleSet = inputModules; groupConfig.VersionBubbleModuleSet = versionBubbleModules; - groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = _command.CompileBubbleGenerics; + groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = compileBubbleGenerics; groupConfig.CrossModuleGenericCompilation = crossModuleInlineableCode.Count > 0; groupConfig.CrossModuleInlining = groupConfig.CrossModuleGenericCompilation; // Currently we set these flags to the same values groupConfig.CrossModuleInlineable = crossModuleInlineableCode; groupConfig.CompileAllPossibleCrossModuleCode = false; // Handle non-local generics command line option - ModuleDesc nonLocalGenericsHome = _command.CompileBubbleGenerics ? inputModules[0] : null; - if (_command.NonLocalGenericsModule == "*") + ModuleDesc nonLocalGenericsHome = compileBubbleGenerics ? inputModules[0] : null; + string nonLocalGenericsModule = Get(_command.NonLocalGenericsModule); + if (nonLocalGenericsModule == "*") { groupConfig.CompileAllPossibleCrossModuleCode = true; nonLocalGenericsHome = inputModules[0]; } - else if (_command.NonLocalGenericsModule == "") + else if (nonLocalGenericsModule == "") { // Nothing was specified } @@ -503,11 +522,11 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru bool matchFound = false; // Allow module to be specified by assembly name or by filename - if (_command.NonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) - _command.NonLocalGenericsModule = Path.GetFileNameWithoutExtension(_command.NonLocalGenericsModule); + if (nonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + nonLocalGenericsModule = Path.GetFileNameWithoutExtension(nonLocalGenericsModule); foreach (var module in inputModules) { - if (String.Compare(module.Assembly.GetName().Name, _command.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) { matchFound = true; nonLocalGenericsHome = module; @@ -520,7 +539,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru { foreach (var module in _referenceableModules) { - if (String.Compare(module.Assembly.GetName().Name, _command.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) { matchFound = true; break; @@ -529,11 +548,12 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru if (!matchFound) { - throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, _command.NonLocalGenericsModule)); + throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, nonLocalGenericsModule)); } } } + bool compileNoMethods = Get(_command.CompileNoMethods); if (singleMethod != null) { // Compiling just a single method @@ -542,7 +562,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru singleMethod); compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); } - else if (_command.CompileNoMethods) + else if (compileNoMethods) { compilationGroup = new NoMethodsCompilationModuleGroup(groupConfig); } @@ -554,10 +574,10 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru // Load any profiles generated by method call chain analyis CallChainProfile jsonProfile = null; - - if (!string.IsNullOrEmpty(_command.CallChainProfileFile)) + string callChainProfileFile = Get(_command.CallChainProfileFile); + if (!string.IsNullOrEmpty(callChainProfileFile)) { - jsonProfile = new CallChainProfile(_command.CallChainProfileFile, typeSystemContext, _referenceableModules); + jsonProfile = new CallChainProfile(callChainProfileFile, typeSystemContext, _referenceableModules); } // Examine profile guided information as appropriate @@ -583,12 +603,13 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru jsonProfile, typeSystemContext, compilationGroup, - _command.EmbedPgoData, + Get(_command.EmbedPgoData), crossModuleInlineableCode.Count == 0 ? compilationGroup.VersionsWithMethodBody : compilationGroup.CrossModuleInlineable); - compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, _command.Partial); + bool partial = Get(_command.Partial); + compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, partial); - if ((singleMethod == null) && !_command.CompileNoMethods) + if ((singleMethod == null) && !compileNoMethods) { // For normal compilations add compilation roots. foreach (var module in rootingModules) @@ -596,7 +617,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru compilationRoots.Add(new ReadyToRunRootProvider( module, profileDataManager, - profileDrivenPartialNGen: _command.Partial)); + profileDrivenPartialNGen: partial)); if (!_command.CompositeOrInputBubble) { @@ -606,18 +627,18 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru } // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize // or produce debuggable code if an explicit optimization level was not specified on the command line - OptimizationMode optimizationMode = _optimizationMode; - if (optimizationMode == OptimizationMode.None && !_command.OptimizeDisabled && !_command.Composite) + OptimizationMode optimizationMode = _command.OptimizationMode; + if (optimizationMode == OptimizationMode.None && !Get(_command.OptimizeDisabled) && !composite) { System.Diagnostics.Debug.Assert(inputModules.Count == 1); optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended; } CompositeImageSettings compositeImageSettings = new CompositeImageSettings(); - - if (_command.CompositeKeyFile != null) + string compositeKeyFile = Get(_command.CompositeKeyFile); + if (compositeKeyFile != null) { - byte[] compositeStrongNameKey = File.ReadAllBytes(_command.CompositeKeyFile); + byte[] compositeStrongNameKey = File.ReadAllBytes(compositeKeyFile); if (!IsValidPublicKey(compositeStrongNameKey)) { throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey)); @@ -637,38 +658,38 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru ILProvider ilProvider = new ReadyToRunILProvider(compilationGroup); - DependencyTrackingLevel trackingLevel = _command.DgmlLogFileName == null ? - DependencyTrackingLevel.None : (_command.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? + DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); - nodeFactoryFlags.OptimizeAsyncMethods = _command.AsyncMethodOptimization; + nodeFactoryFlags.OptimizeAsyncMethods = Get(_command.AsyncMethodOptimization); builder - .UseMapFile(_command.Map) - .UseMapCsvFile(_command.MapCsv) - .UsePdbFile(_command.Pdb, _command.PdbPath) - .UsePerfMapFile(_command.PerfMap, _command.PerfMapPath, _command.PerfMapFormatVersion) + .UseMapFile(Get(_command.Map)) + .UseMapCsvFile(Get(_command.MapCsv)) + .UsePdbFile(Get(_command.Pdb), Get(_command.PdbPath)) + .UsePerfMapFile(Get(_command.PerfMap), Get(_command.PerfMapPath), Get(_command.PerfMapFormatVersion)) .UseProfileFile(jsonProfile != null) .UseProfileData(profileDataManager) .UseNodeFactoryOptimizationFlags(nodeFactoryFlags) - .FileLayoutAlgorithms(_methodLayout, _fileLayout) + .FileLayoutAlgorithms(Get(_command.MethodLayout), Get(_command.FileLayout)) .UseCompositeImageSettings(compositeImageSettings) - .UseJitPath(_command.JitPath) + .UseJitPath(Get(_command.JitPath)) .UseInstructionSetSupport(instructionSetSupport) - .UseCustomPESectionAlignment(_command.CustomPESectionAlignment) - .UseVerifyTypeAndFieldLayout(_command.VerifyTypeAndFieldLayout) + .UseCustomPESectionAlignment(Get(_command.CustomPESectionAlignment)) + .UseVerifyTypeAndFieldLayout(Get(_command.VerifyTypeAndFieldLayout)) .GenerateOutputFile(outFile) - .UseImageBase(ConfigureImageBase(targetDetails)) + .UseImageBase(_imageBase) .UseILProvider(ilProvider) - .UseBackendOptions(_command.CodegenOptions) + .UseBackendOptions(Get(_command.CodegenOptions)) .UseLogger(logger) - .UseParallelism(_command.Parallelism) - .UseResilience(_command.Resilient) + .UseParallelism(Get(_command.Parallelism)) + .UseResilience(Get(_command.Resilient)) .UseDependencyTracking(trackingLevel) .UseCompilationRoots(compilationRoots) .UseOptimizationMode(optimizationMode); - if (_command.PrintReproInstructions) + if (Get(_command.PrintReproInstructions)) builder.UsePrintReproInstructions(CreateReproArgumentString); compilation = builder.ToCompilation(); @@ -676,8 +697,8 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru } compilation.Compile(outFile); - if (_command.DgmlLogFileName != null) - compilation.WriteDependencyLog(_command.DgmlLogFileName); + if (dgmlLogFileName != null) + compilation.WriteDependencyLog(dgmlLogFileName); compilation.Dispose(); } @@ -712,13 +733,16 @@ private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) { - if (_command.SingleMethodName == null && _command.SingleMethodTypeName == null && _command.SingleMethodGenericArg == null) + string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); + string singleMethodName = Get(_command.SingleMethodName); + string singleMethodTypeName = Get(_command.SingleMethodTypeName); + if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) return null; - if (_command.SingleMethodName == null || _command.SingleMethodTypeName == null) + if (singleMethodName == null || singleMethodTypeName == null) throw new CommandLineException(SR.TypeAndMethodNameNeeded); - TypeDesc owningType = FindType(context, _command.SingleMethodTypeName); + TypeDesc owningType = FindType(context, singleMethodTypeName); // TODO: allow specifying signature to distinguish overloads MethodDesc method = null; @@ -726,13 +750,13 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont int curIndex = 0; foreach (var searchMethod in owningType.GetMethods()) { - if (searchMethod.Name != _command.SingleMethodName) + if (searchMethod.Name != singleMethodName) continue; curIndex++; - if (_command.SingleMethodIndex != 0) + if (Get(_command.SingleMethodIndex) != 0) { - if (curIndex == _command.SingleMethodIndex) + if (curIndex == Get(_command.SingleMethodIndex)) { method = searchMethod; break; @@ -756,7 +780,7 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont curIndex = 0; foreach (var searchMethod in owningType.GetMethods()) { - if (searchMethod.Name != _command.SingleMethodName) + if (searchMethod.Name != singleMethodName) continue; curIndex++; @@ -766,19 +790,19 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont } if (method == null) - throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, _command.SingleMethodName, _command.SingleMethodTypeName)); + throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, singleMethodName, singleMethodTypeName)); - if (method.HasInstantiation != (_command.SingleMethodGenericArg != null) || - (method.HasInstantiation && (method.Instantiation.Length != _command.SingleMethodGenericArg.Count))) + if (method.HasInstantiation != (singleMethodGenericArgs != null) || + (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) { throw new CommandLineException( - string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, _command.SingleMethodName, _command.SingleMethodTypeName)); + string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, singleMethodName, singleMethodTypeName)); } if (method.HasInstantiation) { List genericArguments = new List(); - foreach (var argString in _command.SingleMethodGenericArg) + foreach (var argString in singleMethodGenericArgs) genericArguments.Add(FindType(context, argString)); method = method.MakeInstantiatedMethod(genericArguments.ToArray()); } @@ -786,7 +810,7 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont return method; } - private static string CreateReproArgumentString(MethodDesc method) + internal static string CreateReproArgumentString(MethodDesc method) { StringBuilder sb = new StringBuilder(); @@ -945,15 +969,6 @@ internal static bool IsValidPublicKey(byte[] blob) return true; } - private static int Main(string[] args) => - new CommandLineBuilder(new Crossgen2RootCommand(args)) - .UseVersionOption() - .UseHelp(context => context.HelpBuilder.CustomizeLayout(GetExtendedHelp)) - .UseParseErrorReporting() - .Build() - .Invoke(args); - - private T Get(Option option) => _command.Result.GetValueForOption(option); private static int Main(string[] args) => diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 37720091236db..9317b7c2d6c8d 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -390,4 +390,7 @@ The following CPU names are predefined groups of instruction sets and can be used in --instruction-set too: - \ No newline at end of file + + Microsoft (R) .NET Crossgen2 Compiler + + diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.props b/src/coreclr/tools/aot/crossgen2/crossgen2.props index d544530d81378..3eaa3409c82a2 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.props +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.props @@ -29,6 +29,10 @@ + + + + @@ -36,21 +40,7 @@ - - - - - - - - - - - - - - - + From 4e0cc4830e95b7d14afd8df23a1f0ce29fd622f7 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Wed, 13 Jul 2022 12:25:35 +0300 Subject: [PATCH 03/12] Fix release build --- .../tools/aot/ILCompiler/ILCompilerRootCommand.cs | 10 +++++----- .../tools/aot/crossgen2/Crossgen2RootCommand.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 3e0df10675099..f99a2bf06225b 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -247,13 +247,13 @@ public ILCompilerRootCommand(string[] args) : base("Microsoft (R) .NET Native IL #else catch (Exception e) { - context.Console.ResetColor(); - context.Console.ForegroundColor = ConsoleColor.Red; + Console.ResetColor(); + Console.ForegroundColor = ConsoleColor.Red; - context.Console.Error.WriteLine("Error: " + e.Message); - context.Console.Error.WriteLine(e.ToString()); + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); - context.Console.ResetColor(); + Console.ResetColor(); context.ExitCode = 1; } diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index 1af04c2632d70..b1cb833597186 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -265,13 +265,13 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) #else catch (Exception e) { - context.Console.ResetColor(); - context.Console.ForegroundColor = ConsoleColor.Red; + Console.ResetColor(); + Console.ForegroundColor = ConsoleColor.Red; - context.Console.Error.WriteLine("Error: " + e.Message); - context.Console.Error.WriteLine(e.ToString()); + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); - context.Console.ResetColor(); + Console.ResetColor(); context.ExitCode = 1; } From a275b1309eec767107b1edb2a4bcba92505499bd Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Wed, 13 Jul 2022 13:52:35 +0300 Subject: [PATCH 04/12] Fix test build --- .../ILCompiler.Compiler.Tests/DependencyGraphTests.cs | 2 +- .../TestCasesRunner/ILCompilerDriver.cs | 1 + src/coreclr/tools/aot/crossgen2/Program.cs | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index dc1bf6c9cd0c4..369d28271e5e7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -72,7 +72,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider), UsageBasedMetadataGenerationOptions.None, - Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty()); + Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty(), Array.Empty()); CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) .UseILProvider(ilProvider); diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs index a0eff369ee98a..bf2fa320c4263 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs @@ -71,6 +71,7 @@ public void Trim (ILCompilerOptions options, ILogWriter logWriter) logger, Array.Empty> (), Array.Empty (), + Array.Empty (), options.TrimAssemblies.ToArray ()); CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index dbf262653b08c..1385e06dd4322 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -41,6 +41,7 @@ internal class Program private readonly string _outputFilePath; private readonly TargetArchitecture _targetArchitecture; private readonly TargetOS _targetOS; + private readonly bool _armelAbi; public Program(Crossgen2RootCommand command) { @@ -51,6 +52,12 @@ public Program(Crossgen2RootCommand command) _outputFilePath = Get(command.OutputFilePath); _targetOS = Get(command.TargetOS); _targetArchitecture = Get(command.TargetArchitecture); + _armelAbi = _targetArchitecture == TargetArchitecture.ARMEL; + + if (_armelAbi) + { + _targetArchitecture = TargetArchitecture.ARM; + } if (command.Result.GetValueForOption(command.WaitForDebugger)) { @@ -181,7 +188,7 @@ public int Run() SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _targetArchitecture == TargetArchitecture.ARMEL ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); ConfigureImageBase(targetDetails); From e2354215c357dae9953de82ec7dd4b48237b430b Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Wed, 13 Jul 2022 14:44:52 +0300 Subject: [PATCH 05/12] =?UTF-8?q?ilc:=20--insructionset,=20crossgen2:=20--?= =?UTF-8?q?instruction-set=20=F0=9F=A5=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index b1cb833597186..3b2cc5755c180 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -22,7 +22,7 @@ internal class Crossgen2RootCommand : RootCommand public Option> ReferenceFilePaths { get; } = new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), false, SR.ReferenceFiles); public Option InstructionSet { get; } = - new(new[] { "--instructionset" }, SR.InstructionSets); + new(new[] { "--instruction-set" }, SR.InstructionSets); public Option MibcFilePaths { get; } = new(new[] { "--mibc", "-m" }, () => Array.Empty(), SR.MibcFiles); public Option OutputFilePath { get; } = From 1aa261fbb7668fda27b9a2a040ef8be3ea27a242 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Wed, 13 Jul 2022 22:13:29 +0300 Subject: [PATCH 06/12] Fix a couple of defaults --- src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs | 2 +- src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index f99a2bf06225b..0b24470c74cfb 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -18,7 +18,7 @@ internal class ILCompilerRootCommand : RootCommand public Argument> InputFilePaths { get; } = new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; public Option> ReferenceFiles { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), false, "Reference file(s) for compilation"); + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, "Reference file(s) for compilation"); public Option OutputFilePath { get; } = new(new[] { "--out", "-o" }, "Output file path"); public Option Optimize { get; } = diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index 3b2cc5755c180..d3ac826e39747 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -20,7 +20,7 @@ internal class Crossgen2RootCommand : RootCommand public Option> UnrootedInputFilePaths { get; } = new(new[] { "--unrooted-input-file-paths", "-u" }, result => Helpers.BuildPathDictionay(result.Tokens, true), true, SR.UnrootedInputFilesToCompile); public Option> ReferenceFilePaths { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), false, SR.ReferenceFiles); + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.ReferenceFiles); public Option InstructionSet { get; } = new(new[] { "--instruction-set" }, SR.InstructionSets); public Option MibcFilePaths { get; } = @@ -28,7 +28,7 @@ internal class Crossgen2RootCommand : RootCommand public Option OutputFilePath { get; } = new(new[] { "--out", "-o" }, SR.OutputFilePath); public Option CompositeRootPath { get; } = - new(new[] { "--compositerootpath", "-crp" }, SR.CompositeRootPath); + new(new[] { "--compositerootpath", "--crp" }, SR.CompositeRootPath); public Option Optimize { get; } = new(new[] { "--optimize", "-O" }, SR.EnableOptimizationsOption); public Option OptimizeDisabled { get; } = @@ -40,7 +40,7 @@ internal class Crossgen2RootCommand : RootCommand public Option InputBubble { get; } = new(new[] { "--inputbubble" }, SR.InputBubbleOption); public Option> InputBubbleReferenceFilePaths { get; } = - new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionay(result.Tokens, false), false, SR.InputBubbleReferenceFiles); + new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.InputBubbleReferenceFiles); public Option Composite { get; } = new(new[] { "--composite" }, SR.CompositeBuildMode); public Option CompositeKeyFile { get; } = @@ -74,7 +74,7 @@ internal class Crossgen2RootCommand : RootCommand public Option ImageBase { get; } = new(new[] { "--imagebase" }, SR.ImageBase); public Option TargetArchitecture { get; } = - new(new[] { "--targetarch" }, result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetArchOption); + new(new[] { "--targetarch" }, result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetArchOption) { Arity = ArgumentArity.OneOrMore }; public Option TargetOS { get; } = new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetOSOption); public Option JitPath { get; } = From 93b0586f367603e94ad8efbb9f3399d30941115a Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Thu, 14 Jul 2022 00:20:00 +0300 Subject: [PATCH 07/12] Fix repro package arguments for cg2 --- src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index d3ac826e39747..02d568d9e76b0 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -252,7 +252,7 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) // + a rsp file that should work to directly run out of the zip file Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, - context.ParseResult, new[] { "r", "reference", "m", "mibc", "rdxml", "directpinvokelist" }); + context.ParseResult, new[] { "r", "reference", "u", "unrooted-input-file-paths", "m", "mibc", "inputbubbleref" }); } context.ExitCode = new Program(this).Run(); From f896f9363992b60e9bb2ede247fe2d197e140b50 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Thu, 14 Jul 2022 01:43:21 +0300 Subject: [PATCH 08/12] Address CR feedback --- .../tools/Common/CommandLineHelpers.cs | 399 +++-- .../TypeSystem/Common/TargetArchitecture.cs | 1 - .../aot/ILCompiler/ILCompilerRootCommand.cs | 615 +++---- src/coreclr/tools/aot/ILCompiler/Program.cs | 1145 ++++++------- .../aot/crossgen2/Crossgen2RootCommand.cs | 595 +++---- src/coreclr/tools/aot/crossgen2/Program.cs | 1490 ++++++++--------- 6 files changed, 2126 insertions(+), 2119 deletions(-) diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs index e9f17b35f4dce..909fda3ae6bf1 100644 --- a/src/coreclr/tools/Common/CommandLineHelpers.cs +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -10,279 +10,278 @@ using Internal.TypeSystem; -namespace System.CommandLine; - -internal class CommandLineException : Exception -{ - public CommandLineException(string message) : base(message) { } -} - -// -// Helpers for command line processing -// -internal static class Helpers +namespace System.CommandLine { - public const string DefaultSystemModule = "System.Private.CoreLib"; + internal class CommandLineException : Exception + { + public CommandLineException(string message) : base(message) { } + } - public static Dictionary BuildPathDictionay(IReadOnlyList tokens, bool strict) + // + // Helpers for command line processing + // + internal static class Helpers { - Dictionary dictionary = new(StringComparer.OrdinalIgnoreCase); + public const string DefaultSystemModule = "System.Private.CoreLib"; - foreach (Token token in tokens) + public static Dictionary BuildPathDictionay(IReadOnlyList tokens, bool strict) { - AppendExpandedPaths(dictionary, token.Value, strict); - } + Dictionary dictionary = new(StringComparer.OrdinalIgnoreCase); - return dictionary; - } + foreach (Token token in tokens) + { + AppendExpandedPaths(dictionary, token.Value, strict); + } - public static TargetOS GetTargetOS(string token) - { - if(string.IsNullOrEmpty(token)) + return dictionary; + } + + public static TargetOS GetTargetOS(string token) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if(string.IsNullOrEmpty(token)) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return Internal.TypeSystem.TargetOS.Windows; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + return Internal.TypeSystem.TargetOS.Linux; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return Internal.TypeSystem.TargetOS.OSX; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + return Internal.TypeSystem.TargetOS.FreeBSD; + + throw new NotImplementedException(); + } + + if (token.Equals("windows", StringComparison.OrdinalIgnoreCase)) return Internal.TypeSystem.TargetOS.Windows; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + else if (token.Equals("linux", StringComparison.OrdinalIgnoreCase)) return Internal.TypeSystem.TargetOS.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + else if (token.Equals("osx", StringComparison.OrdinalIgnoreCase)) return Internal.TypeSystem.TargetOS.OSX; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) - return Internal.TypeSystem.TargetOS.FreeBSD; - throw new NotImplementedException(); + throw new CommandLineException($"Target OS '{token}' is not supported"); } - if (token.Equals("windows", StringComparison.OrdinalIgnoreCase)) - return Internal.TypeSystem.TargetOS.Windows; - else if (token.Equals("linux", StringComparison.OrdinalIgnoreCase)) - return Internal.TypeSystem.TargetOS.Linux; - else if (token.Equals("osx", StringComparison.OrdinalIgnoreCase)) - return Internal.TypeSystem.TargetOS.OSX; - - throw new CommandLineException($"Target OS '{token}' is not supported"); - } - - public static TargetArchitecture GetTargetArchitecture(string token) - { - if(string.IsNullOrEmpty(token)) + public static TargetArchitecture GetTargetArchitecture(string token) { - return RuntimeInformation.ProcessArchitecture switch + if(string.IsNullOrEmpty(token)) { - Architecture.X86 => Internal.TypeSystem.TargetArchitecture.X86, - Architecture.X64 => Internal.TypeSystem.TargetArchitecture.X64, - Architecture.Arm => Internal.TypeSystem.TargetArchitecture.ARM, - Architecture.Arm64 => Internal.TypeSystem.TargetArchitecture.ARM64, - _ => throw new NotImplementedException() - }; - } - - if (token.Equals("x86", StringComparison.OrdinalIgnoreCase)) - return Internal.TypeSystem.TargetArchitecture.X86; - else if (token.Equals("x64", StringComparison.OrdinalIgnoreCase)) - return Internal.TypeSystem.TargetArchitecture.X64; - else if (token.Equals("arm", StringComparison.OrdinalIgnoreCase)) - return Internal.TypeSystem.TargetArchitecture.ARM; - else if (token.Equals("arm64", StringComparison.OrdinalIgnoreCase)) - return Internal.TypeSystem.TargetArchitecture.ARM64; - else if (token.Equals("armel", StringComparison.OrdinalIgnoreCase)) - return Internal.TypeSystem.TargetArchitecture.ARMEL; - - throw new CommandLineException($"Target architecture '{token}' is not supported"); - } + return RuntimeInformation.ProcessArchitecture switch + { + Architecture.X86 => Internal.TypeSystem.TargetArchitecture.X86, + Architecture.X64 => Internal.TypeSystem.TargetArchitecture.X64, + Architecture.Arm => Internal.TypeSystem.TargetArchitecture.ARM, + Architecture.Arm64 => Internal.TypeSystem.TargetArchitecture.ARM64, + _ => throw new NotImplementedException() + }; + } - public static void MakeReproPackage(string makeReproPath, string outputFilePath, string[] args, ParseResult res, IEnumerable inputOptions) - { - Directory.CreateDirectory(makeReproPath); + if (token.Equals("x86", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.X86; + else if (token.Equals("x64", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.X64; + else if (token.Equals("arm", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.ARM; + else if (token.Equals("arm64", StringComparison.OrdinalIgnoreCase)) + return Internal.TypeSystem.TargetArchitecture.ARM64; - List details = new List(); - details.Add("Tool version"); - try - { - details.Add(Environment.GetCommandLineArgs()[0]); - } - catch { } - try - { - details.Add(System.Diagnostics.FileVersionInfo.GetVersionInfo(Environment.GetCommandLineArgs()[0]).ToString()); + throw new CommandLineException($"Target architecture '{token}' is not supported"); } - catch { } - details.Add("------------------------"); - details.Add("Actual Command Line Args"); - details.Add("------------------------"); - details.AddRange(args); - foreach (string arg in args) + public static void MakeReproPackage(string makeReproPath, string outputFilePath, string[] args, ParseResult res, IEnumerable inputOptions) { - if (arg.StartsWith('@')) + Directory.CreateDirectory(makeReproPath); + + List details = new List(); + details.Add("Tool version"); + try { - string rspFileName = arg.Substring(1); - details.Add("------------------------"); - details.Add(rspFileName); - details.Add("------------------------"); - try + details.Add(Environment.GetCommandLineArgs()[0]); + } + catch { } + try + { + details.Add(System.Diagnostics.FileVersionInfo.GetVersionInfo(Environment.GetCommandLineArgs()[0]).ToString()); + } + catch { } + + details.Add("------------------------"); + details.Add("Actual Command Line Args"); + details.Add("------------------------"); + details.AddRange(args); + foreach (string arg in args) + { + if (arg.StartsWith('@')) { - details.AddRange(File.ReadAllLines(rspFileName)); + string rspFileName = arg.Substring(1); + details.Add("------------------------"); + details.Add(rspFileName); + details.Add("------------------------"); + try + { + details.AddRange(File.ReadAllLines(rspFileName)); + } + catch { } } - catch { } } - } - - HashCode hashCodeOfArgs = new HashCode(); - foreach (string s in details) - hashCodeOfArgs.Add(s); - string zipFileName = ((uint)hashCodeOfArgs.ToHashCode()).ToString(); + HashCode hashCodeOfArgs = new HashCode(); + foreach (string s in details) + hashCodeOfArgs.Add(s); - if (outputFilePath != null) - zipFileName = zipFileName + "_" + Path.GetFileName(outputFilePath); + string zipFileName = ((uint)hashCodeOfArgs.ToHashCode()).ToString(); - zipFileName = Path.Combine(makeReproPath, Path.ChangeExtension(zipFileName, ".zip")); + if (outputFilePath != null) + zipFileName = zipFileName + "_" + Path.GetFileName(outputFilePath); - Console.WriteLine($"Creating {zipFileName}"); - using (var archive = ZipFile.Open(zipFileName, ZipArchiveMode.Create)) - { - ZipArchiveEntry commandEntry = archive.CreateEntry("command.txt"); - using (StreamWriter writer = new StreamWriter(commandEntry.Open())) - { - foreach (string s in details) - writer.WriteLine(s); - } - - HashSet inputOptionNames = new HashSet(inputOptions); - Dictionary inputToReproPackageFileName = new(); + zipFileName = Path.Combine(makeReproPath, Path.ChangeExtension(zipFileName, ".zip")); - List rspFile = new List(); - foreach (var option in res.CommandResult.Command.Options) + Console.WriteLine($"Creating {zipFileName}"); + using (var archive = ZipFile.Open(zipFileName, ZipArchiveMode.Create)) { - if (!res.HasOption(option) || option.Name == "make-repro-path") + ZipArchiveEntry commandEntry = archive.CreateEntry("command.txt"); + using (StreamWriter writer = new StreamWriter(commandEntry.Open())) { - continue; + foreach (string s in details) + writer.WriteLine(s); } - IValueDescriptor descriptor = option; - object val = res.CommandResult.GetValueForOption(option); - if (val is not null && !(descriptor.HasDefaultValue && descriptor.GetDefaultValue().Equals(val))) + HashSet inputOptionNames = new HashSet(inputOptions); + Dictionary inputToReproPackageFileName = new(); + + List rspFile = new List(); + foreach (var option in res.CommandResult.Command.Options) { - if (val is IEnumerable values) + if (!res.HasOption(option) || option.Name == "make-repro-path") { - if (inputOptionNames.Contains(option.Name)) + continue; + } + + IValueDescriptor descriptor = option; + object val = res.CommandResult.GetValueForOption(option); + if (val is not null && !(descriptor.HasDefaultValue && descriptor.GetDefaultValue().Equals(val))) + { + if (val is IEnumerable values) { - Dictionary dictionary = new(); - foreach (string optInList in values) + if (inputOptionNames.Contains(option.Name)) { - Helpers.AppendExpandedPaths(dictionary, optInList, false); + Dictionary dictionary = new(); + foreach (string optInList in values) + { + Helpers.AppendExpandedPaths(dictionary, optInList, false); + } + foreach (string inputFile in dictionary.Values) + { + rspFile.Add($"--{option.Name}:{ConvertFromInputPathToReproPackagePath(inputFile)}"); + } } - foreach (string inputFile in dictionary.Values) + else { - rspFile.Add($"--{option.Name}:{ConvertFromInputPathToReproPackagePath(inputFile)}"); + foreach (string optInList in values) + { + rspFile.Add($"--{option.Name}:{optInList}"); + } } } else { - foreach (string optInList in values) - { - rspFile.Add($"--{option.Name}:{optInList}"); - } + rspFile.Add($"--{option.Name}:{val}"); } } - else - { - rspFile.Add($"--{option.Name}:{val}"); - } } - } - foreach (var argument in res.CommandResult.Command.Arguments) - { - object val = res.CommandResult.GetValueForArgument(argument); - if (val is IEnumerable values) + foreach (var argument in res.CommandResult.Command.Arguments) { - foreach (string optInList in values) + object val = res.CommandResult.GetValueForArgument(argument); + if (val is IEnumerable values) { - rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)optInList)}"); + foreach (string optInList in values) + { + rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)optInList)}"); + } + } + else + { + rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)val)}"); } } - else - { - rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)val)}"); - } - } - ZipArchiveEntry rspEntry = archive.CreateEntry("repro.rsp"); - using (StreamWriter writer = new StreamWriter(rspEntry.Open())) - { - foreach (string s in rspFile) - writer.WriteLine(s); - } - - string ConvertFromInputPathToReproPackagePath(string inputPath) - { - if (inputToReproPackageFileName.TryGetValue(inputPath, out string reproPackagePath)) + ZipArchiveEntry rspEntry = archive.CreateEntry("repro.rsp"); + using (StreamWriter writer = new StreamWriter(rspEntry.Open())) { - return reproPackagePath; + foreach (string s in rspFile) + writer.WriteLine(s); } - try + string ConvertFromInputPathToReproPackagePath(string inputPath) { - string inputFileDir = inputToReproPackageFileName.Count.ToString(); - reproPackagePath = Path.Combine(inputFileDir, Path.GetFileName(inputPath)); - archive.CreateEntryFromFile(inputPath, reproPackagePath); - inputToReproPackageFileName.Add(inputPath, reproPackagePath); + if (inputToReproPackageFileName.TryGetValue(inputPath, out string reproPackagePath)) + { + return reproPackagePath; + } - return reproPackagePath; - } - catch - { - return inputPath; + try + { + string inputFileDir = inputToReproPackageFileName.Count.ToString(); + reproPackagePath = Path.Combine(inputFileDir, Path.GetFileName(inputPath)); + archive.CreateEntryFromFile(inputPath, reproPackagePath); + inputToReproPackageFileName.Add(inputPath, reproPackagePath); + + return reproPackagePath; + } + catch + { + return inputPath; + } } } } - } - // Helper to create a collection of paths unique in their simple names. - private static void AppendExpandedPaths(Dictionary dictionary, string pattern, bool strict) - { - bool empty = true; - string directoryName = Path.GetDirectoryName(pattern); - string searchPattern = Path.GetFileName(pattern); + // Helper to create a collection of paths unique in their simple names. + private static void AppendExpandedPaths(Dictionary dictionary, string pattern, bool strict) + { + bool empty = true; + string directoryName = Path.GetDirectoryName(pattern); + string searchPattern = Path.GetFileName(pattern); - if (directoryName == "") - directoryName = "."; + if (directoryName == "") + directoryName = "."; - if (Directory.Exists(directoryName)) - { - foreach (string fileName in Directory.EnumerateFiles(directoryName, searchPattern)) + if (Directory.Exists(directoryName)) { - string fullFileName = Path.GetFullPath(fileName); + foreach (string fileName in Directory.EnumerateFiles(directoryName, searchPattern)) + { + string fullFileName = Path.GetFullPath(fileName); - string simpleName = Path.GetFileNameWithoutExtension(fileName); + string simpleName = Path.GetFileNameWithoutExtension(fileName); - if (dictionary.ContainsKey(simpleName)) - { - if (strict) + if (dictionary.ContainsKey(simpleName)) { - throw new CommandLineException("Multiple input files matching same simple name " + - fullFileName + " " + dictionary[simpleName]); + if (strict) + { + throw new CommandLineException("Multiple input files matching same simple name " + + fullFileName + " " + dictionary[simpleName]); + } + } + else + { + dictionary.Add(simpleName, fullFileName); } - } - else - { - dictionary.Add(simpleName, fullFileName); - } - empty = false; + empty = false; + } } - } - if (empty) - { - if (strict) - { - throw new CommandLineException("No files matching " + pattern); - } - else + if (empty) { - Console.WriteLine("Warning: No files matching " + pattern); + if (strict) + { + throw new CommandLineException("No files matching " + pattern); + } + else + { + Console.WriteLine("Warning: No files matching " + pattern); + } } } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs index 4d16b7250ffc7..b3b587d1432f4 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs @@ -14,7 +14,6 @@ public enum TargetArchitecture Unknown, ARM, ARM64, - ARMEL, X64, X86, Wasm32, diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 0b24470c74cfb..9862afac8532e 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -11,353 +11,354 @@ using Internal.TypeSystem; -namespace ILCompiler; - -internal class ILCompilerRootCommand : RootCommand +namespace ILCompiler { - public Argument> InputFilePaths { get; } = - new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; - public Option> ReferenceFiles { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, "Reference file(s) for compilation"); - public Option OutputFilePath { get; } = - new(new[] { "--out", "-o" }, "Output file path"); - public Option Optimize { get; } = - new(new[] { "--optimize", "-O" }, "Enable optimizations"); - public Option OptimizeSpace { get; } = - new(new[] { "--optimize-space", "-Os" }, "Enable optimizations, favor code space"); - public Option OptimizeTime { get; } = - new(new[] { "--optimize-time", "-Ot" }, "Enable optimizations, favor code speed"); - public Option MibcFilePaths { get; } = - new(new[] { "--mibc", "-m" }, () => Array.Empty(), "Mibc file(s) for profile guided optimization"); - public Option EnableDebugInfo { get; } = - new(new[] { "--debug", "-g" }, "Emit debugging information"); - public Option UseDwarf5 { get; } = - new(new[] { "--gdwarf-5" }, "Generate source-level debug information with dwarf version 5"); - public Option NativeLib { get; } = - new(new[] { "--nativelib" }, "Compile as static or shared library"); - public Option ExportsFile { get; } = - new(new[] { "--exportsfile" }, "File to write exported method definitions"); - public Option DgmlLogFileName { get; } = - new(new[] { "--dgmllog" }, "Save result of dependency analysis as DGML"); - public Option GenerateFullDgmlLog { get; } = - new(new[] { "--fulllog" }, "Save detailed log of dependency analysis"); - public Option ScanDgmlLogFileName { get; } = - new(new[] { "--scandgmllog" }, "Save result of scanner dependency analysis as DGML"); - public Option GenerateFullScanDgmlLog { get; } = - new(new[] { "--scanfulllog" }, "Save detailed log of scanner dependency analysis"); - public Option IsVerbose { get; } = - new(new[] { "--verbose" }, "Enable verbose logging"); - public Option SystemModuleName { get; } = - new(new[] { "--systemmodule" }, () => Helpers.DefaultSystemModule, "System module name (default: System.Private.CoreLib)"); - public Option MultiFile { get; } = - new(new[] { "--multifile" }, "Compile only input files (do not compile referenced assemblies)"); - public Option WaitForDebugger { get; } = - new(new[] { "--waitfordebugger" }, "Pause to give opportunity to attach debugger"); - public Option Resilient { get; } = - new(new[] { "--resilient" }, "Ignore unresolved types, methods, and assemblies. Defaults to false"); - public Option CodegenOptions { get; } = - new(new[] { "--codegenopt" }, () => Array.Empty(), "Define a codegen option"); - public Option RdXmlFilePaths { get; } = - new(new[] { "--rdxml" }, () => Array.Empty(), "RD.XML file(s) for compilation"); - public Option MapFileName { get; } = - new(new[] { "--map" }, "Generate a map file"); - public Option MetadataLogFileName { get; } = - new(new[] { "--metadatalog" }, "Generate a metadata log file"); - public Option NoMetadataBlocking { get; } = - new(new[] { "--nometadatablocking" }, "Ignore metadata blocking for internal implementation details"); - public Option CompleteTypesMetadata { get; } = - new(new[] { "--completetypemetadata" }, "Generate complete metadata for types"); - public Option ReflectionData { get; } = - new(new[] { "--reflectiondata" }, "Reflection data to generate (one of: all, none)"); - public Option ScanReflection { get; } = - new(new[] { "--scanreflection" }, "Scan IL for reflection patterns"); - public Option UseScanner { get; } = - new(new[] { "--scan" }, "Use IL scanner to generate optimized code (implied by -O)"); - public Option NoScanner { get; } = - new(new[] { "--noscan" }, "Do not use IL scanner to generate optimized code"); - public Option IlDump { get; } = - new(new[] { "--ildump" }, "Dump IL assembly listing for compiler-generated IL"); - public Option EmitStackTraceData { get; } = - new(new[] { "--stacktracedata" }, "Emit data to support generating stack trace strings at runtime"); - public Option MethodBodyFolding { get; } = - new(new[] { "--methodbodyfolding" }, "Fold identical method bodies"); - public Option InitAssemblies { get; } = - new(new[] { "--initassembly" }, () => Array.Empty(), "Assembly(ies) with a library initializer"); - public Option AppContextSwitches { get; } = - new(new[] { "--appcontextswitch" }, () => Array.Empty(), "System.AppContext switches to set (format: 'Key=Value')"); - public Option FeatureSwitches { get; } = - new(new[] { "--feature" }, () => Array.Empty(), "Feature switches to apply (format: 'Namespace.Name=[true|false]'"); - public Option RuntimeOptions { get; } = - new(new[] { "--runtimeopt" }, () => Array.Empty(), "Runtime options to set"); - public Option Parallelism { get; } = - new(new[] { "--parallelism" }, () => Environment.ProcessorCount, "Maximum number of threads to use during compilation"); - public Option InstructionSet { get; } = - new(new[] { "--instructionset" }, "Instruction set to allow or disallow"); - public Option Guard { get; } = - new(new[] { "--guard" }, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); - public Option PreinitStatics { get; } = - new(new[] { "--preinitstatics" }, "Interpret static constructors at compile time if possible (implied by -O)"); - public Option NoPreinitStatics { get; } = - new(new[] { "--nopreinitstatics" }, "Do not interpret static constructors at compile time"); - public Option SuppressedWarnings { get; } = - new(new[] { "--nowarn" }, () => Array.Empty(), "Disable specific warning messages"); - public Option SingleWarn { get; } = - new(new[] { "--singlewarn" }, "Generate single AOT/trimming warning per assembly"); - public Option SingleWarnEnabledAssemblies { get; } = - new(new[] { "--singlewarnassembly" }, () => Array.Empty(), "Generate single AOT/trimming warning for given assembly"); - public Option SingleWarnDisabledAssemblies { get; } = - new(new[] { "--nosinglewarnassembly" }, () => Array.Empty(), "Expand AOT/trimming warnings for given assembly"); - public Option DirectPInvokes { get; } = - new(new[] { "--directpinvoke" }, () => Array.Empty(), "PInvoke to call directly"); - public Option DirectPInvokeLists { get; } = - new(new[] { "--directpinvokelist" }, () => Array.Empty(), "File with list of PInvokes to call directly"); - public Option MaxGenericCycle { get; } = - new(new[] { "--maxgenericcycle" }, () => CompilerTypeSystemContext.DefaultGenericCycleCutoffPoint, "Max depth of generic cycle"); - public Option RootedAssemblies { get; } = - new(new[] { "--root" }, () => Array.Empty(), "Fully generate given assembly"); - public Option> ConditionallyRootedAssemblies { get; } = - new(new[] { "--conditionalroot" }, result => ILLinkify(result.Tokens, true), true, "Fully generate given assembly if it's used"); - public Option> TrimmedAssemblies { get; } = - new(new[] { "--trim" }, result => ILLinkify(result.Tokens, true), true, "Trim the specified assembly"); - public Option RootDefaultAssemblies { get; } = - new(new[] { "--defaultrooting" }, "Root assemblies that are not marked [IsTrimmable]"); - public Option TargetArchitecture { get; } = - new(new[] { "--targetarch" }, result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, "Target architecture for cross compilation"); - public Option TargetOS { get; } = - new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, "Target OS for cross compilation"); - public Option JitPath { get; } = - new(new[] { "--jitpath" }, "Path to JIT compiler library"); - public Option SingleMethodTypeName { get; } = - new(new[] { "--singlemethodtypename" }, "Single method compilation: assembly-qualified name of the owning type"); - public Option SingleMethodName { get; } = - new(new[] { "--singlemethodname" }, "Single method compilation: name of the method"); - public Option SingleMethodGenericArgs { get; } = - new(new[] { "--singlemethodgenericarg" }, "Single method compilation: generic arguments to the method"); - public Option MakeReproPath { get; } = - new(new[] { "--make-repro-path" }, "Path where to place a repro package"); - - public OptimizationMode OptimizationMode { get; private set; } - public ParseResult Result; - - public ILCompilerRootCommand(string[] args) : base("Microsoft (R) .NET Native IL Compiler") + internal class ILCompilerRootCommand : RootCommand { - AddArgument(InputFilePaths); - AddOption(ReferenceFiles); - AddOption(OutputFilePath); - AddOption(Optimize); - AddOption(OptimizeSpace); - AddOption(OptimizeTime); - AddOption(MibcFilePaths); - AddOption(EnableDebugInfo); - AddOption(UseDwarf5); - AddOption(NativeLib); - AddOption(ExportsFile); - AddOption(DgmlLogFileName); - AddOption(GenerateFullDgmlLog); - AddOption(ScanDgmlLogFileName); - AddOption(GenerateFullScanDgmlLog); - AddOption(IsVerbose); - AddOption(SystemModuleName); - AddOption(MultiFile); - AddOption(WaitForDebugger); - AddOption(Resilient); - AddOption(CodegenOptions); - AddOption(RdXmlFilePaths); - AddOption(MapFileName); - AddOption(MetadataLogFileName); - AddOption(NoMetadataBlocking); - AddOption(CompleteTypesMetadata); - AddOption(ReflectionData); - AddOption(ScanReflection); - AddOption(UseScanner); - AddOption(NoScanner); - AddOption(IlDump); - AddOption(EmitStackTraceData); - AddOption(MethodBodyFolding); - AddOption(InitAssemblies); - AddOption(AppContextSwitches); - AddOption(FeatureSwitches); - AddOption(RuntimeOptions); - AddOption(Parallelism); - AddOption(InstructionSet); - AddOption(Guard); - AddOption(PreinitStatics); - AddOption(NoPreinitStatics); - AddOption(SuppressedWarnings); - AddOption(SingleWarn); - AddOption(SingleWarnEnabledAssemblies); - AddOption(SingleWarnDisabledAssemblies); - AddOption(DirectPInvokes); - AddOption(DirectPInvokeLists); - AddOption(MaxGenericCycle); - AddOption(RootedAssemblies); - AddOption(ConditionallyRootedAssemblies); - AddOption(TrimmedAssemblies); - AddOption(RootDefaultAssemblies); - AddOption(TargetArchitecture); - AddOption(TargetOS); - AddOption(JitPath); - AddOption(SingleMethodTypeName); - AddOption(SingleMethodName); - AddOption(SingleMethodGenericArgs); - AddOption(MakeReproPath); - - this.SetHandler(context => + public Argument> InputFilePaths { get; } = + new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; + public Option> ReferenceFiles { get; } = + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, "Reference file(s) for compilation"); + public Option OutputFilePath { get; } = + new(new[] { "--out", "-o" }, "Output file path"); + public Option Optimize { get; } = + new(new[] { "--optimize", "-O" }, "Enable optimizations"); + public Option OptimizeSpace { get; } = + new(new[] { "--optimize-space", "-Os" }, "Enable optimizations, favor code space"); + public Option OptimizeTime { get; } = + new(new[] { "--optimize-time", "-Ot" }, "Enable optimizations, favor code speed"); + public Option MibcFilePaths { get; } = + new(new[] { "--mibc", "-m" }, () => Array.Empty(), "Mibc file(s) for profile guided optimization"); + public Option EnableDebugInfo { get; } = + new(new[] { "--debug", "-g" }, "Emit debugging information"); + public Option UseDwarf5 { get; } = + new(new[] { "--gdwarf-5" }, "Generate source-level debug information with dwarf version 5"); + public Option NativeLib { get; } = + new(new[] { "--nativelib" }, "Compile as static or shared library"); + public Option ExportsFile { get; } = + new(new[] { "--exportsfile" }, "File to write exported method definitions"); + public Option DgmlLogFileName { get; } = + new(new[] { "--dgmllog" }, "Save result of dependency analysis as DGML"); + public Option GenerateFullDgmlLog { get; } = + new(new[] { "--fulllog" }, "Save detailed log of dependency analysis"); + public Option ScanDgmlLogFileName { get; } = + new(new[] { "--scandgmllog" }, "Save result of scanner dependency analysis as DGML"); + public Option GenerateFullScanDgmlLog { get; } = + new(new[] { "--scanfulllog" }, "Save detailed log of scanner dependency analysis"); + public Option IsVerbose { get; } = + new(new[] { "--verbose" }, "Enable verbose logging"); + public Option SystemModuleName { get; } = + new(new[] { "--systemmodule" }, () => Helpers.DefaultSystemModule, "System module name (default: System.Private.CoreLib)"); + public Option MultiFile { get; } = + new(new[] { "--multifile" }, "Compile only input files (do not compile referenced assemblies)"); + public Option WaitForDebugger { get; } = + new(new[] { "--waitfordebugger" }, "Pause to give opportunity to attach debugger"); + public Option Resilient { get; } = + new(new[] { "--resilient" }, "Ignore unresolved types, methods, and assemblies. Defaults to false"); + public Option CodegenOptions { get; } = + new(new[] { "--codegenopt" }, () => Array.Empty(), "Define a codegen option"); + public Option RdXmlFilePaths { get; } = + new(new[] { "--rdxml" }, () => Array.Empty(), "RD.XML file(s) for compilation"); + public Option MapFileName { get; } = + new(new[] { "--map" }, "Generate a map file"); + public Option MetadataLogFileName { get; } = + new(new[] { "--metadatalog" }, "Generate a metadata log file"); + public Option NoMetadataBlocking { get; } = + new(new[] { "--nometadatablocking" }, "Ignore metadata blocking for internal implementation details"); + public Option CompleteTypesMetadata { get; } = + new(new[] { "--completetypemetadata" }, "Generate complete metadata for types"); + public Option ReflectionData { get; } = + new(new[] { "--reflectiondata" }, "Reflection data to generate (one of: all, none)"); + public Option ScanReflection { get; } = + new(new[] { "--scanreflection" }, "Scan IL for reflection patterns"); + public Option UseScanner { get; } = + new(new[] { "--scan" }, "Use IL scanner to generate optimized code (implied by -O)"); + public Option NoScanner { get; } = + new(new[] { "--noscan" }, "Do not use IL scanner to generate optimized code"); + public Option IlDump { get; } = + new(new[] { "--ildump" }, "Dump IL assembly listing for compiler-generated IL"); + public Option EmitStackTraceData { get; } = + new(new[] { "--stacktracedata" }, "Emit data to support generating stack trace strings at runtime"); + public Option MethodBodyFolding { get; } = + new(new[] { "--methodbodyfolding" }, "Fold identical method bodies"); + public Option InitAssemblies { get; } = + new(new[] { "--initassembly" }, () => Array.Empty(), "Assembly(ies) with a library initializer"); + public Option AppContextSwitches { get; } = + new(new[] { "--appcontextswitch" }, () => Array.Empty(), "System.AppContext switches to set (format: 'Key=Value')"); + public Option FeatureSwitches { get; } = + new(new[] { "--feature" }, () => Array.Empty(), "Feature switches to apply (format: 'Namespace.Name=[true|false]'"); + public Option RuntimeOptions { get; } = + new(new[] { "--runtimeopt" }, () => Array.Empty(), "Runtime options to set"); + public Option Parallelism { get; } = + new(new[] { "--parallelism" }, () => Environment.ProcessorCount, "Maximum number of threads to use during compilation"); + public Option InstructionSet { get; } = + new(new[] { "--instructionset" }, "Instruction set to allow or disallow"); + public Option Guard { get; } = + new(new[] { "--guard" }, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); + public Option PreinitStatics { get; } = + new(new[] { "--preinitstatics" }, "Interpret static constructors at compile time if possible (implied by -O)"); + public Option NoPreinitStatics { get; } = + new(new[] { "--nopreinitstatics" }, "Do not interpret static constructors at compile time"); + public Option SuppressedWarnings { get; } = + new(new[] { "--nowarn" }, () => Array.Empty(), "Disable specific warning messages"); + public Option SingleWarn { get; } = + new(new[] { "--singlewarn" }, "Generate single AOT/trimming warning per assembly"); + public Option SingleWarnEnabledAssemblies { get; } = + new(new[] { "--singlewarnassembly" }, () => Array.Empty(), "Generate single AOT/trimming warning for given assembly"); + public Option SingleWarnDisabledAssemblies { get; } = + new(new[] { "--nosinglewarnassembly" }, () => Array.Empty(), "Expand AOT/trimming warnings for given assembly"); + public Option DirectPInvokes { get; } = + new(new[] { "--directpinvoke" }, () => Array.Empty(), "PInvoke to call directly"); + public Option DirectPInvokeLists { get; } = + new(new[] { "--directpinvokelist" }, () => Array.Empty(), "File with list of PInvokes to call directly"); + public Option MaxGenericCycle { get; } = + new(new[] { "--maxgenericcycle" }, () => CompilerTypeSystemContext.DefaultGenericCycleCutoffPoint, "Max depth of generic cycle"); + public Option RootedAssemblies { get; } = + new(new[] { "--root" }, () => Array.Empty(), "Fully generate given assembly"); + public Option> ConditionallyRootedAssemblies { get; } = + new(new[] { "--conditionalroot" }, result => ILLinkify(result.Tokens, true), true, "Fully generate given assembly if it's used"); + public Option> TrimmedAssemblies { get; } = + new(new[] { "--trim" }, result => ILLinkify(result.Tokens, true), true, "Trim the specified assembly"); + public Option RootDefaultAssemblies { get; } = + new(new[] { "--defaultrooting" }, "Root assemblies that are not marked [IsTrimmable]"); + public Option TargetArchitecture { get; } = + new(new[] { "--targetarch" }, result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, "Target architecture for cross compilation"); + public Option TargetOS { get; } = + new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, "Target OS for cross compilation"); + public Option JitPath { get; } = + new(new[] { "--jitpath" }, "Path to JIT compiler library"); + public Option SingleMethodTypeName { get; } = + new(new[] { "--singlemethodtypename" }, "Single method compilation: assembly-qualified name of the owning type"); + public Option SingleMethodName { get; } = + new(new[] { "--singlemethodname" }, "Single method compilation: name of the method"); + public Option SingleMethodGenericArgs { get; } = + new(new[] { "--singlemethodgenericarg" }, "Single method compilation: generic arguments to the method"); + public Option MakeReproPath { get; } = + new(new[] { "--make-repro-path" }, "Path where to place a repro package"); + + public OptimizationMode OptimizationMode { get; private set; } + public ParseResult Result; + + public ILCompilerRootCommand(string[] args) : base("Microsoft (R) .NET Native IL Compiler") { - Result = context.ParseResult; - - if (context.ParseResult.GetValueForOption(OptimizeSpace)) - { - OptimizationMode = OptimizationMode.PreferSize; - } - else if (context.ParseResult.GetValueForOption(OptimizeTime)) - { - OptimizationMode = OptimizationMode.PreferSpeed; - } - else if (context.ParseResult.GetValueForOption(Optimize)) - { - OptimizationMode = OptimizationMode.Blended; - } - else + AddArgument(InputFilePaths); + AddOption(ReferenceFiles); + AddOption(OutputFilePath); + AddOption(Optimize); + AddOption(OptimizeSpace); + AddOption(OptimizeTime); + AddOption(MibcFilePaths); + AddOption(EnableDebugInfo); + AddOption(UseDwarf5); + AddOption(NativeLib); + AddOption(ExportsFile); + AddOption(DgmlLogFileName); + AddOption(GenerateFullDgmlLog); + AddOption(ScanDgmlLogFileName); + AddOption(GenerateFullScanDgmlLog); + AddOption(IsVerbose); + AddOption(SystemModuleName); + AddOption(MultiFile); + AddOption(WaitForDebugger); + AddOption(Resilient); + AddOption(CodegenOptions); + AddOption(RdXmlFilePaths); + AddOption(MapFileName); + AddOption(MetadataLogFileName); + AddOption(NoMetadataBlocking); + AddOption(CompleteTypesMetadata); + AddOption(ReflectionData); + AddOption(ScanReflection); + AddOption(UseScanner); + AddOption(NoScanner); + AddOption(IlDump); + AddOption(EmitStackTraceData); + AddOption(MethodBodyFolding); + AddOption(InitAssemblies); + AddOption(AppContextSwitches); + AddOption(FeatureSwitches); + AddOption(RuntimeOptions); + AddOption(Parallelism); + AddOption(InstructionSet); + AddOption(Guard); + AddOption(PreinitStatics); + AddOption(NoPreinitStatics); + AddOption(SuppressedWarnings); + AddOption(SingleWarn); + AddOption(SingleWarnEnabledAssemblies); + AddOption(SingleWarnDisabledAssemblies); + AddOption(DirectPInvokes); + AddOption(DirectPInvokeLists); + AddOption(MaxGenericCycle); + AddOption(RootedAssemblies); + AddOption(ConditionallyRootedAssemblies); + AddOption(TrimmedAssemblies); + AddOption(RootDefaultAssemblies); + AddOption(TargetArchitecture); + AddOption(TargetOS); + AddOption(JitPath); + AddOption(SingleMethodTypeName); + AddOption(SingleMethodName); + AddOption(SingleMethodGenericArgs); + AddOption(MakeReproPath); + + this.SetHandler(context => { - OptimizationMode = OptimizationMode.None; - } + Result = context.ParseResult; - try - { - string makeReproPath = context.ParseResult.GetValueForOption(MakeReproPath); - if (makeReproPath != null) + if (context.ParseResult.GetValueForOption(OptimizeSpace)) { - // Create a repro package in the specified path - // This package will have the set of input files needed for compilation - // + the original command line arguments - // + a rsp file that should work to directly run out of the zip file - - Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, - context.ParseResult, new[] { "r", "reference", "m", "mibc", "rdxml", "directpinvokelist" }); + OptimizationMode = OptimizationMode.PreferSize; + } + else if (context.ParseResult.GetValueForOption(OptimizeTime)) + { + OptimizationMode = OptimizationMode.PreferSpeed; + } + else if (context.ParseResult.GetValueForOption(Optimize)) + { + OptimizationMode = OptimizationMode.Blended; + } + else + { + OptimizationMode = OptimizationMode.None; } - context.ExitCode = new Program(this).Run(); - } + try + { + string makeReproPath = context.ParseResult.GetValueForOption(MakeReproPath); + if (makeReproPath != null) + { + // Create a repro package in the specified path + // This package will have the set of input files needed for compilation + // + the original command line arguments + // + a rsp file that should work to directly run out of the zip file + + Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, + context.ParseResult, new[] { "r", "reference", "m", "mibc", "rdxml", "directpinvokelist" }); + } + + context.ExitCode = new Program(this).Run(); + } #if DEBUG - catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) - { - throw new NotSupportedException(); // Unreachable - } + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } #else - catch (Exception e) - { - Console.ResetColor(); - Console.ForegroundColor = ConsoleColor.Red; + catch (Exception e) + { + Console.ResetColor(); + Console.ForegroundColor = ConsoleColor.Red; - Console.Error.WriteLine("Error: " + e.Message); - Console.Error.WriteLine(e.ToString()); + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); - Console.ResetColor(); + Console.ResetColor(); - context.ExitCode = 1; - } + context.ExitCode = 1; + } #endif - }); - } - - public static IEnumerable GetExtendedHelp(HelpContext _) - { - foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) - yield return sectionDelegate; + }); + } - yield return _ => + public static IEnumerable GetExtendedHelp(HelpContext _) { - Console.WriteLine("Options may be passed on the command line, or via response file. On the command line switch values may be specified by passing " + - "the option followed by a space followed by the value of the option, or by specifying a : between option and switch value. A response file " + - "is specified by passing the @ symbol before the response file name. In a response file all options must be specified on their own lines, and " + - "only the : syntax for switches is supported.\n"); + foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) + yield return sectionDelegate; - Console.WriteLine("Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are " + - "considered to be input files. If no input files begin with '--' then this option is not necessary.\n"); + yield return _ => + { + Console.WriteLine("Options may be passed on the command line, or via response file. On the command line switch values may be specified by passing " + + "the option followed by a space followed by the value of the option, or by specifying a : between option and switch value. A response file " + + "is specified by passing the @ symbol before the response file name. In a response file all options must be specified on their own lines, and " + + "only the : syntax for switches is supported.\n"); - string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64" }; - string[] ValidOS = new string[] { "windows", "linux", "osx" }; + Console.WriteLine("Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are " + + "considered to be input files. If no input files begin with '--' then this option is not necessary.\n"); - Console.WriteLine("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetos", string.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant()); + string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64" }; + string[] ValidOS = new string[] { "windows", "linux", "osx" }; - Console.WriteLine(String.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetarch", String.Join("', '", ValidArchitectures), Helpers.GetTargetArchitecture(null).ToString().ToLowerInvariant())); + Console.WriteLine("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetos", string.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant()); - Console.WriteLine("The allowable values for the --instruction-set option are described in the table below. Each architecture has a different set of valid " + - "instruction sets, and multiple instruction sets may be specified by separating the instructions sets by a ','. For example 'avx2,bmi,lzcnt'"); + Console.WriteLine(String.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetarch", String.Join("', '", ValidArchitectures), Helpers.GetTargetArchitecture(null).ToString().ToLowerInvariant())); - foreach (string arch in ValidArchitectures) - { - Console.Write(arch); - Console.Write(": "); + Console.WriteLine("The allowable values for the --instruction-set option are described in the table below. Each architecture has a different set of valid " + + "instruction sets, and multiple instruction sets may be specified by separating the instructions sets by a ','. For example 'avx2,bmi,lzcnt'"); - TargetArchitecture targetArch = Helpers.GetTargetArchitecture(arch); - bool first = true; - foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) + foreach (string arch in ValidArchitectures) { - // Only instruction sets with are specifiable should be printed to the help text - if (instructionSet.Specifiable) + Console.Write(arch); + Console.Write(": "); + + TargetArchitecture targetArch = Helpers.GetTargetArchitecture(arch); + bool first = true; + foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) { - if (first) - { - first = false; - } - else + // Only instruction sets with are specifiable should be printed to the help text + if (instructionSet.Specifiable) { - Console.Write(", "); + if (first) + { + first = false; + } + else + { + Console.Write(", "); + } + Console.Write(instructionSet.Name); } - Console.Write(instructionSet.Name); - } - Console.WriteLine(); + Console.WriteLine(); + } } - } - Console.WriteLine(); - Console.WriteLine("The following CPU names are predefined groups of instruction sets and can be used in --instruction-set too:"); - Console.WriteLine(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); - }; - } - - private static IEnumerable ILLinkify(IReadOnlyList tokens, bool setDefaultToEmpty) - { - if (tokens.Count == 0) - { - yield return string.Empty; - yield break; + Console.WriteLine(); + Console.WriteLine("The following CPU names are predefined groups of instruction sets and can be used in --instruction-set too:"); + Console.WriteLine(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); + }; } - foreach(Token token in tokens) + private static IEnumerable ILLinkify(IReadOnlyList tokens, bool setDefaultToEmpty) { - string rootedAssembly = token.Value; - - // For compatibility with IL Linker, the parameter could be a file name or an assembly name. - // This is the logic IL Linker uses to decide how to interpret the string. Really. - string simpleName; - if (File.Exists(rootedAssembly)) - simpleName = Path.GetFileNameWithoutExtension(rootedAssembly); - else - simpleName = rootedAssembly; - yield return simpleName; + if (tokens.Count == 0) + { + yield return string.Empty; + yield break; + } + + foreach(Token token in tokens) + { + string rootedAssembly = token.Value; + + // For compatibility with IL Linker, the parameter could be a file name or an assembly name. + // This is the logic IL Linker uses to decide how to interpret the string. Really. + string simpleName; + if (File.Exists(rootedAssembly)) + simpleName = Path.GetFileNameWithoutExtension(rootedAssembly); + else + simpleName = rootedAssembly; + yield return simpleName; + } } - } #if DEBUG - private static bool DumpReproArguments(CodeGenerationFailedException ex) - { - Console.WriteLine("To repro, add following arguments to the command line:"); + private static bool DumpReproArguments(CodeGenerationFailedException ex) + { + Console.WriteLine("To repro, add following arguments to the command line:"); - MethodDesc failingMethod = ex.Method; + MethodDesc failingMethod = ex.Method; - var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)failingMethod.Context.SystemModule); + var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)failingMethod.Context.SystemModule); - Console.Write($"--singlemethodtypename \"{formatter.FormatName(failingMethod.OwningType, true)}\""); - Console.Write($" --singlemethodname {failingMethod.Name}"); + Console.Write($"--singlemethodtypename \"{formatter.FormatName(failingMethod.OwningType, true)}\""); + Console.Write($" --singlemethodname {failingMethod.Name}"); - for (int i = 0; i < failingMethod.Instantiation.Length; i++) - Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); + for (int i = 0; i < failingMethod.Instantiation.Length; i++) + Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); - return false; - } + return false; + } #endif + } } diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 8031885709ff3..4765a6be7e223 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -18,711 +18,712 @@ using Debug = System.Diagnostics.Debug; using InstructionSet = Internal.JitInterface.InstructionSet; -namespace ILCompiler; - -internal class Program +namespace ILCompiler { - private readonly ILCompilerRootCommand _command; - - public Program(ILCompilerRootCommand command) + internal class Program { - _command = command; + private readonly ILCompilerRootCommand _command; - if (command.Result.GetValueForOption(command.WaitForDebugger)) + public Program(ILCompilerRootCommand command) { - Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); - Console.ReadLine(); - } - } - - private IReadOnlyCollection CreateInitializerList(CompilerTypeSystemContext context) - { - List assembliesWithInitializers = new List(); + _command = command; - // Build a list of assemblies that have an initializer that needs to run before - // any user code runs. - foreach (string initAssemblyName in Get(_command.InitAssemblies)) - { - ModuleDesc assembly = context.ResolveAssembly(new AssemblyName(initAssemblyName), throwIfNotFound: true); - assembliesWithInitializers.Add(assembly); + if (command.Result.GetValueForOption(command.WaitForDebugger)) + { + Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); + Console.ReadLine(); + } } - var libraryInitializers = new LibraryInitializers(context, assembliesWithInitializers); - - List initializerList = new List(libraryInitializers.LibraryInitializerMethods); - - // If there are any AppContext switches the user wishes to enable, generate code that sets them. - string[] appContextSwitches = Get(_command.AppContextSwitches); - if (appContextSwitches.Length > 0) + private IReadOnlyCollection CreateInitializerList(CompilerTypeSystemContext context) { - MethodDesc appContextInitMethod = new Internal.IL.Stubs.StartupCode.AppContextInitializerMethod( - context.GeneratedAssembly.GetGlobalModuleType(), appContextSwitches); - initializerList.Add(appContextInitMethod); - } + List assembliesWithInitializers = new List(); - return initializerList; - } + // Build a list of assemblies that have an initializer that needs to run before + // any user code runs. + foreach (string initAssemblyName in Get(_command.InitAssemblies)) + { + ModuleDesc assembly = context.ResolveAssembly(new AssemblyName(initAssemblyName), throwIfNotFound: true); + assembliesWithInitializers.Add(assembly); + } - public int Run() - { - string outputFilePath = Get(_command.OutputFilePath); - if (outputFilePath == null) - throw new CommandLineException("Output filename must be specified (/out )"); + var libraryInitializers = new LibraryInitializers(context, assembliesWithInitializers); - TargetArchitecture targetArchitecture = Get(_command.TargetArchitecture); - InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); - TargetOS targetOS = Get(_command.TargetOS); + List initializerList = new List(libraryInitializers.LibraryInitializerMethods); - // The runtime expects certain baselines that the codegen can assume as well. - if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) - { - instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication - } - else if (targetArchitecture == TargetArchitecture.ARM64) - { - if (targetOS == TargetOS.OSX) - { - // For osx-arm64 we know that apple-m1 is a baseline - instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); - } - else + // If there are any AppContext switches the user wishes to enable, generate code that sets them. + string[] appContextSwitches = Get(_command.AppContextSwitches); + if (appContextSwitches.Length > 0) { - instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication + MethodDesc appContextInitMethod = new Internal.IL.Stubs.StartupCode.AppContextInitializerMethod( + context.GeneratedAssembly.GetGlobalModuleType(), appContextSwitches); + initializerList.Add(appContextInitMethod); } + + return initializerList; } - string instructionSetArg = Get(_command.InstructionSet); - if (instructionSetArg != null) + public int Run() { - var instructionSetParams = new List(); - - // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = instructionSetArg.Split(','); - for (int i = 0; i < instructionSetParamsInput.Length; i++) - { - string instructionSet = instructionSetParamsInput[i]; + string outputFilePath = Get(_command.OutputFilePath); + if (outputFilePath == null) + throw new CommandLineException("Output filename must be specified (/out )"); - if (String.IsNullOrEmpty(instructionSet)) - throw new CommandLineException("Instruction set must not be empty"); + TargetArchitecture targetArchitecture = Get(_command.TargetArchitecture); + InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); + TargetOS targetOS = Get(_command.TargetOS); - char firstChar = instructionSet[0]; - if ((firstChar != '+') && (firstChar != '-')) + // The runtime expects certain baselines that the codegen can assume as well. + if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) + { + instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication + } + else if (targetArchitecture == TargetArchitecture.ARM64) + { + if (targetOS == TargetOS.OSX) + { + // For osx-arm64 we know that apple-m1 is a baseline + instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); + } + else { - instructionSet = "+" + instructionSet; + instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication } - instructionSetParams.Add(instructionSet); } - Dictionary instructionSetSpecification = new Dictionary(); - foreach (string instructionSetSpecifier in instructionSetParams) + string instructionSetArg = Get(_command.InstructionSet); + if (instructionSetArg != null) { - string instructionSet = instructionSetSpecifier.Substring(1); + var instructionSetParams = new List(); - bool enabled = instructionSetSpecifier[0] == '+' ? true : false; - if (enabled) + // Normalize instruction set format to include implied +. + string[] instructionSetParamsInput = instructionSetArg.Split(','); + for (int i = 0; i < instructionSetParamsInput.Length; i++) { - if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) - throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); + string instructionSet = instructionSetParamsInput[i]; + + if (String.IsNullOrEmpty(instructionSet)) + throw new CommandLineException("Instruction set must not be empty"); + + char firstChar = instructionSet[0]; + if ((firstChar != '+') && (firstChar != '-')) + { + instructionSet = "+" + instructionSet; + } + instructionSetParams.Add(instructionSet); } - else + + Dictionary instructionSetSpecification = new Dictionary(); + foreach (string instructionSetSpecifier in instructionSetParams) { - if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) - throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); + string instructionSet = instructionSetSpecifier.Substring(1); + + bool enabled = instructionSetSpecifier[0] == '+' ? true : false; + if (enabled) + { + if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) + throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); + } + else + { + if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) + throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); + } } } - } - instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, - (string specifiedInstructionSet, string impliedInstructionSet) => - throw new CommandLineException(String.Format("Unsupported combination of instruction sets: {0}/{1}", specifiedInstructionSet, impliedInstructionSet))); + instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, + (string specifiedInstructionSet, string impliedInstructionSet) => + throw new CommandLineException(String.Format("Unsupported combination of instruction sets: {0}/{1}", specifiedInstructionSet, impliedInstructionSet))); - InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); + InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); - // Optimistically assume some instruction sets are present. - if (targetArchitecture == TargetArchitecture.X86 || targetArchitecture == TargetArchitecture.X64) - { - // We set these hardware features as opportunistically enabled as most of hardware in the wild supports them. - // Note that we do not indicate support for AVX, or any other instruction set which uses the VEX encodings as - // the presence of those makes otherwise acceptable code be unusable on hardware which does not support VEX encodings. - // - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("movbe"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); - - // If AVX was enabled, we can opportunistically enable instruction sets which use the VEX encodings - Debug.Assert(InstructionSet.X64_AVX == InstructionSet.X86_AVX); - if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX)) + // Optimistically assume some instruction sets are present. + if (targetArchitecture == TargetArchitecture.X86 || targetArchitecture == TargetArchitecture.X64) { - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("fma"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi2"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); - } - } - else if (targetArchitecture == TargetArchitecture.ARM64) - { - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha1"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha2"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lse"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rcpc"); - } - - optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, - (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); - optimisticInstructionSet.Remove(unsupportedInstructionSet); - optimisticInstructionSet.Add(supportedInstructionSet); - - var instructionSetSupport = new InstructionSetSupport(supportedInstructionSet, - unsupportedInstructionSet, - optimisticInstructionSet, - InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(targetArchitecture), - targetArchitecture); - - string systemModuleName = Get(_command.SystemModuleName); - string reflectionData = Get(_command.ReflectionData); - bool supportsReflection = reflectionData != "none" && systemModuleName == Helpers.DefaultSystemModule; - - // - // Initialize type system context - // - - SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - - var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); - var targetAbi = TargetAbi.NativeAot; - var targetDetails = new TargetDetails(targetArchitecture, targetOS, targetAbi, simdVectorLength); - CompilerTypeSystemContext typeSystemContext = - new CompilerTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0, Get(_command.MaxGenericCycle)); - - // - // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since - // some tests contain a mixture of both managed and native binaries. - // - // See: https://github.com/dotnet/corert/issues/2785 - // - // When we undo this hack, replace the foreach with - // typeSystemContext.InputFilePaths = _command.Result.GetValueForArgument(inputFilePaths); - // - Dictionary inputFilePaths = new Dictionary(); - foreach (var inputFile in _command.Result.GetValueForArgument(_command.InputFilePaths)) - { - try - { - var module = typeSystemContext.GetModuleFromPath(inputFile.Value); - inputFilePaths.Add(inputFile.Key, inputFile.Value); + // We set these hardware features as opportunistically enabled as most of hardware in the wild supports them. + // Note that we do not indicate support for AVX, or any other instruction set which uses the VEX encodings as + // the presence of those makes otherwise acceptable code be unusable on hardware which does not support VEX encodings. + // + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("movbe"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); + + // If AVX was enabled, we can opportunistically enable instruction sets which use the VEX encodings + Debug.Assert(InstructionSet.X64_AVX == InstructionSet.X86_AVX); + if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX)) + { + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("fma"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); + } } - catch (TypeSystemException.BadImageFormatException) + else if (targetArchitecture == TargetArchitecture.ARM64) { - // Keep calm and carry on. + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha1"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lse"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rcpc"); } - } - typeSystemContext.InputFilePaths = inputFilePaths; - typeSystemContext.ReferenceFilePaths = Get(_command.ReferenceFiles); - if (!typeSystemContext.InputFilePaths.ContainsKey(systemModuleName) - && !typeSystemContext.ReferenceFilePaths.ContainsKey(systemModuleName)) - throw new CommandLineException($"System module {systemModuleName} does not exists. Make sure that you specify --systemmodule"); + optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, + (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); + optimisticInstructionSet.Remove(unsupportedInstructionSet); + optimisticInstructionSet.Add(supportedInstructionSet); - typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(systemModuleName)); + var instructionSetSupport = new InstructionSetSupport(supportedInstructionSet, + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(targetArchitecture), + targetArchitecture); - if (typeSystemContext.InputFilePaths.Count == 0) - throw new CommandLineException("No input files specified"); + string systemModuleName = Get(_command.SystemModuleName); + string reflectionData = Get(_command.ReflectionData); + bool supportsReflection = reflectionData != "none" && systemModuleName == Helpers.DefaultSystemModule; - SecurityMitigationOptions securityMitigationOptions = 0; - string guard = Get(_command.Guard); - if (StringComparer.OrdinalIgnoreCase.Equals(guard, "cf")) - { - if (targetOS != TargetOS.Windows) + // + // Initialize type system context + // + + SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; + + var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); + var targetAbi = TargetAbi.NativeAot; + var targetDetails = new TargetDetails(targetArchitecture, targetOS, targetAbi, simdVectorLength); + CompilerTypeSystemContext typeSystemContext = + new CompilerTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0, Get(_command.MaxGenericCycle)); + + // + // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since + // some tests contain a mixture of both managed and native binaries. + // + // See: https://github.com/dotnet/corert/issues/2785 + // + // When we undo this hack, replace the foreach with + // typeSystemContext.InputFilePaths = _command.Result.GetValueForArgument(inputFilePaths); + // + Dictionary inputFilePaths = new Dictionary(); + foreach (var inputFile in _command.Result.GetValueForArgument(_command.InputFilePaths)) { - throw new CommandLineException($"Control flow guard only available on Windows"); + try + { + var module = typeSystemContext.GetModuleFromPath(inputFile.Value); + inputFilePaths.Add(inputFile.Key, inputFile.Value); + } + catch (TypeSystemException.BadImageFormatException) + { + // Keep calm and carry on. + } } - securityMitigationOptions = SecurityMitigationOptions.ControlFlowGuardAnnotations; - } - else if (!String.IsNullOrEmpty(guard)) - { - throw new CommandLineException($"Unrecognized mitigation option '{guard}'"); - } + typeSystemContext.InputFilePaths = inputFilePaths; + typeSystemContext.ReferenceFilePaths = Get(_command.ReferenceFiles); + if (!typeSystemContext.InputFilePaths.ContainsKey(systemModuleName) + && !typeSystemContext.ReferenceFilePaths.ContainsKey(systemModuleName)) + throw new CommandLineException($"System module {systemModuleName} does not exists. Make sure that you specify --systemmodule"); - // - // Initialize compilation group and compilation roots - // + typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(systemModuleName)); - // Single method mode? - MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); + if (typeSystemContext.InputFilePaths.Count == 0) + throw new CommandLineException("No input files specified"); - CompilationModuleGroup compilationGroup; - List compilationRoots = new List(); - bool multiFile = Get(_command.MultiFile); - if (singleMethod != null) - { - // Compiling just a single method - compilationGroup = new SingleMethodCompilationModuleGroup(singleMethod); - compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); - } - else - { - // Either single file, or multifile library, or multifile consumption. - EcmaModule entrypointModule = null; - bool systemModuleIsInputModule = false; - foreach (var inputFile in typeSystemContext.InputFilePaths) + SecurityMitigationOptions securityMitigationOptions = 0; + string guard = Get(_command.Guard); + if (StringComparer.OrdinalIgnoreCase.Equals(guard, "cf")) { - EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); - - if (module.PEReader.PEHeaders.IsExe) + if (targetOS != TargetOS.Windows) { - if (entrypointModule != null) - throw new Exception("Multiple EXE modules"); - entrypointModule = module; + throw new CommandLineException($"Control flow guard only available on Windows"); } - if (module == typeSystemContext.SystemModule) - systemModuleIsInputModule = true; - - compilationRoots.Add(new ExportedMethodsRootProvider(module)); + securityMitigationOptions = SecurityMitigationOptions.ControlFlowGuardAnnotations; } - - string[] runtimeOptions = Get(_command.RuntimeOptions); - if (entrypointModule != null) + else if (!String.IsNullOrEmpty(guard)) { - compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext))); - compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); - compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); + throw new CommandLineException($"Unrecognized mitigation option '{guard}'"); } - bool nativeLib = Get(_command.NativeLib); - if (multiFile) - { - List inputModules = new List(); + // + // Initialize compilation group and compilation roots + // + // Single method mode? + MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); + + CompilationModuleGroup compilationGroup; + List compilationRoots = new List(); + bool multiFile = Get(_command.MultiFile); + if (singleMethod != null) + { + // Compiling just a single method + compilationGroup = new SingleMethodCompilationModuleGroup(singleMethod); + compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); + } + else + { + // Either single file, or multifile library, or multifile consumption. + EcmaModule entrypointModule = null; + bool systemModuleIsInputModule = false; foreach (var inputFile in typeSystemContext.InputFilePaths) { EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); - if (entrypointModule == null) + if (module.PEReader.PEHeaders.IsExe) { - // This is a multifile production build - we need to root all methods - compilationRoots.Add(new LibraryRootProvider(module)); + if (entrypointModule != null) + throw new Exception("Multiple EXE modules"); + entrypointModule = module; } - inputModules.Add(module); + + if (module == typeSystemContext.SystemModule) + systemModuleIsInputModule = true; + + compilationRoots.Add(new ExportedMethodsRootProvider(module)); } - compilationGroup = new MultiFileSharedCompilationModuleGroup(typeSystemContext, inputModules); - } - else - { - if (entrypointModule == null && !nativeLib) - throw new Exception("No entrypoint module"); + string[] runtimeOptions = Get(_command.RuntimeOptions); + if (entrypointModule != null) + { + compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext))); + compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); + compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); + } - if (!systemModuleIsInputModule) - compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule)); - compilationGroup = new SingleFileCompilationModuleGroup(); - } + bool nativeLib = Get(_command.NativeLib); + if (multiFile) + { + List inputModules = new List(); - if (nativeLib) - { - // Set owning module of generated native library startup method to compiler generated module, - // to ensure the startup method is included in the object file during multimodule mode build - compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext))); - compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); - compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); + foreach (var inputFile in typeSystemContext.InputFilePaths) + { + EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); + + if (entrypointModule == null) + { + // This is a multifile production build - we need to root all methods + compilationRoots.Add(new LibraryRootProvider(module)); + } + inputModules.Add(module); + } + + compilationGroup = new MultiFileSharedCompilationModuleGroup(typeSystemContext, inputModules); + } + else + { + if (entrypointModule == null && !nativeLib) + throw new Exception("No entrypoint module"); + + if (!systemModuleIsInputModule) + compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule)); + compilationGroup = new SingleFileCompilationModuleGroup(); + } + + if (nativeLib) + { + // Set owning module of generated native library startup method to compiler generated module, + // to ensure the startup method is included in the object file during multimodule mode build + compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext))); + compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); + compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); + } + + foreach (var rdXmlFilePath in Get(_command.RdXmlFilePaths)) + { + compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath)); + } } - foreach (var rdXmlFilePath in Get(_command.RdXmlFilePaths)) + // Root whatever assemblies were specified on the command line + string[] rootedAssemblies = Get(_command.RootedAssemblies); + foreach (var rootedAssembly in rootedAssemblies) { - compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath)); + // For compatibility with IL Linker, the parameter could be a file name or an assembly name. + // This is the logic IL Linker uses to decide how to interpret the string. Really. + EcmaModule module = File.Exists(rootedAssembly) + ? typeSystemContext.GetModuleFromPath(rootedAssembly) + : typeSystemContext.GetModuleForSimpleName(rootedAssembly); + + // We only root the module type. The rest will fall out because we treat rootedAssemblies + // same as conditionally rooted ones and here we're fulfilling the condition ("something is used"). + compilationRoots.Add( + new GenericRootProvider(module, + (ModuleDesc module, IRootingServiceProvider rooter) => rooter.AddCompilationRoot(module.GetGlobalModuleType(), "Command line root"))); } - } - - // Root whatever assemblies were specified on the command line - string[] rootedAssemblies = Get(_command.RootedAssemblies); - foreach (var rootedAssembly in rootedAssemblies) - { - // For compatibility with IL Linker, the parameter could be a file name or an assembly name. - // This is the logic IL Linker uses to decide how to interpret the string. Really. - EcmaModule module = File.Exists(rootedAssembly) - ? typeSystemContext.GetModuleFromPath(rootedAssembly) - : typeSystemContext.GetModuleForSimpleName(rootedAssembly); - - // We only root the module type. The rest will fall out because we treat rootedAssemblies - // same as conditionally rooted ones and here we're fulfilling the condition ("something is used"). - compilationRoots.Add( - new GenericRootProvider(module, - (ModuleDesc module, IRootingServiceProvider rooter) => rooter.AddCompilationRoot(module.GetGlobalModuleType(), "Command line root"))); - } - // - // Compile - // + // + // Compile + // - CompilationBuilder builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); + CompilationBuilder builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); - string compilationUnitPrefix = multiFile ? Path.GetFileNameWithoutExtension(outputFilePath) : ""; - builder.UseCompilationUnitPrefix(compilationUnitPrefix); + string compilationUnitPrefix = multiFile ? Path.GetFileNameWithoutExtension(outputFilePath) : ""; + builder.UseCompilationUnitPrefix(compilationUnitPrefix); - string[] mibcFilePaths = Get(_command.MibcFilePaths); - if (mibcFilePaths.Length > 0) - ((RyuJitCompilationBuilder)builder).UseProfileData(mibcFilePaths); + string[] mibcFilePaths = Get(_command.MibcFilePaths); + if (mibcFilePaths.Length > 0) + ((RyuJitCompilationBuilder)builder).UseProfileData(mibcFilePaths); - string jitPath = Get(_command.JitPath); - if (!String.IsNullOrEmpty(jitPath)) - ((RyuJitCompilationBuilder)builder).UseJitPath(jitPath); + string jitPath = Get(_command.JitPath); + if (!String.IsNullOrEmpty(jitPath)) + ((RyuJitCompilationBuilder)builder).UseJitPath(jitPath); - PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, - Get(_command.DirectPInvokes), Get(_command.DirectPInvokeLists)); + PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, + Get(_command.DirectPInvokes), Get(_command.DirectPInvokeLists)); - ILProvider ilProvider = new NativeAotILProvider(); + ILProvider ilProvider = new NativeAotILProvider(); - List> featureSwitches = new List>(); - foreach (var switchPair in Get(_command.FeatureSwitches)) - { - string[] switchAndValue = switchPair.Split('='); - if (switchAndValue.Length != 2 - || !bool.TryParse(switchAndValue[1], out bool switchValue)) - throw new CommandLineException($"Unexpected feature switch pair '{switchPair}'"); - featureSwitches.Add(new KeyValuePair(switchAndValue[0], switchValue)); - } - ilProvider = new FeatureSwitchManager(ilProvider, featureSwitches); + List> featureSwitches = new List>(); + foreach (var switchPair in Get(_command.FeatureSwitches)) + { + string[] switchAndValue = switchPair.Split('='); + if (switchAndValue.Length != 2 + || !bool.TryParse(switchAndValue[1], out bool switchValue)) + throw new CommandLineException($"Unexpected feature switch pair '{switchPair}'"); + featureSwitches.Add(new KeyValuePair(switchAndValue[0], switchValue)); + } + ilProvider = new FeatureSwitchManager(ilProvider, featureSwitches); - var logger = new Logger(Console.Out, Get(_command.IsVerbose), ProcessWarningCodes(Get(_command.SuppressedWarnings)), - Get(_command.SingleWarn), Get(_command.SingleWarnEnabledAssemblies), Get(_command.SingleWarnDisabledAssemblies)); + var logger = new Logger(Console.Out, Get(_command.IsVerbose), ProcessWarningCodes(Get(_command.SuppressedWarnings)), + Get(_command.SingleWarn), Get(_command.SingleWarnEnabledAssemblies), Get(_command.SingleWarnDisabledAssemblies)); - var stackTracePolicy = Get(_command.EmitStackTraceData) ? - (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); + var stackTracePolicy = Get(_command.EmitStackTraceData) ? + (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); - MetadataBlockingPolicy mdBlockingPolicy; - ManifestResourceBlockingPolicy resBlockingPolicy; - UsageBasedMetadataGenerationOptions metadataGenerationOptions = default; - if (supportsReflection) - { - mdBlockingPolicy = Get(_command.NoMetadataBlocking) ? - new NoMetadataBlockingPolicy() : new BlockedInternalsBlockingPolicy(typeSystemContext); - - resBlockingPolicy = new ManifestResourceBlockingPolicy(featureSwitches); - - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; - if (Get(_command.CompleteTypesMetadata)) - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly; - if (Get(_command.ScanReflection)) - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning; - if (reflectionData == "all") - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts; - if (Get(_command.RootDefaultAssemblies)) - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.RootDefaultAssemblies; - } - else - { - mdBlockingPolicy = new FullyBlockedMetadataBlockingPolicy(); - resBlockingPolicy = new FullyBlockedManifestResourceBlockingPolicy(); - } + MetadataBlockingPolicy mdBlockingPolicy; + ManifestResourceBlockingPolicy resBlockingPolicy; + UsageBasedMetadataGenerationOptions metadataGenerationOptions = default; + if (supportsReflection) + { + mdBlockingPolicy = Get(_command.NoMetadataBlocking) ? + new NoMetadataBlockingPolicy() : new BlockedInternalsBlockingPolicy(typeSystemContext); + + resBlockingPolicy = new ManifestResourceBlockingPolicy(featureSwitches); + + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; + if (Get(_command.CompleteTypesMetadata)) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly; + if (Get(_command.ScanReflection)) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning; + if (reflectionData == "all") + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts; + if (Get(_command.RootDefaultAssemblies)) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.RootDefaultAssemblies; + } + else + { + mdBlockingPolicy = new FullyBlockedMetadataBlockingPolicy(); + resBlockingPolicy = new FullyBlockedManifestResourceBlockingPolicy(); + } - DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy(); - - var flowAnnotations = new ILLink.Shared.TrimAnalysis.FlowAnnotations(logger, ilProvider); - - MetadataManager metadataManager = new UsageBasedMetadataManager( - compilationGroup, - typeSystemContext, - mdBlockingPolicy, - resBlockingPolicy, - Get(_command.MetadataLogFileName), - stackTracePolicy, - invokeThunkGenerationPolicy, - flowAnnotations, - metadataGenerationOptions, - logger, - featureSwitches, - Get(_command.ConditionallyRootedAssemblies), - rootedAssemblies, - Get(_command.TrimmedAssemblies)); - - InteropStateManager interopStateManager = new InteropStateManager(typeSystemContext.GeneratedAssembly); - InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger); - - // Unless explicitly opted in at the command line, we enable scanner for retail builds by default. - // We also don't do this for multifile because scanner doesn't simulate inlining (this would be - // fixable by using a CompilationGroup for the scanner that has a bigger worldview, but - // let's cross that bridge when we get there). - bool useScanner = Get(_command.UseScanner) || - (_command.OptimizationMode != OptimizationMode.None && !multiFile); - - useScanner &= !Get(_command.NoScanner); - - // Enable static data preinitialization in optimized builds. - bool preinitStatics = Get(_command.PreinitStatics) || - (_command.OptimizationMode != OptimizationMode.None && !multiFile); - preinitStatics &= !Get(_command.NoPreinitStatics); - - var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics); - builder - .UseILProvider(ilProvider) - .UsePreinitializationManager(preinitManager); - - ILScanResults scanResults = null; - string scanDgmlLogFileName = Get(_command.ScanDgmlLogFileName); - int parallelism = Get(_command.Parallelism); - if (useScanner) - { - ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() - .UseCompilationRoots(compilationRoots) - .UseMetadataManager(metadataManager) - .UseParallelism(parallelism) - .UseInteropStubManager(interopStubManager) - .UseLogger(logger); + DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy(); + + var flowAnnotations = new ILLink.Shared.TrimAnalysis.FlowAnnotations(logger, ilProvider); + + MetadataManager metadataManager = new UsageBasedMetadataManager( + compilationGroup, + typeSystemContext, + mdBlockingPolicy, + resBlockingPolicy, + Get(_command.MetadataLogFileName), + stackTracePolicy, + invokeThunkGenerationPolicy, + flowAnnotations, + metadataGenerationOptions, + logger, + featureSwitches, + Get(_command.ConditionallyRootedAssemblies), + rootedAssemblies, + Get(_command.TrimmedAssemblies)); + + InteropStateManager interopStateManager = new InteropStateManager(typeSystemContext.GeneratedAssembly); + InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger); + + // Unless explicitly opted in at the command line, we enable scanner for retail builds by default. + // We also don't do this for multifile because scanner doesn't simulate inlining (this would be + // fixable by using a CompilationGroup for the scanner that has a bigger worldview, but + // let's cross that bridge when we get there). + bool useScanner = Get(_command.UseScanner) || + (_command.OptimizationMode != OptimizationMode.None && !multiFile); + + useScanner &= !Get(_command.NoScanner); + + // Enable static data preinitialization in optimized builds. + bool preinitStatics = Get(_command.PreinitStatics) || + (_command.OptimizationMode != OptimizationMode.None && !multiFile); + preinitStatics &= !Get(_command.NoPreinitStatics); + + var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics); + builder + .UseILProvider(ilProvider) + .UsePreinitializationManager(preinitManager); + + ILScanResults scanResults = null; + string scanDgmlLogFileName = Get(_command.ScanDgmlLogFileName); + int parallelism = Get(_command.Parallelism); + if (useScanner) + { + ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() + .UseCompilationRoots(compilationRoots) + .UseMetadataManager(metadataManager) + .UseParallelism(parallelism) + .UseInteropStubManager(interopStubManager) + .UseLogger(logger); - if (scanDgmlLogFileName != null) - scannerBuilder.UseDependencyTracking(Get(_command.GenerateFullScanDgmlLog) ? - DependencyTrackingLevel.All : DependencyTrackingLevel.First); + if (scanDgmlLogFileName != null) + scannerBuilder.UseDependencyTracking(Get(_command.GenerateFullScanDgmlLog) ? + DependencyTrackingLevel.All : DependencyTrackingLevel.First); - IILScanner scanner = scannerBuilder.ToILScanner(); + IILScanner scanner = scannerBuilder.ToILScanner(); - scanResults = scanner.Scan(); + scanResults = scanner.Scan(); - metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); + metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); - interopStubManager = scanResults.GetInteropStubManager(interopStateManager, pinvokePolicy); - } + interopStubManager = scanResults.GetInteropStubManager(interopStateManager, pinvokePolicy); + } - string ilDump = Get(_command.IlDump); - DebugInformationProvider debugInfoProvider = Get(_command.EnableDebugInfo) ? - (ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(ilDump, new EcmaOnlyDebugInformationProvider())) : - new NullDebugInformationProvider(); - - string dgmlLogFileName = Get(_command.DgmlLogFileName); - DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? - DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? - DependencyTrackingLevel.All : DependencyTrackingLevel.First); - - compilationRoots.Add(metadataManager); - compilationRoots.Add(interopStubManager); - - builder - .UseInstructionSetSupport(instructionSetSupport) - .UseBackendOptions(Get(_command.CodegenOptions)) - .UseMethodBodyFolding(enable: Get(_command.MethodBodyFolding)) - .UseParallelism(parallelism) - .UseMetadataManager(metadataManager) - .UseInteropStubManager(interopStubManager) - .UseLogger(logger) - .UseDependencyTracking(trackingLevel) - .UseCompilationRoots(compilationRoots) - .UseOptimizationMode(_command.OptimizationMode) - .UseSecurityMitigationOptions(securityMitigationOptions) - .UseDebugInfoProvider(debugInfoProvider) - .UseDwarf5(Get(_command.UseDwarf5)); - - if (scanResults != null) - { - // If we have a scanner, feed the vtable analysis results to the compilation. - // This could be a command line switch if we really wanted to. - builder.UseVTableSliceProvider(scanResults.GetVTableLayoutInfo()); - - // If we have a scanner, feed the generic dictionary results to the compilation. - // This could be a command line switch if we really wanted to. - builder.UseGenericDictionaryLayoutProvider(scanResults.GetDictionaryLayoutInfo()); - - // If we have a scanner, we can drive devirtualization using the information - // we collected at scanning time (effectively sealing unsealed types if possible). - // This could be a command line switch if we really wanted to. - builder.UseDevirtualizationManager(scanResults.GetDevirtualizationManager()); - - // If we use the scanner's result, we need to consult it to drive inlining. - // This prevents e.g. devirtualizing and inlining methods on types that were - // never actually allocated. - builder.UseInliningPolicy(scanResults.GetInliningPolicy()); - - // Use an error provider that prevents us from re-importing methods that failed - // to import with an exception during scanning phase. We would see the same failure during - // compilation, but before RyuJIT gets there, it might ask questions that we don't - // have answers for because we didn't scan the entire method. - builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); - } + string ilDump = Get(_command.IlDump); + DebugInformationProvider debugInfoProvider = Get(_command.EnableDebugInfo) ? + (ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(ilDump, new EcmaOnlyDebugInformationProvider())) : + new NullDebugInformationProvider(); - builder.UseResilience(Get(_command.Resilient)); + string dgmlLogFileName = Get(_command.DgmlLogFileName); + DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? + DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? + DependencyTrackingLevel.All : DependencyTrackingLevel.First); - ICompilation compilation = builder.ToCompilation(); + compilationRoots.Add(metadataManager); + compilationRoots.Add(interopStubManager); - string mapFileName = Get(_command.MapFileName); - ObjectDumper dumper = mapFileName != null ? new ObjectDumper(mapFileName) : null; + builder + .UseInstructionSetSupport(instructionSetSupport) + .UseBackendOptions(Get(_command.CodegenOptions)) + .UseMethodBodyFolding(enable: Get(_command.MethodBodyFolding)) + .UseParallelism(parallelism) + .UseMetadataManager(metadataManager) + .UseInteropStubManager(interopStubManager) + .UseLogger(logger) + .UseDependencyTracking(trackingLevel) + .UseCompilationRoots(compilationRoots) + .UseOptimizationMode(_command.OptimizationMode) + .UseSecurityMitigationOptions(securityMitigationOptions) + .UseDebugInfoProvider(debugInfoProvider) + .UseDwarf5(Get(_command.UseDwarf5)); - CompilationResults compilationResults = compilation.Compile(outputFilePath, dumper); - string exportsFile = Get(_command.ExportsFile); - if (exportsFile != null) - { - ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile); - foreach (var compilationRoot in compilationRoots) + if (scanResults != null) { - if (compilationRoot is ExportedMethodsRootProvider provider) - defFileWriter.AddExportedMethods(provider.ExportedMethods); + // If we have a scanner, feed the vtable analysis results to the compilation. + // This could be a command line switch if we really wanted to. + builder.UseVTableSliceProvider(scanResults.GetVTableLayoutInfo()); + + // If we have a scanner, feed the generic dictionary results to the compilation. + // This could be a command line switch if we really wanted to. + builder.UseGenericDictionaryLayoutProvider(scanResults.GetDictionaryLayoutInfo()); + + // If we have a scanner, we can drive devirtualization using the information + // we collected at scanning time (effectively sealing unsealed types if possible). + // This could be a command line switch if we really wanted to. + builder.UseDevirtualizationManager(scanResults.GetDevirtualizationManager()); + + // If we use the scanner's result, we need to consult it to drive inlining. + // This prevents e.g. devirtualizing and inlining methods on types that were + // never actually allocated. + builder.UseInliningPolicy(scanResults.GetInliningPolicy()); + + // Use an error provider that prevents us from re-importing methods that failed + // to import with an exception during scanning phase. We would see the same failure during + // compilation, but before RyuJIT gets there, it might ask questions that we don't + // have answers for because we didn't scan the entire method. + builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); } - defFileWriter.EmitExportedMethods(); - } + builder.UseResilience(Get(_command.Resilient)); - typeSystemContext.LogWarnings(logger); + ICompilation compilation = builder.ToCompilation(); - if (dgmlLogFileName != null) - compilationResults.WriteDependencyLog(dgmlLogFileName); + string mapFileName = Get(_command.MapFileName); + ObjectDumper dumper = mapFileName != null ? new ObjectDumper(mapFileName) : null; - if (scanResults != null) - { - if (scanDgmlLogFileName != null) - scanResults.WriteDependencyLog(scanDgmlLogFileName); - - // If the scanner and compiler don't agree on what to compile, the outputs of the scanner might not actually be usable. - // We are going to check this two ways: - // 1. The methods and types generated during compilation are a subset of method and types scanned - // 2. The methods and types scanned are a subset of methods and types compiled (this has a chance to hold for unoptimized builds only). - - // Check that methods and types generated during compilation are a subset of method and types scanned - bool scanningFail = false; - DiffCompilationResults(ref scanningFail, compilationResults.CompiledMethodBodies, scanResults.CompiledMethodBodies, - "Methods", "compiled", "scanned", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || IsRelatedToInvalidInput(method)); - DiffCompilationResults(ref scanningFail, compilationResults.ConstructedEETypes, scanResults.ConstructedEETypes, - "EETypes", "compiled", "scanned", type => !(type.GetTypeDefinition() is EcmaType)); - - static bool IsRelatedToInvalidInput(MethodDesc method) + CompilationResults compilationResults = compilation.Compile(outputFilePath, dumper); + string exportsFile = Get(_command.ExportsFile); + if (exportsFile != null) { - // RyuJIT is more sensitive to invalid input and might detect cases that the scanner didn't have trouble with. - // If we find logic related to compiling fallback method bodies (methods that just throw) that got compiled - // but not scanned, it's usually fine. If it wasn't fine, we would probably crash before getting here. - return method.OwningType is MetadataType mdType - && mdType.Module == method.Context.SystemModule - && (mdType.Name.EndsWith("Exception") || mdType.Namespace.StartsWith("Internal.Runtime")); + ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile); + foreach (var compilationRoot in compilationRoots) + { + if (compilationRoot is ExportedMethodsRootProvider provider) + defFileWriter.AddExportedMethods(provider.ExportedMethods); + } + + defFileWriter.EmitExportedMethods(); } - // If optimizations are enabled, the results will for sure not match in the other direction due to inlining, etc. - // But there's at least some value in checking the scanner doesn't expand the universe too much in debug. - if (_command.OptimizationMode == OptimizationMode.None) - { - // Check that methods and types scanned are a subset of methods and types compiled + typeSystemContext.LogWarnings(logger); - // If we find diffs here, they're not critical, but still might be causing a Size on Disk regression. - bool dummy = false; + if (dgmlLogFileName != null) + compilationResults.WriteDependencyLog(dgmlLogFileName); - // We additionally skip methods in SIMD module because there's just too many intrisics to handle and IL scanner - // doesn't expand them. They would show up as noisy diffs. - DiffCompilationResults(ref dummy, scanResults.CompiledMethodBodies, compilationResults.CompiledMethodBodies, - "Methods", "scanned", "compiled", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || method.OwningType.IsIntrinsic); - DiffCompilationResults(ref dummy, scanResults.ConstructedEETypes, compilationResults.ConstructedEETypes, - "EETypes", "scanned", "compiled", type => !(type.GetTypeDefinition() is EcmaType)); - } + if (scanResults != null) + { + if (scanDgmlLogFileName != null) + scanResults.WriteDependencyLog(scanDgmlLogFileName); + + // If the scanner and compiler don't agree on what to compile, the outputs of the scanner might not actually be usable. + // We are going to check this two ways: + // 1. The methods and types generated during compilation are a subset of method and types scanned + // 2. The methods and types scanned are a subset of methods and types compiled (this has a chance to hold for unoptimized builds only). + + // Check that methods and types generated during compilation are a subset of method and types scanned + bool scanningFail = false; + DiffCompilationResults(ref scanningFail, compilationResults.CompiledMethodBodies, scanResults.CompiledMethodBodies, + "Methods", "compiled", "scanned", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || IsRelatedToInvalidInput(method)); + DiffCompilationResults(ref scanningFail, compilationResults.ConstructedEETypes, scanResults.ConstructedEETypes, + "EETypes", "compiled", "scanned", type => !(type.GetTypeDefinition() is EcmaType)); + + static bool IsRelatedToInvalidInput(MethodDesc method) + { + // RyuJIT is more sensitive to invalid input and might detect cases that the scanner didn't have trouble with. + // If we find logic related to compiling fallback method bodies (methods that just throw) that got compiled + // but not scanned, it's usually fine. If it wasn't fine, we would probably crash before getting here. + return method.OwningType is MetadataType mdType + && mdType.Module == method.Context.SystemModule + && (mdType.Name.EndsWith("Exception") || mdType.Namespace.StartsWith("Internal.Runtime")); + } - if (scanningFail) - throw new Exception("Scanning failure"); - } + // If optimizations are enabled, the results will for sure not match in the other direction due to inlining, etc. + // But there's at least some value in checking the scanner doesn't expand the universe too much in debug. + if (_command.OptimizationMode == OptimizationMode.None) + { + // Check that methods and types scanned are a subset of methods and types compiled - if (debugInfoProvider is IDisposable) - ((IDisposable)debugInfoProvider).Dispose(); + // If we find diffs here, they're not critical, but still might be causing a Size on Disk regression. + bool dummy = false; - preinitManager.LogStatistics(logger); + // We additionally skip methods in SIMD module because there's just too many intrisics to handle and IL scanner + // doesn't expand them. They would show up as noisy diffs. + DiffCompilationResults(ref dummy, scanResults.CompiledMethodBodies, compilationResults.CompiledMethodBodies, + "Methods", "scanned", "compiled", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || method.OwningType.IsIntrinsic); + DiffCompilationResults(ref dummy, scanResults.ConstructedEETypes, compilationResults.ConstructedEETypes, + "EETypes", "scanned", "compiled", type => !(type.GetTypeDefinition() is EcmaType)); + } - return 0; - } + if (scanningFail) + throw new Exception("Scanning failure"); + } - [System.Diagnostics.Conditional("DEBUG")] - private void DiffCompilationResults(ref bool result, IEnumerable set1, IEnumerable set2, string prefix, - string set1name, string set2name, Predicate filter) - { - HashSet diff = new HashSet(set1); - diff.ExceptWith(set2); + if (debugInfoProvider is IDisposable) + ((IDisposable)debugInfoProvider).Dispose(); + + preinitManager.LogStatistics(logger); - // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. - // https://github.com/dotnet/corert/issues/3873 - diff.RemoveWhere(filter); + return 0; + } - if (diff.Count > 0) + [System.Diagnostics.Conditional("DEBUG")] + private void DiffCompilationResults(ref bool result, IEnumerable set1, IEnumerable set2, string prefix, + string set1name, string set2name, Predicate filter) { - result = true; + HashSet diff = new HashSet(set1); + diff.ExceptWith(set2); - Console.WriteLine($"*** {prefix} {set1name} but not {set2name}:"); + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + diff.RemoveWhere(filter); - foreach (var d in diff) + if (diff.Count > 0) { - Console.WriteLine(d.ToString()); + result = true; + + Console.WriteLine($"*** {prefix} {set1name} but not {set2name}:"); + + foreach (var d in diff) + { + Console.WriteLine(d.ToString()); + } } } - } - private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) - { - ModuleDesc systemModule = context.SystemModule; - - TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => + private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) { - return (MetadataType)context.GetCanonType(typeDefName) - ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); - }); - if (foundType == null) - throw new CommandLineException($"Type '{typeName}' not found"); + ModuleDesc systemModule = context.SystemModule; - return foundType; - } + TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => + { + return (MetadataType)context.GetCanonType(typeDefName) + ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); + }); + if (foundType == null) + throw new CommandLineException($"Type '{typeName}' not found"); - private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) - { - string singleMethodName = Get(_command.SingleMethodName); - string singleMethodTypeName = Get(_command.SingleMethodTypeName); - string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); + return foundType; + } - if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) - return null; + private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) + { + string singleMethodName = Get(_command.SingleMethodName); + string singleMethodTypeName = Get(_command.SingleMethodTypeName); + string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); - if (singleMethodName == null || singleMethodTypeName == null) - throw new CommandLineException("Both method name and type name are required parameters for single method mode"); + if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) + return null; - TypeDesc owningType = FindType(context, singleMethodTypeName); + if (singleMethodName == null || singleMethodTypeName == null) + throw new CommandLineException("Both method name and type name are required parameters for single method mode"); - // TODO: allow specifying signature to distinguish overloads - MethodDesc method = owningType.GetMethod(singleMethodName, null); - if (method == null) - throw new CommandLineException($"Method '{singleMethodName}' not found in '{singleMethodTypeName}'"); + TypeDesc owningType = FindType(context, singleMethodTypeName); - if (method.HasInstantiation != (singleMethodGenericArgs != null) || - (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) - { - throw new CommandLineException( - $"Expected {method.Instantiation.Length} generic arguments for method '{singleMethodName}' on type '{singleMethodTypeName}'"); - } + // TODO: allow specifying signature to distinguish overloads + MethodDesc method = owningType.GetMethod(singleMethodName, null); + if (method == null) + throw new CommandLineException($"Method '{singleMethodName}' not found in '{singleMethodTypeName}'"); - if (method.HasInstantiation) - { - List genericArguments = new List(); - foreach (var argString in singleMethodGenericArgs) - genericArguments.Add(FindType(context, argString)); - method = method.MakeInstantiatedMethod(genericArguments.ToArray()); - } + if (method.HasInstantiation != (singleMethodGenericArgs != null) || + (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) + { + throw new CommandLineException( + $"Expected {method.Instantiation.Length} generic arguments for method '{singleMethodName}' on type '{singleMethodTypeName}'"); + } - return method; - } + if (method.HasInstantiation) + { + List genericArguments = new List(); + foreach (var argString in singleMethodGenericArgs) + genericArguments.Add(FindType(context, argString)); + method = method.MakeInstantiatedMethod(genericArguments.ToArray()); + } - private static IEnumerable ProcessWarningCodes(IEnumerable warningCodes) - { - foreach (string value in warningCodes) + return method; + } + + private static IEnumerable ProcessWarningCodes(IEnumerable warningCodes) { - string[] values = value.Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); - foreach (string id in values) + foreach (string value in warningCodes) { - if (!id.StartsWith("IL", StringComparison.Ordinal) || !ushort.TryParse(id.Substring(2), out ushort code)) - continue; + string[] values = value.Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string id in values) + { + if (!id.StartsWith("IL", StringComparison.Ordinal) || !ushort.TryParse(id.Substring(2), out ushort code)) + continue; - yield return code; + yield return code; + } } } - } - private T Get(Option option) => _command.Result.GetValueForOption(option); + private T Get(Option option) => _command.Result.GetValueForOption(option); - private static int Main(string[] args) => - new CommandLineBuilder(new ILCompilerRootCommand(args)) - .UseVersionOption() - .UseHelp(context => context.HelpBuilder.CustomizeLayout(ILCompilerRootCommand.GetExtendedHelp)) - .UseParseErrorReporting() - .Build() - .Invoke(args); + private static int Main(string[] args) => + new CommandLineBuilder(new ILCompilerRootCommand(args)) + .UseVersionOption() + .UseHelp(context => context.HelpBuilder.CustomizeLayout(ILCompilerRootCommand.GetExtendedHelp)) + .UseParseErrorReporting() + .Build() + .Invoke(args); + } } diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index 02d568d9e76b0..095070cf271c5 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -11,336 +11,349 @@ using Internal.TypeSystem; -namespace ILCompiler; - -internal class Crossgen2RootCommand : RootCommand +namespace ILCompiler { - public Argument> InputFilePaths { get; } = - new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; - public Option> UnrootedInputFilePaths { get; } = - new(new[] { "--unrooted-input-file-paths", "-u" }, result => Helpers.BuildPathDictionay(result.Tokens, true), true, SR.UnrootedInputFilesToCompile); - public Option> ReferenceFilePaths { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.ReferenceFiles); - public Option InstructionSet { get; } = - new(new[] { "--instruction-set" }, SR.InstructionSets); - public Option MibcFilePaths { get; } = - new(new[] { "--mibc", "-m" }, () => Array.Empty(), SR.MibcFiles); - public Option OutputFilePath { get; } = - new(new[] { "--out", "-o" }, SR.OutputFilePath); - public Option CompositeRootPath { get; } = - new(new[] { "--compositerootpath", "--crp" }, SR.CompositeRootPath); - public Option Optimize { get; } = - new(new[] { "--optimize", "-O" }, SR.EnableOptimizationsOption); - public Option OptimizeDisabled { get; } = - new(new[] { "--optimize-disabled", "-Od" }, SR.DisableOptimizationsOption); - public Option OptimizeSpace { get; } = - new(new[] { "--optimize-space", "-Os" }, SR.OptimizeSpaceOption); - public Option OptimizeTime { get; } = - new(new[] { "--optimize-time", "-Ot" }, SR.OptimizeSpeedOption); - public Option InputBubble { get; } = - new(new[] { "--inputbubble" }, SR.InputBubbleOption); - public Option> InputBubbleReferenceFilePaths { get; } = - new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.InputBubbleReferenceFiles); - public Option Composite { get; } = - new(new[] { "--composite" }, SR.CompositeBuildMode); - public Option CompositeKeyFile { get; } = - new(new[] { "--compositekeyfile" }, SR.CompositeKeyFile); - public Option CompileNoMethods { get; } = - new(new[] { "--compile-no-methods" }, SR.CompileNoMethodsOption); - public Option OutNearInput { get; } = - new(new[] { "--out-near-input" }, SR.OutNearInputOption); - public Option SingleFileCompilation { get; } = - new(new[] { "--single-file-compilation" }, SR.SingleFileCompilationOption); - public Option Partial { get; } = - new(new[] { "--partial" }, SR.PartialImageOption); - public Option CompileBubbleGenerics { get; } = - new(new[] { "--compilebubblegenerics" }, SR.BubbleGenericsOption); - public Option EmbedPgoData { get; } = - new(new[] { "--embed-pgo-data" }, SR.EmbedPgoDataOption); - public Option DgmlLogFileName { get; } = - new(new[] { "--dgmllog" }, SR.SaveDependencyLogOption); - public Option GenerateFullDgmlLog { get; } = - new(new[] { "--fulllog" }, SR.SaveDetailedLogOption); - public Option IsVerbose { get; } = - new(new[] { "--verbose" }, SR.VerboseLoggingOption); - public Option SystemModuleName { get; } = - new(new[] { "--systemmodule" }, () => Helpers.DefaultSystemModule, SR.SystemModuleOverrideOption); - public Option WaitForDebugger { get; } = - new(new[] { "--waitfordebugger" }, SR.WaitForDebuggerOption); - public Option CodegenOptions { get; } = - new(new[] { "--codegenopt" }, () => Array.Empty(), SR.CodeGenOptions); - public Option Resilient { get; } = - new(new[] { "--resilient" }, SR.ResilientOption); - public Option ImageBase { get; } = - new(new[] { "--imagebase" }, SR.ImageBase); - public Option TargetArchitecture { get; } = - new(new[] { "--targetarch" }, result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetArchOption) { Arity = ArgumentArity.OneOrMore }; - public Option TargetOS { get; } = - new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetOSOption); - public Option JitPath { get; } = - new(new[] { "--jitpath" }, SR.JitPathOption); - public Option PrintReproInstructions { get; } = - new(new[] { "--print-repro-instructions" }, SR.PrintReproInstructionsOption); - public Option SingleMethodTypeName { get; } = - new(new[] { "--singlemethodtypename" }, SR.SingleMethodTypeName); - public Option SingleMethodName { get; } = - new(new[] { "--singlemethodname" }, SR.SingleMethodMethodName); - public Option SingleMethodIndex { get; } = - new(new[] { "--singlemethodindex" }, SR.SingleMethodIndex); - public Option SingleMethodGenericArgs { get; } = - new(new[] { "--singlemethodgenericarg" }, SR.SingleMethodGenericArgs); - public Option Parallelism { get; } = - new(new[] { "--parallelism" }, () => Environment.ProcessorCount, SR.ParalellismOption); - public Option CustomPESectionAlignment { get; } = - new(new[] { "--custom-pe-section-alignment" }, SR.CustomPESectionAlignmentOption); - public Option Map { get; } = - new(new[] { "--map" }, SR.MapFileOption); - public Option MapCsv { get; } = - new(new[] { "--mapcsv" }, SR.MapCsvFileOption); - public Option Pdb { get; } = - new(new[] { "--pdb" }, SR.PdbFileOption); - public Option PdbPath { get; } = - new(new[] { "--pdb-path" }, SR.PdbFilePathOption); - public Option PerfMap { get; } = - new(new[] { "--perfmap" }, SR.PerfMapFileOption); - public Option PerfMapPath { get; } = - new(new[] { "--perfmap-path" }, SR.PerfMapFilePathOption); - public Option PerfMapFormatVersion { get; } = - new(new[] { "--perfmap-format-version" }, () => 0, SR.PerfMapFormatVersionOption); - public Option CrossModuleInlining { get; } = - new(new[] { "--opt-cross-module" }, SR.CrossModuleInlining); - public Option AsyncMethodOptimization { get; } = - new(new[] { "--opt-async-methods" }, SR.AsyncModuleOptimization); - public Option NonLocalGenericsModule { get; } = - new(new[] { "--non-local-generics-module" }, () => string.Empty, SR.NonLocalGenericsModule); - public Option MethodLayout { get; } = - new(new[] { "--method-layout" }, result => - { - if (result.Tokens.Count == 0 ) - return ReadyToRunMethodLayoutAlgorithm.DefaultSort; + internal class Crossgen2RootCommand : RootCommand + { + public Argument> InputFilePaths { get; } = + new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; + public Option> UnrootedInputFilePaths { get; } = + new(new[] { "--unrooted-input-file-paths", "-u" }, result => Helpers.BuildPathDictionay(result.Tokens, true), true, SR.UnrootedInputFilesToCompile); + public Option> ReferenceFilePaths { get; } = + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.ReferenceFiles); + public Option InstructionSet { get; } = + new(new[] { "--instruction-set" }, SR.InstructionSets); + public Option MibcFilePaths { get; } = + new(new[] { "--mibc", "-m" }, () => Array.Empty(), SR.MibcFiles); + public Option OutputFilePath { get; } = + new(new[] { "--out", "-o" }, SR.OutputFilePath); + public Option CompositeRootPath { get; } = + new(new[] { "--compositerootpath", "--crp" }, SR.CompositeRootPath); + public Option Optimize { get; } = + new(new[] { "--optimize", "-O" }, SR.EnableOptimizationsOption); + public Option OptimizeDisabled { get; } = + new(new[] { "--optimize-disabled", "-Od" }, SR.DisableOptimizationsOption); + public Option OptimizeSpace { get; } = + new(new[] { "--optimize-space", "-Os" }, SR.OptimizeSpaceOption); + public Option OptimizeTime { get; } = + new(new[] { "--optimize-time", "-Ot" }, SR.OptimizeSpeedOption); + public Option InputBubble { get; } = + new(new[] { "--inputbubble" }, SR.InputBubbleOption); + public Option> InputBubbleReferenceFilePaths { get; } = + new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.InputBubbleReferenceFiles); + public Option Composite { get; } = + new(new[] { "--composite" }, SR.CompositeBuildMode); + public Option CompositeKeyFile { get; } = + new(new[] { "--compositekeyfile" }, SR.CompositeKeyFile); + public Option CompileNoMethods { get; } = + new(new[] { "--compile-no-methods" }, SR.CompileNoMethodsOption); + public Option OutNearInput { get; } = + new(new[] { "--out-near-input" }, SR.OutNearInputOption); + public Option SingleFileCompilation { get; } = + new(new[] { "--single-file-compilation" }, SR.SingleFileCompilationOption); + public Option Partial { get; } = + new(new[] { "--partial" }, SR.PartialImageOption); + public Option CompileBubbleGenerics { get; } = + new(new[] { "--compilebubblegenerics" }, SR.BubbleGenericsOption); + public Option EmbedPgoData { get; } = + new(new[] { "--embed-pgo-data" }, SR.EmbedPgoDataOption); + public Option DgmlLogFileName { get; } = + new(new[] { "--dgmllog" }, SR.SaveDependencyLogOption); + public Option GenerateFullDgmlLog { get; } = + new(new[] { "--fulllog" }, SR.SaveDetailedLogOption); + public Option IsVerbose { get; } = + new(new[] { "--verbose" }, SR.VerboseLoggingOption); + public Option SystemModuleName { get; } = + new(new[] { "--systemmodule" }, () => Helpers.DefaultSystemModule, SR.SystemModuleOverrideOption); + public Option WaitForDebugger { get; } = + new(new[] { "--waitfordebugger" }, SR.WaitForDebuggerOption); + public Option CodegenOptions { get; } = + new(new[] { "--codegenopt" }, () => Array.Empty(), SR.CodeGenOptions); + public Option Resilient { get; } = + new(new[] { "--resilient" }, SR.ResilientOption); + public Option ImageBase { get; } = + new(new[] { "--imagebase" }, SR.ImageBase); + public Option TargetArchitecture { get; } = + new(new[] { "--targetarch" }, result => + { + string firstToken = result.Tokens.Count > 0 ? result.Tokens[0].Value : null; + if (firstToken != null && firstToken.Equals("armel", StringComparison.OrdinalIgnoreCase)) + { + IsArmel = true; + return Internal.TypeSystem.TargetArchitecture.ARM; + } - return result.Tokens[0].Value.ToLowerInvariant() switch + return Helpers.GetTargetArchitecture(firstToken); + }, true, SR.TargetArchOption) { Arity = ArgumentArity.OneOrMore }; + public Option TargetOS { get; } = + new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetOSOption); + public Option JitPath { get; } = + new(new[] { "--jitpath" }, SR.JitPathOption); + public Option PrintReproInstructions { get; } = + new(new[] { "--print-repro-instructions" }, SR.PrintReproInstructionsOption); + public Option SingleMethodTypeName { get; } = + new(new[] { "--singlemethodtypename" }, SR.SingleMethodTypeName); + public Option SingleMethodName { get; } = + new(new[] { "--singlemethodname" }, SR.SingleMethodMethodName); + public Option SingleMethodIndex { get; } = + new(new[] { "--singlemethodindex" }, SR.SingleMethodIndex); + public Option SingleMethodGenericArgs { get; } = + new(new[] { "--singlemethodgenericarg" }, SR.SingleMethodGenericArgs); + public Option Parallelism { get; } = + new(new[] { "--parallelism" }, () => Environment.ProcessorCount, SR.ParalellismOption); + public Option CustomPESectionAlignment { get; } = + new(new[] { "--custom-pe-section-alignment" }, SR.CustomPESectionAlignmentOption); + public Option Map { get; } = + new(new[] { "--map" }, SR.MapFileOption); + public Option MapCsv { get; } = + new(new[] { "--mapcsv" }, SR.MapCsvFileOption); + public Option Pdb { get; } = + new(new[] { "--pdb" }, SR.PdbFileOption); + public Option PdbPath { get; } = + new(new[] { "--pdb-path" }, SR.PdbFilePathOption); + public Option PerfMap { get; } = + new(new[] { "--perfmap" }, SR.PerfMapFileOption); + public Option PerfMapPath { get; } = + new(new[] { "--perfmap-path" }, SR.PerfMapFilePathOption); + public Option PerfMapFormatVersion { get; } = + new(new[] { "--perfmap-format-version" }, () => 0, SR.PerfMapFormatVersionOption); + public Option CrossModuleInlining { get; } = + new(new[] { "--opt-cross-module" }, SR.CrossModuleInlining); + public Option AsyncMethodOptimization { get; } = + new(new[] { "--opt-async-methods" }, SR.AsyncModuleOptimization); + public Option NonLocalGenericsModule { get; } = + new(new[] { "--non-local-generics-module" }, () => string.Empty, SR.NonLocalGenericsModule); + public Option MethodLayout { get; } = + new(new[] { "--method-layout" }, result => { - "defaultsort" => ReadyToRunMethodLayoutAlgorithm.DefaultSort, - "exclusiveweight" => ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight, - "hotcold" => ReadyToRunMethodLayoutAlgorithm.HotCold, - "hotwarmcold" => ReadyToRunMethodLayoutAlgorithm.HotWarmCold, - "callfrequency" => ReadyToRunMethodLayoutAlgorithm.CallFrequency, - "pettishansen" => ReadyToRunMethodLayoutAlgorithm.PettisHansen, - "random" => ReadyToRunMethodLayoutAlgorithm.Random, - _ => throw new CommandLineException(SR.InvalidMethodLayout) - }; - }, true, SR.MethodLayoutOption); - public Option FileLayout { get; } = - new(new[] { "--file-layout" }, result => - { - if (result.Tokens.Count == 0 ) - return ReadyToRunFileLayoutAlgorithm.DefaultSort; + if (result.Tokens.Count == 0 ) + return ReadyToRunMethodLayoutAlgorithm.DefaultSort; - return result.Tokens[0].Value.ToLowerInvariant() switch + return result.Tokens[0].Value.ToLowerInvariant() switch + { + "defaultsort" => ReadyToRunMethodLayoutAlgorithm.DefaultSort, + "exclusiveweight" => ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight, + "hotcold" => ReadyToRunMethodLayoutAlgorithm.HotCold, + "hotwarmcold" => ReadyToRunMethodLayoutAlgorithm.HotWarmCold, + "callfrequency" => ReadyToRunMethodLayoutAlgorithm.CallFrequency, + "pettishansen" => ReadyToRunMethodLayoutAlgorithm.PettisHansen, + "random" => ReadyToRunMethodLayoutAlgorithm.Random, + _ => throw new CommandLineException(SR.InvalidMethodLayout) + }; + }, true, SR.MethodLayoutOption); + public Option FileLayout { get; } = + new(new[] { "--file-layout" }, result => { - "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort, - "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder, - _ => throw new CommandLineException(SR.InvalidFileLayout) - }; - }, true, SR.FileLayoutOption); - public Option VerifyTypeAndFieldLayout { get; } = - new(new[] { "--verify-type-and-field-layout" }, SR.VerifyTypeAndFieldLayoutOption); - public Option CallChainProfileFile { get; } = - new(new[] { "--callchain-profile" }, SR.CallChainProfileFile); - public Option MakeReproPath { get; } = - new(new[] { "--make-repro-path" }, "Path where to place a repro package"); + if (result.Tokens.Count == 0 ) + return ReadyToRunFileLayoutAlgorithm.DefaultSort; - public bool CompositeOrInputBubble { get; private set; } - public OptimizationMode OptimizationMode { get; private set; } - public ParseResult Result { get; private set; } + return result.Tokens[0].Value.ToLowerInvariant() switch + { + "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort, + "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder, + _ => throw new CommandLineException(SR.InvalidFileLayout) + }; + }, true, SR.FileLayoutOption); + public Option VerifyTypeAndFieldLayout { get; } = + new(new[] { "--verify-type-and-field-layout" }, SR.VerifyTypeAndFieldLayoutOption); + public Option CallChainProfileFile { get; } = + new(new[] { "--callchain-profile" }, SR.CallChainProfileFile); + public Option MakeReproPath { get; } = + new(new[] { "--make-repro-path" }, "Path where to place a repro package"); - public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) - { - AddArgument(InputFilePaths); - AddOption(UnrootedInputFilePaths); - AddOption(ReferenceFilePaths); - AddOption(InstructionSet); - AddOption(MibcFilePaths); - AddOption(OutputFilePath); - AddOption(CompositeRootPath); - AddOption(Optimize); - AddOption(OptimizeDisabled); - AddOption(OptimizeSpace); - AddOption(OptimizeTime); - AddOption(InputBubble); - AddOption(InputBubbleReferenceFilePaths); - AddOption(Composite); - AddOption(CompositeKeyFile); - AddOption(CompileNoMethods); - AddOption(OutNearInput); - AddOption(SingleFileCompilation); - AddOption(Partial); - AddOption(CompileBubbleGenerics); - AddOption(EmbedPgoData); - AddOption(DgmlLogFileName); - AddOption(GenerateFullDgmlLog); - AddOption(IsVerbose); - AddOption(SystemModuleName); - AddOption(WaitForDebugger); - AddOption(CodegenOptions); - AddOption(Resilient); - AddOption(ImageBase); - AddOption(TargetArchitecture); - AddOption(TargetOS); - AddOption(JitPath); - AddOption(PrintReproInstructions); - AddOption(SingleMethodTypeName); - AddOption(SingleMethodName); - AddOption(SingleMethodIndex); - AddOption(SingleMethodGenericArgs); - AddOption(Parallelism); - AddOption(CustomPESectionAlignment); - AddOption(Map); - AddOption(MapCsv); - AddOption(Pdb); - AddOption(PdbPath); - AddOption(PerfMap); - AddOption(PerfMapPath); - AddOption(PerfMapFormatVersion); - AddOption(CrossModuleInlining); - AddOption(AsyncMethodOptimization); - AddOption(NonLocalGenericsModule); - AddOption(MethodLayout); - AddOption(FileLayout); - AddOption(VerifyTypeAndFieldLayout); - AddOption(CallChainProfileFile); - AddOption(MakeReproPath); + public bool CompositeOrInputBubble { get; private set; } + public OptimizationMode OptimizationMode { get; private set; } + public ParseResult Result { get; private set; } + + public static bool IsArmel { get; private set; } - this.SetHandler(context => + public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) { - Result = context.ParseResult; - CompositeOrInputBubble = context.ParseResult.GetValueForOption(Composite) | context.ParseResult.GetValueForOption(InputBubble); - if (context.ParseResult.GetValueForOption(OptimizeSpace)) - { - OptimizationMode = OptimizationMode.PreferSize; - } - else if (context.ParseResult.GetValueForOption(OptimizeTime)) - { - OptimizationMode = OptimizationMode.PreferSpeed; - } - else if (context.ParseResult.GetValueForOption(Optimize)) - { - OptimizationMode = OptimizationMode.Blended; - } - else - { - OptimizationMode = OptimizationMode.None; - } + AddArgument(InputFilePaths); + AddOption(UnrootedInputFilePaths); + AddOption(ReferenceFilePaths); + AddOption(InstructionSet); + AddOption(MibcFilePaths); + AddOption(OutputFilePath); + AddOption(CompositeRootPath); + AddOption(Optimize); + AddOption(OptimizeDisabled); + AddOption(OptimizeSpace); + AddOption(OptimizeTime); + AddOption(InputBubble); + AddOption(InputBubbleReferenceFilePaths); + AddOption(Composite); + AddOption(CompositeKeyFile); + AddOption(CompileNoMethods); + AddOption(OutNearInput); + AddOption(SingleFileCompilation); + AddOption(Partial); + AddOption(CompileBubbleGenerics); + AddOption(EmbedPgoData); + AddOption(DgmlLogFileName); + AddOption(GenerateFullDgmlLog); + AddOption(IsVerbose); + AddOption(SystemModuleName); + AddOption(WaitForDebugger); + AddOption(CodegenOptions); + AddOption(Resilient); + AddOption(ImageBase); + AddOption(TargetArchitecture); + AddOption(TargetOS); + AddOption(JitPath); + AddOption(PrintReproInstructions); + AddOption(SingleMethodTypeName); + AddOption(SingleMethodName); + AddOption(SingleMethodIndex); + AddOption(SingleMethodGenericArgs); + AddOption(Parallelism); + AddOption(CustomPESectionAlignment); + AddOption(Map); + AddOption(MapCsv); + AddOption(Pdb); + AddOption(PdbPath); + AddOption(PerfMap); + AddOption(PerfMapPath); + AddOption(PerfMapFormatVersion); + AddOption(CrossModuleInlining); + AddOption(AsyncMethodOptimization); + AddOption(NonLocalGenericsModule); + AddOption(MethodLayout); + AddOption(FileLayout); + AddOption(VerifyTypeAndFieldLayout); + AddOption(CallChainProfileFile); + AddOption(MakeReproPath); - try + this.SetHandler(context => { - int alignment = context.ParseResult.GetValueForOption(CustomPESectionAlignment); - if (alignment != 0) + Result = context.ParseResult; + CompositeOrInputBubble = context.ParseResult.GetValueForOption(Composite) | context.ParseResult.GetValueForOption(InputBubble); + if (context.ParseResult.GetValueForOption(OptimizeSpace)) + { + OptimizationMode = OptimizationMode.PreferSize; + } + else if (context.ParseResult.GetValueForOption(OptimizeTime)) + { + OptimizationMode = OptimizationMode.PreferSpeed; + } + else if (context.ParseResult.GetValueForOption(Optimize)) { - // Must be a power of two and >= 4096 - if (alignment < 4096 || (alignment & (alignment - 1)) != 0) - throw new CommandLineException(SR.InvalidCustomPESectionAlignment); + OptimizationMode = OptimizationMode.Blended; + } + else + { + OptimizationMode = OptimizationMode.None; } - string makeReproPath = context.ParseResult.GetValueForOption(MakeReproPath); - if (makeReproPath != null) + try { - // Create a repro package in the specified path - // This package will have the set of input files needed for compilation - // + the original command line arguments - // + a rsp file that should work to directly run out of the zip file + int alignment = context.ParseResult.GetValueForOption(CustomPESectionAlignment); + if (alignment != 0) + { + // Must be a power of two and >= 4096 + if (alignment < 4096 || (alignment & (alignment - 1)) != 0) + throw new CommandLineException(SR.InvalidCustomPESectionAlignment); + } - Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, - context.ParseResult, new[] { "r", "reference", "u", "unrooted-input-file-paths", "m", "mibc", "inputbubbleref" }); - } + string makeReproPath = context.ParseResult.GetValueForOption(MakeReproPath); + if (makeReproPath != null) + { + // Create a repro package in the specified path + // This package will have the set of input files needed for compilation + // + the original command line arguments + // + a rsp file that should work to directly run out of the zip file + + Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, + context.ParseResult, new[] { "r", "reference", "u", "unrooted-input-file-paths", "m", "mibc", "inputbubbleref" }); + } - context.ExitCode = new Program(this).Run(); - } + context.ExitCode = new Program(this).Run(); + } #if DEBUG - catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) - { - throw new NotSupportedException(); // Unreachable - } + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } #else - catch (Exception e) - { - Console.ResetColor(); - Console.ForegroundColor = ConsoleColor.Red; + catch (Exception e) + { + Console.ResetColor(); + Console.ForegroundColor = ConsoleColor.Red; - Console.Error.WriteLine("Error: " + e.Message); - Console.Error.WriteLine(e.ToString()); + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); - Console.ResetColor(); + Console.ResetColor(); - context.ExitCode = 1; - } + context.ExitCode = 1; + } #endif - }); - } - - public static IEnumerable GetExtendedHelp(HelpContext _) - { - foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) - yield return sectionDelegate; + }); + } - yield return _ => + public static IEnumerable GetExtendedHelp(HelpContext _) { - Console.WriteLine(SR.OptionPassingHelp); - Console.WriteLine(); - Console.WriteLine(SR.DashDashHelp); - Console.WriteLine(); + foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) + yield return sectionDelegate; - string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"}; - string[] ValidOS = new string[] {"windows", "linux", "osx"}; + yield return _ => + { + Console.WriteLine(SR.OptionPassingHelp); + Console.WriteLine(); + Console.WriteLine(SR.DashDashHelp); + Console.WriteLine(); - Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant())); - Console.WriteLine(); - Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetarch", String.Join("', '", ValidArchitectures), Helpers.GetTargetArchitecture(null).ToString().ToLowerInvariant())); - Console.WriteLine(); + string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"}; + string[] ValidOS = new string[] {"windows", "linux", "osx"}; - Console.WriteLine(SR.InstructionSetHelp); - foreach (string arch in ValidArchitectures) - { - Console.Write(arch); - Console.Write(": "); + Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant())); + Console.WriteLine(); + Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetarch", String.Join("', '", ValidArchitectures), Helpers.GetTargetArchitecture(null).ToString().ToLowerInvariant())); + Console.WriteLine(); - TargetArchitecture targetArch = Helpers.GetTargetArchitecture(arch); - bool first = true; - foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) + Console.WriteLine(SR.InstructionSetHelp); + foreach (string arch in ValidArchitectures) { - // Only instruction sets with are specifiable should be printed to the help text - if (instructionSet.Specifiable) + Console.Write(arch); + Console.Write(": "); + + TargetArchitecture targetArch = Helpers.GetTargetArchitecture(arch); + bool first = true; + foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) { - if (first) + // Only instruction sets with are specifiable should be printed to the help text + if (instructionSet.Specifiable) { - first = false; + if (first) + { + first = false; + } + else + { + Console.Write(", "); + } + Console.Write(instructionSet.Name); } - else - { - Console.Write(", "); - } - Console.Write(instructionSet.Name); } + + Console.WriteLine(); } Console.WriteLine(); - } - - Console.WriteLine(); - Console.WriteLine(SR.CpuFamilies); - Console.WriteLine(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); - }; - } + Console.WriteLine(SR.CpuFamilies); + Console.WriteLine(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); + }; + } #if DEBUG - private static bool DumpReproArguments(CodeGenerationFailedException ex) - { - Console.WriteLine(SR.DumpReproInstructions); + private static bool DumpReproArguments(CodeGenerationFailedException ex) + { + Console.WriteLine(SR.DumpReproInstructions); - MethodDesc failingMethod = ex.Method; - Console.WriteLine(Program.CreateReproArgumentString(failingMethod)); - return false; - } + MethodDesc failingMethod = ex.Method; + Console.WriteLine(Program.CreateReproArgumentString(failingMethod)); + return false; + } #endif + } } diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 1385e06dd4322..7df61ff3f44d0 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -22,967 +22,961 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.IBC; -namespace ILCompiler; - -internal class Program +namespace ILCompiler { - private readonly Crossgen2RootCommand _command; - - // Modules and their names after loading - private Dictionary _allInputFilePaths = new(); - private List _referenceableModules = new(); - - private ReadyToRunCompilerContext _typeSystemContext; - private ulong _imageBase; - - private readonly bool _inputBubble; - private readonly bool _singleFileCompilation; - private readonly bool _outNearInput; - private readonly string _outputFilePath; - private readonly TargetArchitecture _targetArchitecture; - private readonly TargetOS _targetOS; - private readonly bool _armelAbi; - - public Program(Crossgen2RootCommand command) + internal class Program { - _command = command; - _inputBubble = Get(command.InputBubble); - _singleFileCompilation = Get(command.SingleFileCompilation); - _outNearInput = Get(command.OutNearInput); - _outputFilePath = Get(command.OutputFilePath); - _targetOS = Get(command.TargetOS); - _targetArchitecture = Get(command.TargetArchitecture); - _armelAbi = _targetArchitecture == TargetArchitecture.ARMEL; - - if (_armelAbi) - { - _targetArchitecture = TargetArchitecture.ARM; - } + private readonly Crossgen2RootCommand _command; - if (command.Result.GetValueForOption(command.WaitForDebugger)) - { - Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); - Console.ReadLine(); - } - } + // Modules and their names after loading + private Dictionary _allInputFilePaths = new(); + private List _referenceableModules = new(); - private InstructionSetSupport ConfigureInstructionSetSupport() - { - InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); + private ReadyToRunCompilerContext _typeSystemContext; + private ulong _imageBase; - // Ready to run images are built with certain instruction set baselines - if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) - { - instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication - } - else if (_targetArchitecture == TargetArchitecture.ARM64) + private readonly bool _inputBubble; + private readonly bool _singleFileCompilation; + private readonly bool _outNearInput; + private readonly string _outputFilePath; + private readonly TargetArchitecture _targetArchitecture; + private readonly TargetOS _targetOS; + + public Program(Crossgen2RootCommand command) { - if (_targetOS == TargetOS.OSX) - { - // For osx-arm64 we know that apple-m1 is a baseline - instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); - } - else + _command = command; + _inputBubble = Get(command.InputBubble); + _singleFileCompilation = Get(command.SingleFileCompilation); + _outNearInput = Get(command.OutNearInput); + _outputFilePath = Get(command.OutputFilePath); + _targetOS = Get(command.TargetOS); + _targetArchitecture = Get(command.TargetArchitecture); + + if (command.Result.GetValueForOption(command.WaitForDebugger)) { - instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication + Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); + Console.ReadLine(); } } - string instructionSetArg = Get(_command.InstructionSet); - if (instructionSetArg != null) + private InstructionSetSupport ConfigureInstructionSetSupport() { - List instructionSetParams = new List(); + InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); - // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = instructionSetArg.Split(","); - for (int i = 0; i < instructionSetParamsInput.Length; i++) + // Ready to run images are built with certain instruction set baselines + if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) { - string instructionSet = instructionSetParamsInput[i]; - - if (String.IsNullOrEmpty(instructionSet)) - throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, "")); - - char firstChar = instructionSet[0]; - if ((firstChar != '+') && (firstChar != '-')) + instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication + } + else if (_targetArchitecture == TargetArchitecture.ARM64) + { + if (_targetOS == TargetOS.OSX) + { + // For osx-arm64 we know that apple-m1 is a baseline + instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); + } + else { - instructionSet = "+" + instructionSet; + instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication } - instructionSetParams.Add(instructionSet); } - Dictionary instructionSetSpecification = new Dictionary(); - foreach (string instructionSetSpecifier in instructionSetParams) + string instructionSetArg = Get(_command.InstructionSet); + if (instructionSetArg != null) { - string instructionSet = instructionSetSpecifier.Substring(1); + List instructionSetParams = new List(); - bool enabled = instructionSetSpecifier[0] == '+' ? true : false; - if (enabled) + // Normalize instruction set format to include implied +. + string[] instructionSetParamsInput = instructionSetArg.Split(","); + for (int i = 0; i < instructionSetParamsInput.Length; i++) { - if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) - throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet)); + string instructionSet = instructionSetParamsInput[i]; + + if (String.IsNullOrEmpty(instructionSet)) + throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, "")); + + char firstChar = instructionSet[0]; + if ((firstChar != '+') && (firstChar != '-')) + { + instructionSet = "+" + instructionSet; + } + instructionSetParams.Add(instructionSet); } - else + + Dictionary instructionSetSpecification = new Dictionary(); + foreach (string instructionSetSpecifier in instructionSetParams) { - if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) - throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet)); + string instructionSet = instructionSetSpecifier.Substring(1); + + bool enabled = instructionSetSpecifier[0] == '+' ? true : false; + if (enabled) + { + if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) + throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet)); + } + else + { + if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) + throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet)); + } } } - } - instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, - (string specifiedInstructionSet, string impliedInstructionSet) => - throw new CommandLineException(String.Format(SR.InstructionSetInvalidImplication, specifiedInstructionSet, impliedInstructionSet))); + instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, + (string specifiedInstructionSet, string impliedInstructionSet) => + throw new CommandLineException(String.Format(SR.InstructionSetInvalidImplication, specifiedInstructionSet, impliedInstructionSet))); - InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); + InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); - // Ready to run images are built with certain instruction sets that are optimistically assumed to be present - if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) - { - // For ReadyToRun we set these hardware features as enabled always, as most - // of hardware in the wild supports them. Note that we do not indicate support for AVX, or any other - // instruction set which uses the VEX encodings as the presence of those makes otherwise acceptable - // code be unusable on hardware which does not support VEX encodings, as well as emulators that do not - // support AVX instructions. As the jit generates logic that depends on these features it will call - // notifyInstructionSetUsage, which will result in generation of a fixup to verify the behavior of - // code. - // - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); - } + // Ready to run images are built with certain instruction sets that are optimistically assumed to be present + if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) + { + // For ReadyToRun we set these hardware features as enabled always, as most + // of hardware in the wild supports them. Note that we do not indicate support for AVX, or any other + // instruction set which uses the VEX encodings as the presence of those makes otherwise acceptable + // code be unusable on hardware which does not support VEX encodings, as well as emulators that do not + // support AVX instructions. As the jit generates logic that depends on these features it will call + // notifyInstructionSetUsage, which will result in generation of a fixup to verify the behavior of + // code. + // + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); + } - optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, - (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); - optimisticInstructionSet.Remove(unsupportedInstructionSet); - optimisticInstructionSet.Add(supportedInstructionSet); + optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, + (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); + optimisticInstructionSet.Remove(unsupportedInstructionSet); + optimisticInstructionSet.Add(supportedInstructionSet); - return new InstructionSetSupport(supportedInstructionSet, - unsupportedInstructionSet, - optimisticInstructionSet, - InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), - _targetArchitecture); - } + return new InstructionSetSupport(supportedInstructionSet, + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), + _targetArchitecture); + } - private void ConfigureImageBase(TargetDetails targetDetails) - { - bool is64BitTarget = targetDetails.PointerSize == sizeof(long); + private void ConfigureImageBase(TargetDetails targetDetails) + { + bool is64BitTarget = targetDetails.PointerSize == sizeof(long); - string imageBaseArg = Get(_command.ImageBase); - if (imageBaseArg != null) - _imageBase = is64BitTarget ? Convert.ToUInt64(imageBaseArg, 16) : Convert.ToUInt32(imageBaseArg, 16); - else - _imageBase = is64BitTarget ? PEWriter.PE64HeaderConstants.DllImageBase : PEWriter.PE32HeaderConstants.ImageBase; - } + string imageBaseArg = Get(_command.ImageBase); + if (imageBaseArg != null) + _imageBase = is64BitTarget ? Convert.ToUInt64(imageBaseArg, 16) : Convert.ToUInt32(imageBaseArg, 16); + else + _imageBase = is64BitTarget ? PEWriter.PE64HeaderConstants.DllImageBase : PEWriter.PE32HeaderConstants.ImageBase; + } - public int Run() - { - if (_outputFilePath == null && !_outNearInput) - throw new CommandLineException(SR.MissingOutputFile); + public int Run() + { + if (_outputFilePath == null && !_outNearInput) + throw new CommandLineException(SR.MissingOutputFile); - if (_singleFileCompilation && !_outNearInput) - throw new CommandLineException(SR.MissingOutNearInput); + if (_singleFileCompilation && !_outNearInput) + throw new CommandLineException(SR.MissingOutNearInput); - InstructionSetSupport instructionSetSupport = ConfigureInstructionSetSupport(); + InstructionSetSupport instructionSetSupport = ConfigureInstructionSetSupport(); - SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; + SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, Crossgen2RootCommand.IsArmel ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); - ConfigureImageBase(targetDetails); + ConfigureImageBase(targetDetails); - bool versionBubbleIncludesCoreLib = false; - Dictionary inputFilePathsArg = _command.Result.GetValueForArgument(_command.InputFilePaths); - Dictionary unrootedInputFilePathsArg = Get(_command.UnrootedInputFilePaths); + bool versionBubbleIncludesCoreLib = false; + Dictionary inputFilePathsArg = _command.Result.GetValueForArgument(_command.InputFilePaths); + Dictionary unrootedInputFilePathsArg = Get(_command.UnrootedInputFilePaths); - if (_inputBubble) - { - versionBubbleIncludesCoreLib = true; - } - else - { - if (!_singleFileCompilation) + if (_inputBubble) { - foreach (var inputFile in inputFilePathsArg) + versionBubbleIncludesCoreLib = true; + } + else + { + if (!_singleFileCompilation) { - if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) + foreach (var inputFile in inputFilePathsArg) { - versionBubbleIncludesCoreLib = true; - break; + if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) + { + versionBubbleIncludesCoreLib = true; + break; + } } } - } - if (!versionBubbleIncludesCoreLib) - { - foreach (var inputFile in unrootedInputFilePathsArg) + if (!versionBubbleIncludesCoreLib) { - if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) + foreach (var inputFile in unrootedInputFilePathsArg) { - versionBubbleIncludesCoreLib = true; - break; + if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) + { + versionBubbleIncludesCoreLib = true; + break; + } } } } - } - - // - // Initialize type system context - // - _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib); - - string compositeRootPath = Get(_command.CompositeRootPath); - // Collections for already loaded modules - Dictionary inputFilePaths = new Dictionary(); - Dictionary unrootedInputFilePaths = new Dictionary(); - HashSet versionBubbleModulesHash = new HashSet(); - Dictionary referenceFilePaths = Get(_command.ReferenceFilePaths); - - using (PerfEventSource.StartStopEvents.LoadingEvents()) - { - // - // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since - // some tests contain a mixture of both managed and native binaries. // - // See: https://github.com/dotnet/corert/issues/2785 - // - // When we undo this hack, replace the foreach with - // typeSystemContext.InputFilePaths = inFilePaths; + // Initialize type system context // + _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib); + + string compositeRootPath = Get(_command.CompositeRootPath); - foreach (var inputFile in inputFilePathsArg) + // Collections for already loaded modules + Dictionary inputFilePaths = new Dictionary(); + Dictionary unrootedInputFilePaths = new Dictionary(); + HashSet versionBubbleModulesHash = new HashSet(); + Dictionary referenceFilePaths = Get(_command.ReferenceFilePaths); + + using (PerfEventSource.StartStopEvents.LoadingEvents()) { - try + // + // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since + // some tests contain a mixture of both managed and native binaries. + // + // See: https://github.com/dotnet/corert/issues/2785 + // + // When we undo this hack, replace the foreach with + // typeSystemContext.InputFilePaths = inFilePaths; + // + + foreach (var inputFile in inputFilePathsArg) { - var module = _typeSystemContext.GetModuleFromPath(inputFile.Value); - if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0 - && module.PEReader.TryGetReadyToRunHeader(out int _)) + try { - Console.WriteLine(SR.IgnoringCompositeImage, inputFile.Value); - continue; + var module = _typeSystemContext.GetModuleFromPath(inputFile.Value); + if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0 + && module.PEReader.TryGetReadyToRunHeader(out int _)) + { + Console.WriteLine(SR.IgnoringCompositeImage, inputFile.Value); + continue; + } + _allInputFilePaths.Add(inputFile.Key, inputFile.Value); + inputFilePaths.Add(inputFile.Key, inputFile.Value); + _referenceableModules.Add(module); + if (compositeRootPath == null) + { + compositeRootPath = Path.GetDirectoryName(inputFile.Value); + } } - _allInputFilePaths.Add(inputFile.Key, inputFile.Value); - inputFilePaths.Add(inputFile.Key, inputFile.Value); - _referenceableModules.Add(module); - if (compositeRootPath == null) + catch (TypeSystemException.BadImageFormatException) { - compositeRootPath = Path.GetDirectoryName(inputFile.Value); + // Keep calm and carry on. } } - catch (TypeSystemException.BadImageFormatException) - { - // Keep calm and carry on. - } - } - foreach (var unrootedInputFile in unrootedInputFilePathsArg) - { - try + foreach (var unrootedInputFile in unrootedInputFilePathsArg) { - var module = _typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); - if (!_allInputFilePaths.ContainsKey(unrootedInputFile.Key)) + try { - _allInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); - unrootedInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); - _referenceableModules.Add(module); - if (compositeRootPath == null) + var module = _typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); + if (!_allInputFilePaths.ContainsKey(unrootedInputFile.Key)) { - compositeRootPath = Path.GetDirectoryName(unrootedInputFile.Value); + _allInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); + unrootedInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); + _referenceableModules.Add(module); + if (compositeRootPath == null) + { + compositeRootPath = Path.GetDirectoryName(unrootedInputFile.Value); + } } } + catch (TypeSystemException.BadImageFormatException) + { + // Keep calm and carry on. + } } - catch (TypeSystemException.BadImageFormatException) - { - // Keep calm and carry on. - } - } - CheckManagedCppInputFiles(_allInputFilePaths.Values); + CheckManagedCppInputFiles(_allInputFilePaths.Values); - _typeSystemContext.InputFilePaths = _allInputFilePaths; - _typeSystemContext.ReferenceFilePaths = referenceFilePaths; + _typeSystemContext.InputFilePaths = _allInputFilePaths; + _typeSystemContext.ReferenceFilePaths = referenceFilePaths; - if (_typeSystemContext.InputFilePaths.Count == 0) - { - if (inputFilePathsArg.Count > 0) + if (_typeSystemContext.InputFilePaths.Count == 0) { - Console.WriteLine(SR.InputWasNotLoadable); - return 2; - } - throw new CommandLineException(SR.NoInputFiles); - } - - Dictionary inputBubbleReferenceFilePaths = Get(_command.InputBubbleReferenceFilePaths); - foreach (var referenceFile in referenceFilePaths.Values) - { - try - { - EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile, throwOnFailureToLoad: false); - if (module == null) - continue; - - _referenceableModules.Add(module); - if (_inputBubble && inputBubbleReferenceFilePaths.Count == 0) + if (inputFilePathsArg.Count > 0) { - // In large version bubble mode add reference paths to the compilation group - // Consider bubble as large if no explicit bubble references were passed - versionBubbleModulesHash.Add(module); + Console.WriteLine(SR.InputWasNotLoadable); + return 2; } + throw new CommandLineException(SR.NoInputFiles); } - catch { } // Ignore non-managed pe files - } - if (_inputBubble) - { - foreach (var referenceFile in inputBubbleReferenceFilePaths.Values) + Dictionary inputBubbleReferenceFilePaths = Get(_command.InputBubbleReferenceFilePaths); + foreach (var referenceFile in referenceFilePaths.Values) { try { EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile, throwOnFailureToLoad: false); - if (module == null) continue; - versionBubbleModulesHash.Add(module); + _referenceableModules.Add(module); + if (_inputBubble && inputBubbleReferenceFilePaths.Count == 0) + { + // In large version bubble mode add reference paths to the compilation group + // Consider bubble as large if no explicit bubble references were passed + versionBubbleModulesHash.Add(module); + } } catch { } // Ignore non-managed pe files } - } - } - string systemModuleName = Get(_command.SystemModuleName) ?? Helpers.DefaultSystemModule; - _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName)); - CompilerTypeSystemContext typeSystemContext = _typeSystemContext; + if (_inputBubble) + { + foreach (var referenceFile in inputBubbleReferenceFilePaths.Values) + { + try + { + EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile, throwOnFailureToLoad: false); - if (_singleFileCompilation) - { - var singleCompilationInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (module == null) + continue; - foreach (var inputFile in inputFilePaths) - { - var singleCompilationVersionBubbleModulesHash = new HashSet(versionBubbleModulesHash); + versionBubbleModulesHash.Add(module); + } + catch { } // Ignore non-managed pe files + } + } + } + + string systemModuleName = Get(_command.SystemModuleName) ?? Helpers.DefaultSystemModule; + _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName)); + CompilerTypeSystemContext typeSystemContext = _typeSystemContext; - singleCompilationInputFilePaths.Clear(); - singleCompilationInputFilePaths.Add(inputFile.Key, inputFile.Value); - typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; + if (_singleFileCompilation) + { + var singleCompilationInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (!_inputBubble) + foreach (var inputFile in inputFilePaths) { - bool singleCompilationVersionBubbleIncludesCoreLib = versionBubbleIncludesCoreLib || (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0); + var singleCompilationVersionBubbleModulesHash = new HashSet(versionBubbleModulesHash); - typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext); + singleCompilationInputFilePaths.Clear(); + singleCompilationInputFilePaths.Add(inputFile.Key, inputFile.Value); typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; - typeSystemContext.ReferenceFilePaths = referenceFilePaths; - typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName)); - } - RunSingleCompilation(singleCompilationInputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, singleCompilationVersionBubbleModulesHash, typeSystemContext); - } + if (!_inputBubble) + { + bool singleCompilationVersionBubbleIncludesCoreLib = versionBubbleIncludesCoreLib || (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0); - // In case of inputbubble ni.dll are created as ni.dll.tmp in order to not interfere with crossgen2, move them all to ni.dll - // See https://github.com/dotnet/runtime/issues/55663#issuecomment-898161751 for more details - if (_inputBubble) - { - foreach (var inputFile in inputFilePaths) + typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext); + typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; + typeSystemContext.ReferenceFilePaths = referenceFilePaths; + typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName)); + } + + RunSingleCompilation(singleCompilationInputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, singleCompilationVersionBubbleModulesHash, typeSystemContext); + } + + // In case of inputbubble ni.dll are created as ni.dll.tmp in order to not interfere with crossgen2, move them all to ni.dll + // See https://github.com/dotnet/runtime/issues/55663#issuecomment-898161751 for more details + if (_inputBubble) { - var tmpOutFile = inputFile.Value.Replace(".dll", ".ni.dll.tmp"); - var outFile = inputFile.Value.Replace(".dll", ".ni.dll"); - Console.WriteLine($@"Moving R2R PE file: {tmpOutFile} to {outFile}"); - System.IO.File.Move(tmpOutFile, outFile); + foreach (var inputFile in inputFilePaths) + { + var tmpOutFile = inputFile.Value.Replace(".dll", ".ni.dll.tmp"); + var outFile = inputFile.Value.Replace(".dll", ".ni.dll"); + Console.WriteLine($@"Moving R2R PE file: {tmpOutFile} to {outFile}"); + System.IO.File.Move(tmpOutFile, outFile); + } } } - } - else - { - RunSingleCompilation(inputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, versionBubbleModulesHash, typeSystemContext); - } + else + { + RunSingleCompilation(inputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, versionBubbleModulesHash, typeSystemContext); + } - return 0; - } + return 0; + } - private void RunSingleCompilation(Dictionary inFilePaths, InstructionSetSupport instructionSetSupport, string compositeRootPath, Dictionary unrootedInputFilePaths, HashSet versionBubbleModulesHash, CompilerTypeSystemContext typeSystemContext) - { - // - // Initialize output filename - // - var e = inFilePaths.GetEnumerator(); - e.MoveNext(); - string inFilePath = e.Current.Value; - string inputFileExtension = Path.GetExtension(inFilePath); - string nearOutFilePath = inputFileExtension switch + private void RunSingleCompilation(Dictionary inFilePaths, InstructionSetSupport instructionSetSupport, string compositeRootPath, Dictionary unrootedInputFilePaths, HashSet versionBubbleModulesHash, CompilerTypeSystemContext typeSystemContext) { - ".dll" => Path.ChangeExtension(inFilePath, - _singleFileCompilation&& _inputBubble - ? ".ni.dll.tmp" - : ".ni.dll"), - ".exe" => Path.ChangeExtension(inFilePath, - _singleFileCompilation && _inputBubble - ? ".ni.exe.tmp" - : ".ni.exe"), - _ => throw new CommandLineException(string.Format(SR.UnsupportedInputFileExtension, inputFileExtension)) - }; - - string outFile = _outNearInput ? nearOutFilePath : _outputFilePath; - string dgmlLogFileName = Get(_command.DgmlLogFileName); - - using (PerfEventSource.StartStopEvents.CompilationEvents()) - { - ICompilation compilation; - using (PerfEventSource.StartStopEvents.LoadingEvents()) + // + // Initialize output filename + // + var e = inFilePaths.GetEnumerator(); + e.MoveNext(); + string inFilePath = e.Current.Value; + string inputFileExtension = Path.GetExtension(inFilePath); + string nearOutFilePath = inputFileExtension switch { - List inputModules = new List(); - List rootingModules = new List(); - HashSet crossModuleInlineableCode = new HashSet(); - - foreach (var inputFile in inFilePaths) + ".dll" => Path.ChangeExtension(inFilePath, + _singleFileCompilation&& _inputBubble + ? ".ni.dll.tmp" + : ".ni.dll"), + ".exe" => Path.ChangeExtension(inFilePath, + _singleFileCompilation && _inputBubble + ? ".ni.exe.tmp" + : ".ni.exe"), + _ => throw new CommandLineException(string.Format(SR.UnsupportedInputFileExtension, inputFileExtension)) + }; + + string outFile = _outNearInput ? nearOutFilePath : _outputFilePath; + string dgmlLogFileName = Get(_command.DgmlLogFileName); + + using (PerfEventSource.StartStopEvents.CompilationEvents()) + { + ICompilation compilation; + using (PerfEventSource.StartStopEvents.LoadingEvents()) { - EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); - inputModules.Add(module); - rootingModules.Add(module); - versionBubbleModulesHash.Add(module); + List inputModules = new List(); + List rootingModules = new List(); + HashSet crossModuleInlineableCode = new HashSet(); - - if (!_command.CompositeOrInputBubble) + foreach (var inputFile in inFilePaths) { - break; + EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); + inputModules.Add(module); + rootingModules.Add(module); + versionBubbleModulesHash.Add(module); + + + if (!_command.CompositeOrInputBubble) + { + break; + } } - } - foreach (var unrootedInputFile in unrootedInputFilePaths) - { - EcmaModule module = typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); - inputModules.Add(module); - versionBubbleModulesHash.Add(module); - } + foreach (var unrootedInputFile in unrootedInputFilePaths) + { + EcmaModule module = typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); + inputModules.Add(module); + versionBubbleModulesHash.Add(module); + } - string[] crossModuleInlining = Get(_command.CrossModuleInlining); - if (crossModuleInlining.Length > 0) - { - foreach (var crossModulePgoAssemblyName in crossModuleInlining) + string[] crossModuleInlining = Get(_command.CrossModuleInlining); + if (crossModuleInlining.Length > 0) { - foreach (var module in _referenceableModules) + foreach (var crossModulePgoAssemblyName in crossModuleInlining) { - if (!versionBubbleModulesHash.Contains(module)) + foreach (var module in _referenceableModules) { - if (crossModulePgoAssemblyName == "*" || - (String.Compare(crossModulePgoAssemblyName, module.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase) == 0)) + if (!versionBubbleModulesHash.Contains(module)) { - crossModuleInlineableCode.Add((EcmaModule)module); + if (crossModulePgoAssemblyName == "*" || + (String.Compare(crossModulePgoAssemblyName, module.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase) == 0)) + { + crossModuleInlineableCode.Add((EcmaModule)module); + } } } } } - } - - // - // Initialize compilation group and compilation roots - // - - // Single method mode? - MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); - var logger = new Logger(Console.Out, Get(_command.IsVerbose)); + // + // Initialize compilation group and compilation roots + // - List mibcFiles = new List(); - foreach (var file in Get(_command.MibcFilePaths)) - { - mibcFiles.Add(file); - } + // Single method mode? + MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); - List versionBubbleModules = new List(versionBubbleModulesHash); - bool composite = Get(_command.Composite); - if (!composite && inputModules.Count != 1) - { - throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); - } + var logger = new Logger(Console.Out, Get(_command.IsVerbose)); - bool compileBubbleGenerics = Get(_command.CompileBubbleGenerics); - ReadyToRunCompilationModuleGroupBase compilationGroup; - List compilationRoots = new List(); - ReadyToRunCompilationModuleGroupConfig groupConfig = new ReadyToRunCompilationModuleGroupConfig(); - groupConfig.Context = typeSystemContext; - groupConfig.IsCompositeBuildMode = composite; - groupConfig.IsInputBubble = _inputBubble; - groupConfig.CompilationModuleSet = inputModules; - groupConfig.VersionBubbleModuleSet = versionBubbleModules; - groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = compileBubbleGenerics; - groupConfig.CrossModuleGenericCompilation = crossModuleInlineableCode.Count > 0; - groupConfig.CrossModuleInlining = groupConfig.CrossModuleGenericCompilation; // Currently we set these flags to the same values - groupConfig.CrossModuleInlineable = crossModuleInlineableCode; - groupConfig.CompileAllPossibleCrossModuleCode = false; - - // Handle non-local generics command line option - ModuleDesc nonLocalGenericsHome = compileBubbleGenerics ? inputModules[0] : null; - string nonLocalGenericsModule = Get(_command.NonLocalGenericsModule); - if (nonLocalGenericsModule == "*") - { - groupConfig.CompileAllPossibleCrossModuleCode = true; - nonLocalGenericsHome = inputModules[0]; - } - else if (nonLocalGenericsModule == "") - { - // Nothing was specified - } - else - { - bool matchFound = false; + List mibcFiles = new List(); + foreach (var file in Get(_command.MibcFilePaths)) + { + mibcFiles.Add(file); + } - // Allow module to be specified by assembly name or by filename - if (nonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) - nonLocalGenericsModule = Path.GetFileNameWithoutExtension(nonLocalGenericsModule); - foreach (var module in inputModules) + List versionBubbleModules = new List(versionBubbleModulesHash); + bool composite = Get(_command.Composite); + if (!composite && inputModules.Count != 1) { - if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) - { - matchFound = true; - nonLocalGenericsHome = module; - groupConfig.CompileAllPossibleCrossModuleCode = true; - break; - } + throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); } - if (!matchFound) + bool compileBubbleGenerics = Get(_command.CompileBubbleGenerics); + ReadyToRunCompilationModuleGroupBase compilationGroup; + List compilationRoots = new List(); + ReadyToRunCompilationModuleGroupConfig groupConfig = new ReadyToRunCompilationModuleGroupConfig(); + groupConfig.Context = typeSystemContext; + groupConfig.IsCompositeBuildMode = composite; + groupConfig.IsInputBubble = _inputBubble; + groupConfig.CompilationModuleSet = inputModules; + groupConfig.VersionBubbleModuleSet = versionBubbleModules; + groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = compileBubbleGenerics; + groupConfig.CrossModuleGenericCompilation = crossModuleInlineableCode.Count > 0; + groupConfig.CrossModuleInlining = groupConfig.CrossModuleGenericCompilation; // Currently we set these flags to the same values + groupConfig.CrossModuleInlineable = crossModuleInlineableCode; + groupConfig.CompileAllPossibleCrossModuleCode = false; + + // Handle non-local generics command line option + ModuleDesc nonLocalGenericsHome = compileBubbleGenerics ? inputModules[0] : null; + string nonLocalGenericsModule = Get(_command.NonLocalGenericsModule); + if (nonLocalGenericsModule == "*") + { + groupConfig.CompileAllPossibleCrossModuleCode = true; + nonLocalGenericsHome = inputModules[0]; + } + else if (nonLocalGenericsModule == "") + { + // Nothing was specified + } + else { - foreach (var module in _referenceableModules) + bool matchFound = false; + + // Allow module to be specified by assembly name or by filename + if (nonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + nonLocalGenericsModule = Path.GetFileNameWithoutExtension(nonLocalGenericsModule); + foreach (var module in inputModules) { if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) { matchFound = true; + nonLocalGenericsHome = module; + groupConfig.CompileAllPossibleCrossModuleCode = true; break; } } if (!matchFound) { - throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, nonLocalGenericsModule)); + foreach (var module in _referenceableModules) + { + if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + { + matchFound = true; + break; + } + } + + if (!matchFound) + { + throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, nonLocalGenericsModule)); + } } } - } - - bool compileNoMethods = Get(_command.CompileNoMethods); - if (singleMethod != null) - { - // Compiling just a single method - compilationGroup = new SingleMethodCompilationModuleGroup( - groupConfig, - singleMethod); - compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); - } - else if (compileNoMethods) - { - compilationGroup = new NoMethodsCompilationModuleGroup(groupConfig); - } - else - { - // Single assembly compilation. - compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(groupConfig); - } - // Load any profiles generated by method call chain analyis - CallChainProfile jsonProfile = null; - string callChainProfileFile = Get(_command.CallChainProfileFile); - if (!string.IsNullOrEmpty(callChainProfileFile)) - { - jsonProfile = new CallChainProfile(callChainProfileFile, typeSystemContext, _referenceableModules); - } + bool compileNoMethods = Get(_command.CompileNoMethods); + if (singleMethod != null) + { + // Compiling just a single method + compilationGroup = new SingleMethodCompilationModuleGroup( + groupConfig, + singleMethod); + compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); + } + else if (compileNoMethods) + { + compilationGroup = new NoMethodsCompilationModuleGroup(groupConfig); + } + else + { + // Single assembly compilation. + compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(groupConfig); + } - // Examine profile guided information as appropriate - MIbcProfileParser.MibcGroupParseRules parseRule; - if (nonLocalGenericsHome != null) - { - parseRule = MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule2; - } - else - { - parseRule = MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule1; - } + // Load any profiles generated by method call chain analyis + CallChainProfile jsonProfile = null; + string callChainProfileFile = Get(_command.CallChainProfileFile); + if (!string.IsNullOrEmpty(callChainProfileFile)) + { + jsonProfile = new CallChainProfile(callChainProfileFile, typeSystemContext, _referenceableModules); + } - ProfileDataManager profileDataManager = - new ProfileDataManager(logger, - _referenceableModules, - inputModules, - versionBubbleModules, - crossModuleInlineableCode, - nonLocalGenericsHome, - mibcFiles, - parseRule, - jsonProfile, - typeSystemContext, - compilationGroup, - Get(_command.EmbedPgoData), - crossModuleInlineableCode.Count == 0 ? compilationGroup.VersionsWithMethodBody : compilationGroup.CrossModuleInlineable); - - bool partial = Get(_command.Partial); - compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, partial); - - if ((singleMethod == null) && !compileNoMethods) - { - // For normal compilations add compilation roots. - foreach (var module in rootingModules) + // Examine profile guided information as appropriate + MIbcProfileParser.MibcGroupParseRules parseRule; + if (nonLocalGenericsHome != null) + { + parseRule = MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule2; + } + else { - compilationRoots.Add(new ReadyToRunRootProvider( - module, - profileDataManager, - profileDrivenPartialNGen: partial)); + parseRule = MIbcProfileParser.MibcGroupParseRules.VersionBubbleWithCrossModule1; + } - if (!_command.CompositeOrInputBubble) + ProfileDataManager profileDataManager = + new ProfileDataManager(logger, + _referenceableModules, + inputModules, + versionBubbleModules, + crossModuleInlineableCode, + nonLocalGenericsHome, + mibcFiles, + parseRule, + jsonProfile, + typeSystemContext, + compilationGroup, + Get(_command.EmbedPgoData), + crossModuleInlineableCode.Count == 0 ? compilationGroup.VersionsWithMethodBody : compilationGroup.CrossModuleInlineable); + + bool partial = Get(_command.Partial); + compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, partial); + + if ((singleMethod == null) && !compileNoMethods) + { + // For normal compilations add compilation roots. + foreach (var module in rootingModules) { - break; + compilationRoots.Add(new ReadyToRunRootProvider( + module, + profileDataManager, + profileDrivenPartialNGen: partial)); + + if (!_command.CompositeOrInputBubble) + { + break; + } } } - } - // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize - // or produce debuggable code if an explicit optimization level was not specified on the command line - OptimizationMode optimizationMode = _command.OptimizationMode; - if (optimizationMode == OptimizationMode.None && !Get(_command.OptimizeDisabled) && !composite) - { - System.Diagnostics.Debug.Assert(inputModules.Count == 1); - optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended; - } - - CompositeImageSettings compositeImageSettings = new CompositeImageSettings(); - string compositeKeyFile = Get(_command.CompositeKeyFile); - if (compositeKeyFile != null) - { - byte[] compositeStrongNameKey = File.ReadAllBytes(compositeKeyFile); - if (!IsValidPublicKey(compositeStrongNameKey)) + // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize + // or produce debuggable code if an explicit optimization level was not specified on the command line + OptimizationMode optimizationMode = _command.OptimizationMode; + if (optimizationMode == OptimizationMode.None && !Get(_command.OptimizeDisabled) && !composite) { - throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey)); + System.Diagnostics.Debug.Assert(inputModules.Count == 1); + optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended; } - compositeImageSettings.PublicKey = compositeStrongNameKey.ToImmutableArray(); - } - - // - // Compile - // + CompositeImageSettings compositeImageSettings = new CompositeImageSettings(); + string compositeKeyFile = Get(_command.CompositeKeyFile); + if (compositeKeyFile != null) + { + byte[] compositeStrongNameKey = File.ReadAllBytes(compositeKeyFile); + if (!IsValidPublicKey(compositeStrongNameKey)) + { + throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey)); + } - ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder( - typeSystemContext, compilationGroup, _allInputFilePaths.Values, compositeRootPath); - string compilationUnitPrefix = ""; - builder.UseCompilationUnitPrefix(compilationUnitPrefix); - - ILProvider ilProvider = new ReadyToRunILProvider(compilationGroup); - - DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? - DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); - - NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); - nodeFactoryFlags.OptimizeAsyncMethods = Get(_command.AsyncMethodOptimization); - - builder - .UseMapFile(Get(_command.Map)) - .UseMapCsvFile(Get(_command.MapCsv)) - .UsePdbFile(Get(_command.Pdb), Get(_command.PdbPath)) - .UsePerfMapFile(Get(_command.PerfMap), Get(_command.PerfMapPath), Get(_command.PerfMapFormatVersion)) - .UseProfileFile(jsonProfile != null) - .UseProfileData(profileDataManager) - .UseNodeFactoryOptimizationFlags(nodeFactoryFlags) - .FileLayoutAlgorithms(Get(_command.MethodLayout), Get(_command.FileLayout)) - .UseCompositeImageSettings(compositeImageSettings) - .UseJitPath(Get(_command.JitPath)) - .UseInstructionSetSupport(instructionSetSupport) - .UseCustomPESectionAlignment(Get(_command.CustomPESectionAlignment)) - .UseVerifyTypeAndFieldLayout(Get(_command.VerifyTypeAndFieldLayout)) - .GenerateOutputFile(outFile) - .UseImageBase(_imageBase) - .UseILProvider(ilProvider) - .UseBackendOptions(Get(_command.CodegenOptions)) - .UseLogger(logger) - .UseParallelism(Get(_command.Parallelism)) - .UseResilience(Get(_command.Resilient)) - .UseDependencyTracking(trackingLevel) - .UseCompilationRoots(compilationRoots) - .UseOptimizationMode(optimizationMode); - - if (Get(_command.PrintReproInstructions)) - builder.UsePrintReproInstructions(CreateReproArgumentString); - - compilation = builder.ToCompilation(); + compositeImageSettings.PublicKey = compositeStrongNameKey.ToImmutableArray(); + } + // + // Compile + // + + ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder( + typeSystemContext, compilationGroup, _allInputFilePaths.Values, compositeRootPath); + string compilationUnitPrefix = ""; + builder.UseCompilationUnitPrefix(compilationUnitPrefix); + + ILProvider ilProvider = new ReadyToRunILProvider(compilationGroup); + + DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? + DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + + NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); + nodeFactoryFlags.OptimizeAsyncMethods = Get(_command.AsyncMethodOptimization); + + builder + .UseMapFile(Get(_command.Map)) + .UseMapCsvFile(Get(_command.MapCsv)) + .UsePdbFile(Get(_command.Pdb), Get(_command.PdbPath)) + .UsePerfMapFile(Get(_command.PerfMap), Get(_command.PerfMapPath), Get(_command.PerfMapFormatVersion)) + .UseProfileFile(jsonProfile != null) + .UseProfileData(profileDataManager) + .UseNodeFactoryOptimizationFlags(nodeFactoryFlags) + .FileLayoutAlgorithms(Get(_command.MethodLayout), Get(_command.FileLayout)) + .UseCompositeImageSettings(compositeImageSettings) + .UseJitPath(Get(_command.JitPath)) + .UseInstructionSetSupport(instructionSetSupport) + .UseCustomPESectionAlignment(Get(_command.CustomPESectionAlignment)) + .UseVerifyTypeAndFieldLayout(Get(_command.VerifyTypeAndFieldLayout)) + .GenerateOutputFile(outFile) + .UseImageBase(_imageBase) + .UseILProvider(ilProvider) + .UseBackendOptions(Get(_command.CodegenOptions)) + .UseLogger(logger) + .UseParallelism(Get(_command.Parallelism)) + .UseResilience(Get(_command.Resilient)) + .UseDependencyTracking(trackingLevel) + .UseCompilationRoots(compilationRoots) + .UseOptimizationMode(optimizationMode); + + if (Get(_command.PrintReproInstructions)) + builder.UsePrintReproInstructions(CreateReproArgumentString); + + compilation = builder.ToCompilation(); + + } + compilation.Compile(outFile); + + if (dgmlLogFileName != null) + compilation.WriteDependencyLog(dgmlLogFileName); + + compilation.Dispose(); } - compilation.Compile(outFile); - - if (dgmlLogFileName != null) - compilation.WriteDependencyLog(dgmlLogFileName); - - compilation.Dispose(); } - } - private void CheckManagedCppInputFiles(IEnumerable inputPaths) - { - foreach (string inputFilePath in inputPaths) + private void CheckManagedCppInputFiles(IEnumerable inputPaths) { - EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFilePath); - if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0) + foreach (string inputFilePath in inputPaths) { - throw new CommandLineException(string.Format(SR.ManagedCppNotSupported, inputFilePath)); + EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFilePath); + if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0) + { + throw new CommandLineException(string.Format(SR.ManagedCppNotSupported, inputFilePath)); + } } } - } - - private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) - { - ModuleDesc systemModule = context.SystemModule; - TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => + private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) { - return (MetadataType)context.GetCanonType(typeDefName) - ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); - }); - if (foundType == null) - throw new CommandLineException(string.Format(SR.TypeNotFound, typeName)); + ModuleDesc systemModule = context.SystemModule; - return foundType; - } + TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => + { + return (MetadataType)context.GetCanonType(typeDefName) + ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); + }); + if (foundType == null) + throw new CommandLineException(string.Format(SR.TypeNotFound, typeName)); - private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) - { - string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); - string singleMethodName = Get(_command.SingleMethodName); - string singleMethodTypeName = Get(_command.SingleMethodTypeName); - if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) - return null; - - if (singleMethodName == null || singleMethodTypeName == null) - throw new CommandLineException(SR.TypeAndMethodNameNeeded); - - TypeDesc owningType = FindType(context, singleMethodTypeName); - - // TODO: allow specifying signature to distinguish overloads - MethodDesc method = null; - bool printMethodList = false; - int curIndex = 0; - foreach (var searchMethod in owningType.GetMethods()) + return foundType; + } + + private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) { - if (searchMethod.Name != singleMethodName) - continue; + string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); + string singleMethodName = Get(_command.SingleMethodName); + string singleMethodTypeName = Get(_command.SingleMethodTypeName); + if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) + return null; - curIndex++; - if (Get(_command.SingleMethodIndex) != 0) - { - if (curIndex == Get(_command.SingleMethodIndex)) - { - method = searchMethod; - break; - } - } - else + if (singleMethodName == null || singleMethodTypeName == null) + throw new CommandLineException(SR.TypeAndMethodNameNeeded); + + TypeDesc owningType = FindType(context, singleMethodTypeName); + + // TODO: allow specifying signature to distinguish overloads + MethodDesc method = null; + bool printMethodList = false; + int curIndex = 0; + foreach (var searchMethod in owningType.GetMethods()) { - if (method == null) + if (searchMethod.Name != singleMethodName) + continue; + + curIndex++; + if (Get(_command.SingleMethodIndex) != 0) { - method = searchMethod; + if (curIndex == Get(_command.SingleMethodIndex)) + { + method = searchMethod; + break; + } } else { - printMethodList = true; + if (method == null) + { + method = searchMethod; + } + else + { + printMethodList = true; + } } } - } - if (printMethodList) - { - curIndex = 0; - foreach (var searchMethod in owningType.GetMethods()) + if (printMethodList) { - if (searchMethod.Name != singleMethodName) - continue; + curIndex = 0; + foreach (var searchMethod in owningType.GetMethods()) + { + if (searchMethod.Name != singleMethodName) + continue; - curIndex++; - Console.WriteLine($"{curIndex} - {searchMethod}"); + curIndex++; + Console.WriteLine($"{curIndex} - {searchMethod}"); + } + throw new CommandLineException(SR.SingleMethodIndexNeeded); } - throw new CommandLineException(SR.SingleMethodIndexNeeded); - } - if (method == null) - throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, singleMethodName, singleMethodTypeName)); + if (method == null) + throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, singleMethodName, singleMethodTypeName)); - if (method.HasInstantiation != (singleMethodGenericArgs != null) || - (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) - { - throw new CommandLineException( - string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, singleMethodName, singleMethodTypeName)); - } + if (method.HasInstantiation != (singleMethodGenericArgs != null) || + (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) + { + throw new CommandLineException( + string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, singleMethodName, singleMethodTypeName)); + } - if (method.HasInstantiation) - { - List genericArguments = new List(); - foreach (var argString in singleMethodGenericArgs) - genericArguments.Add(FindType(context, argString)); - method = method.MakeInstantiatedMethod(genericArguments.ToArray()); - } + if (method.HasInstantiation) + { + List genericArguments = new List(); + foreach (var argString in singleMethodGenericArgs) + genericArguments.Add(FindType(context, argString)); + method = method.MakeInstantiatedMethod(genericArguments.ToArray()); + } - return method; - } + return method; + } - internal static string CreateReproArgumentString(MethodDesc method) - { - StringBuilder sb = new StringBuilder(); + internal static string CreateReproArgumentString(MethodDesc method) + { + StringBuilder sb = new StringBuilder(); - var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)method.Context.SystemModule); + var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)method.Context.SystemModule); - sb.Append($"--singlemethodtypename \"{formatter.FormatName(method.OwningType, true)}\""); - sb.Append($" --singlemethodname \"{method.Name}\""); - { - int curIndex = 0; - foreach (var searchMethod in method.OwningType.GetMethods()) + sb.Append($"--singlemethodtypename \"{formatter.FormatName(method.OwningType, true)}\""); + sb.Append($" --singlemethodname \"{method.Name}\""); { - if (searchMethod.Name != method.Name) - continue; - - curIndex++; - if (searchMethod == method.GetMethodDefinition()) + int curIndex = 0; + foreach (var searchMethod in method.OwningType.GetMethods()) { - sb.Append($" --singlemethodindex {curIndex}"); + if (searchMethod.Name != method.Name) + continue; + + curIndex++; + if (searchMethod == method.GetMethodDefinition()) + { + sb.Append($" --singlemethodindex {curIndex}"); + } } } - } - - for (int i = 0; i < method.Instantiation.Length; i++) - sb.Append($" --singlemethodgenericarg \"{formatter.FormatName(method.Instantiation[i], true)}\""); - return sb.ToString(); - } + for (int i = 0; i < method.Instantiation.Length; i++) + sb.Append($" --singlemethodgenericarg \"{formatter.FormatName(method.Instantiation[i], true)}\""); - private static bool DumpReproArguments(CodeGenerationFailedException ex) - { - Console.WriteLine(SR.DumpReproInstructions); - - MethodDesc failingMethod = ex.Method; - Console.WriteLine(CreateReproArgumentString(failingMethod)); - return false; - } - - private enum AlgorithmClass - { - Signature = 1, - Hash = 4, - } - - private enum AlgorithmSubId - { - Sha1Hash = 4, - MacHash = 5, - RipeMdHash = 6, - RipeMd160Hash = 7, - Ssl3ShaMD5Hash = 8, - HmacHash = 9, - Tls1PrfHash = 10, - HashReplacOwfHash = 11, - Sha256Hash = 12, - Sha384Hash = 13, - Sha512Hash = 14, - } - - private struct AlgorithmId - { - // From wincrypt.h - private const int AlgorithmClassOffset = 13; - private const int AlgorithmClassMask = 0x7; - private const int AlgorithmSubIdOffset = 0; - private const int AlgorithmSubIdMask = 0x1ff; - - private readonly uint _flags; - - public const int RsaSign = 0x00002400; - public const int Sha = 0x00008004; + return sb.ToString(); + } - public bool IsSet + private static bool DumpReproArguments(CodeGenerationFailedException ex) { - get { return _flags != 0; } + Console.WriteLine(SR.DumpReproInstructions); + + MethodDesc failingMethod = ex.Method; + Console.WriteLine(CreateReproArgumentString(failingMethod)); + return false; } - public AlgorithmClass Class + private enum AlgorithmClass { - get { return (AlgorithmClass)((_flags >> AlgorithmClassOffset) & AlgorithmClassMask); } + Signature = 1, + Hash = 4, } - public AlgorithmSubId SubId + private enum AlgorithmSubId { - get { return (AlgorithmSubId)((_flags >> AlgorithmSubIdOffset) & AlgorithmSubIdMask); } + Sha1Hash = 4, + MacHash = 5, + RipeMdHash = 6, + RipeMd160Hash = 7, + Ssl3ShaMD5Hash = 8, + HmacHash = 9, + Tls1PrfHash = 10, + HashReplacOwfHash = 11, + Sha256Hash = 12, + Sha384Hash = 13, + Sha512Hash = 14, } - public AlgorithmId(uint flags) + private struct AlgorithmId { - _flags = flags; - } - } + // From wincrypt.h + private const int AlgorithmClassOffset = 13; + private const int AlgorithmClassMask = 0x7; + private const int AlgorithmSubIdOffset = 0; + private const int AlgorithmSubIdMask = 0x1ff; - private static ReadOnlySpan s_ecmaKey => new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 }; + private readonly uint _flags; - private const int SnPublicKeyBlobSize = 13; + public const int RsaSign = 0x00002400; + public const int Sha = 0x00008004; - // From wincrypt.h - private const byte PublicKeyBlobId = 0x06; - private const byte PrivateKeyBlobId = 0x07; + public bool IsSet + { + get { return _flags != 0; } + } - // internal for testing - internal const int s_publicKeyHeaderSize = SnPublicKeyBlobSize - 1; + public AlgorithmClass Class + { + get { return (AlgorithmClass)((_flags >> AlgorithmClassOffset) & AlgorithmClassMask); } + } - // From StrongNameInternal.cpp - // Checks to see if a public key is a valid instance of a PublicKeyBlob as - // defined in StongName.h - internal static bool IsValidPublicKey(byte[] blob) - { - // The number of public key bytes must be at least large enough for the header and one byte of data. - if (blob.Length < s_publicKeyHeaderSize + 1) - { - return false; - } + public AlgorithmSubId SubId + { + get { return (AlgorithmSubId)((_flags >> AlgorithmSubIdOffset) & AlgorithmSubIdMask); } + } - // Check for the ECMA key, which does not obey the invariants checked below. - if (blob.AsSpan().SequenceEqual(s_ecmaKey)) - { - return true; + public AlgorithmId(uint flags) + { + _flags = flags; + } } - var blobReader = new BinaryReader(new MemoryStream(blob, writable: false)); + private static ReadOnlySpan s_ecmaKey => new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 }; - // Signature algorithm ID - var sigAlgId = blobReader.ReadUInt32(); - // Hash algorithm ID - var hashAlgId = blobReader.ReadUInt32(); - // Size of public key data in bytes, not including the header - var publicKeySize = blobReader.ReadUInt32(); - // publicKeySize bytes of public key data - var publicKey = blobReader.ReadByte(); + private const int SnPublicKeyBlobSize = 13; - // The number of public key bytes must be the same as the size of the header plus the size of the public key data. - if (blob.Length != s_publicKeyHeaderSize + publicKeySize) - { - return false; - } + // From wincrypt.h + private const byte PublicKeyBlobId = 0x06; + private const byte PrivateKeyBlobId = 0x07; - // The public key must be in the wincrypto PUBLICKEYBLOB format - if (publicKey != PublicKeyBlobId) - { - return false; - } + // internal for testing + internal const int s_publicKeyHeaderSize = SnPublicKeyBlobSize - 1; - var signatureAlgorithmId = new AlgorithmId(sigAlgId); - if (signatureAlgorithmId.IsSet && signatureAlgorithmId.Class != AlgorithmClass.Signature) + // From StrongNameInternal.cpp + // Checks to see if a public key is a valid instance of a PublicKeyBlob as + // defined in StongName.h + internal static bool IsValidPublicKey(byte[] blob) { - return false; - } + // The number of public key bytes must be at least large enough for the header and one byte of data. + if (blob.Length < s_publicKeyHeaderSize + 1) + { + return false; + } - var hashAlgorithmId = new AlgorithmId(hashAlgId); - if (hashAlgorithmId.IsSet && (hashAlgorithmId.Class != AlgorithmClass.Hash || hashAlgorithmId.SubId < AlgorithmSubId.Sha1Hash)) - { - return false; - } + // Check for the ECMA key, which does not obey the invariants checked below. + if (blob.AsSpan().SequenceEqual(s_ecmaKey)) + { + return true; + } - return true; - } + var blobReader = new BinaryReader(new MemoryStream(blob, writable: false)); + + // Signature algorithm ID + var sigAlgId = blobReader.ReadUInt32(); + // Hash algorithm ID + var hashAlgId = blobReader.ReadUInt32(); + // Size of public key data in bytes, not including the header + var publicKeySize = blobReader.ReadUInt32(); + // publicKeySize bytes of public key data + var publicKey = blobReader.ReadByte(); + + // The number of public key bytes must be the same as the size of the header plus the size of the public key data. + if (blob.Length != s_publicKeyHeaderSize + publicKeySize) + { + return false; + } + + // The public key must be in the wincrypto PUBLICKEYBLOB format + if (publicKey != PublicKeyBlobId) + { + return false; + } + + var signatureAlgorithmId = new AlgorithmId(sigAlgId); + if (signatureAlgorithmId.IsSet && signatureAlgorithmId.Class != AlgorithmClass.Signature) + { + return false; + } + + var hashAlgorithmId = new AlgorithmId(hashAlgId); + if (hashAlgorithmId.IsSet && (hashAlgorithmId.Class != AlgorithmClass.Hash || hashAlgorithmId.SubId < AlgorithmSubId.Sha1Hash)) + { + return false; + } + + return true; + } - private T Get(Option option) => _command.Result.GetValueForOption(option); + private T Get(Option option) => _command.Result.GetValueForOption(option); - private static int Main(string[] args) => - new CommandLineBuilder(new Crossgen2RootCommand(args)) - .UseVersionOption() - .UseHelp(context => context.HelpBuilder.CustomizeLayout(Crossgen2RootCommand.GetExtendedHelp)) - .UseParseErrorReporting() - .Build() - .Invoke(args); + private static int Main(string[] args) => + new CommandLineBuilder(new Crossgen2RootCommand(args)) + .UseVersionOption() + .UseHelp(context => context.HelpBuilder.CustomizeLayout(Crossgen2RootCommand.GetExtendedHelp)) + .UseParseErrorReporting() + .Build() + .Invoke(args); + } } From 3a242d2715627e36766f5d3cd172c8107a9bd1b7 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Thu, 14 Jul 2022 01:50:14 +0300 Subject: [PATCH 09/12] ilc: rename --instructionset to --instruction-set --- .../nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets | 2 +- src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs | 2 +- .../nativeaot/SmokeTests/HardwareIntrinsics/x64NonVex.csproj | 2 +- src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64Vex.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 1c99b64ea4296..2b60f41a3c917 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -232,7 +232,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 9862afac8532e..0eaae1ee3fb7c 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -92,7 +92,7 @@ internal class ILCompilerRootCommand : RootCommand public Option Parallelism { get; } = new(new[] { "--parallelism" }, () => Environment.ProcessorCount, "Maximum number of threads to use during compilation"); public Option InstructionSet { get; } = - new(new[] { "--instructionset" }, "Instruction set to allow or disallow"); + new(new[] { "--instruction-set" }, "Instruction set to allow or disallow"); public Option Guard { get; } = new(new[] { "--guard" }, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); public Option PreinitStatics { get; } = diff --git a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64NonVex.csproj b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64NonVex.csproj index 9c6dfc29a3b91..5e8d35d67bf3d 100644 --- a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64NonVex.csproj +++ b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64NonVex.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64Vex.csproj b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64Vex.csproj index 8ef7fb3a0b7b2..b14f7c618632c 100644 --- a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64Vex.csproj +++ b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/x64Vex.csproj @@ -9,7 +9,7 @@ - + From bf4bd513e4f68c05279af677cea4e5b48678c498 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Thu, 14 Jul 2022 08:09:16 +0300 Subject: [PATCH 10/12] Remove 'Microsoft (R)' from tools' banner --- src/coreclr/ilasm/main.cpp | 4 ++-- src/coreclr/ildasm/dasm.cpp | 2 +- src/coreclr/ildasm/windasm.cpp | 2 +- src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs | 2 +- src/coreclr/tools/aot/crossgen2/Properties/Resources.resx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/ilasm/main.cpp b/src/coreclr/ilasm/main.cpp index d6d6e9201b1bf..4e4b789107587 100644 --- a/src/coreclr/ilasm/main.cpp +++ b/src/coreclr/ilasm/main.cpp @@ -147,7 +147,7 @@ extern "C" int _cdecl wmain(int argc, _In_ WCHAR **argv) #pragma warning(pop) #endif { - printf("\nMicrosoft (R) .NET IL Assembler version " CLR_PRODUCT_VERSION); + printf("\n.NET IL Assembler version " CLR_PRODUCT_VERSION); printf("\n%S\n\n", VER_LEGALCOPYRIGHT_LOGO_STR_L); goto PrintUsageAndExit; @@ -630,7 +630,7 @@ extern "C" int _cdecl wmain(int argc, _In_ WCHAR **argv) //====================================================================== if(bLogo) { - printf("\nMicrosoft (R) .NET IL Assembler. Version " CLR_PRODUCT_VERSION); + printf("\n.NET IL Assembler. Version " CLR_PRODUCT_VERSION); printf("\n%S", VER_LEGALCOPYRIGHT_LOGO_STR_L); } diff --git a/src/coreclr/ildasm/dasm.cpp b/src/coreclr/ildasm/dasm.cpp index 9942397f9da6d..cca36fdbcf0ae 100644 --- a/src/coreclr/ildasm/dasm.cpp +++ b/src/coreclr/ildasm/dasm.cpp @@ -7029,7 +7029,7 @@ void DumpPreamble() else if(g_fDumpRTF) { } - sprintf_s(szString,SZSTRING_SIZE,"// Microsoft (R) .NET IL Disassembler. Version " CLR_PRODUCT_VERSION); + sprintf_s(szString,SZSTRING_SIZE,"// .NET IL Disassembler. Version " CLR_PRODUCT_VERSION); printLine(g_pFile,COMMENT(szString)); if(g_fDumpHTML) { diff --git a/src/coreclr/ildasm/windasm.cpp b/src/coreclr/ildasm/windasm.cpp index bf80bad26b6cf..490abb53775c8 100644 --- a/src/coreclr/ildasm/windasm.cpp +++ b/src/coreclr/ildasm/windasm.cpp @@ -93,7 +93,7 @@ FILE* OpenOutput(_In_ __nullterminated const char* szFileName); void PrintLogo() { - printf("Microsoft (R) .NET IL Disassembler. Version " CLR_PRODUCT_VERSION); + printf(".NET IL Disassembler. Version " CLR_PRODUCT_VERSION); printf("\n%S\n\n", VER_LEGALCOPYRIGHT_LOGO_STR_L); } diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 0eaae1ee3fb7c..27b3b2cf2e121 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -139,7 +139,7 @@ internal class ILCompilerRootCommand : RootCommand public OptimizationMode OptimizationMode { get; private set; } public ParseResult Result; - public ILCompilerRootCommand(string[] args) : base("Microsoft (R) .NET Native IL Compiler") + public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") { AddArgument(InputFilePaths); AddOption(ReferenceFiles); diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 9317b7c2d6c8d..2b0eab2a9ba11 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -391,6 +391,6 @@ The following CPU names are predefined groups of instruction sets and can be used in --instruction-set too: - Microsoft (R) .NET Crossgen2 Compiler + .NET Crossgen2 Compiler From 75044e7f7f996218cf9b6291da9745c1b9d718d6 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sat, 16 Jul 2022 03:37:13 +0300 Subject: [PATCH 11/12] Merge conflict typo fix --- src/coreclr/tools/Common/CommandLineHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs index 4637a74168d8e..7129e2b73fa4e 100644 --- a/src/coreclr/tools/Common/CommandLineHelpers.cs +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -85,7 +85,7 @@ public static TargetArchitecture GetTargetArchitecture(string token) return TargetArchitecture.ARM; else if (token.Equals("arm64", StringComparison.OrdinalIgnoreCase)) return TargetArchitecture.ARM64; - else if (archArg.Equals("loongarch64", StringComparison.OrdinalIgnoreCase)) + else if (token.Equals("loongarch64", StringComparison.OrdinalIgnoreCase)) return TargetArchitecture.LoongArch64; throw new CommandLineException($"Target architecture '{token}' is not supported"); From 6b90827de1ad50e806bf96699e9d82389b9e484f Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:31:47 +0300 Subject: [PATCH 12/12] Add -v alias for version (to match old behavior) --- src/coreclr/tools/aot/ILCompiler/Program.cs | 2 +- src/coreclr/tools/aot/crossgen2/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index e8d0575051314..1d487683c0b5a 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -738,7 +738,7 @@ private static IEnumerable ProcessWarningCodes(IEnumerable warningC private static int Main(string[] args) => new CommandLineBuilder(new ILCompilerRootCommand(args)) - .UseVersionOption() + .UseVersionOption("-v") .UseHelp(context => context.HelpBuilder.CustomizeLayout(ILCompilerRootCommand.GetExtendedHelp)) .UseParseErrorReporting() .Build() diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 7df61ff3f44d0..c2b21c3569038 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -973,7 +973,7 @@ internal static bool IsValidPublicKey(byte[] blob) private static int Main(string[] args) => new CommandLineBuilder(new Crossgen2RootCommand(args)) - .UseVersionOption() + .UseVersionOption("-v") .UseHelp(context => context.HelpBuilder.CustomizeLayout(Crossgen2RootCommand.GetExtendedHelp)) .UseParseErrorReporting() .Build()