diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs new file mode 100644 index 0000000000000..2e8028abfd841 --- /dev/null +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -0,0 +1,291 @@ +// 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 System.Runtime.InteropServices; + +using Internal.TypeSystem; + +namespace System.CommandLine +{ + internal sealed 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; + } + + public static TargetOS GetTargetOS(string token) + { + if(string.IsNullOrEmpty(token)) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return TargetOS.Windows; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + return TargetOS.Linux; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return TargetOS.OSX; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + return TargetOS.FreeBSD; + + throw new NotImplementedException(); + } + + if (token.Equals("windows", StringComparison.OrdinalIgnoreCase)) + return TargetOS.Windows; + else if (token.Equals("linux", StringComparison.OrdinalIgnoreCase)) + return TargetOS.Linux; + else if (token.Equals("osx", StringComparison.OrdinalIgnoreCase)) + return TargetOS.OSX; + + throw new CommandLineException($"Target OS '{token}' is not supported"); + } + + public static TargetArchitecture GetTargetArchitecture(string token) + { + if(string.IsNullOrEmpty(token)) + { + return RuntimeInformation.ProcessArchitecture switch + { + Architecture.X86 => TargetArchitecture.X86, + Architecture.X64 => TargetArchitecture.X64, + Architecture.Arm => TargetArchitecture.ARM, + Architecture.Arm64 => TargetArchitecture.ARM64, + Architecture.LoongArch64 => TargetArchitecture.LoongArch64, + _ => throw new NotImplementedException() + }; + } + + if (token.Equals("x86", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.X86; + else if (token.Equals("x64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.X64; + else if (token.Equals("arm", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.ARM; + else if (token.Equals("arm64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.ARM64; + else if (token.Equals("loongarch64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.LoongArch64; + + 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 = default; + 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.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index 5d7a4ab2eac3c..b382e3babb8da 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -73,7 +73,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider, compilerGeneratedState), 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/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index b194f26594485..f5e9126850d8f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -79,6 +79,7 @@ public UsageBasedMetadataManager( Logger logger, IEnumerable> featureSwitchValues, IEnumerable rootEntireAssembliesModules, + IEnumerable additionalRootedAssemblies, IEnumerable trimmedAssemblies) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy) { @@ -92,6 +93,7 @@ public UsageBasedMetadataManager( FeatureSwitches = 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..9f9346089b2ff --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -0,0 +1,390 @@ +// 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 sealed 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), 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 LinkTrimFilePaths { get; } = + new(new[] { "--descriptor" }, Array.Empty, "ILLinkTrim.Descriptor file(s) for compilation"); + public Option MapFileName { get; } = + new(new[] { "--map" }, "Generate a map file"); + public Option MstatFileName { get; } = + new(new[] { "--mstat" }, "Generate an mstat 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" }, result => + { + if (result.Tokens.Count > 0) + return int.Parse(result.Tokens[0].Value); + + // Limit parallelism to 24 wide at most by default, more parallelism is unlikely to improve compilation speed + // as many portions of the process are single threaded, and is known to use excessive memory. + var parallelism = Math.Min(24, Environment.ProcessorCount); + + // On 32bit platforms restrict it more, as virtual address space is quite limited + if (!Environment.Is64BitProcess) + parallelism = Math.Min(4, parallelism); + + return parallelism; + }, true, "Maximum number of threads to use during compilation"); + public Option InstructionSet { get; } = + 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; } = + 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 NoTrimWarn { get; } = + new(new[] { "--notrimwarn" }, "Disable warnings related to trimming"); + public Option NoAotWarn { get; } = + new(new[] { "--noaotwarn" }, "Disable warnings related to AOT"); + 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(".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(LinkTrimFilePaths); + AddOption(MapFileName); + AddOption(MstatFileName); + 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(NoTrimWarn); + AddOption(NoAotWarn); + 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", "descriptor" }); + } + + context.ExitCode = new Program(this).Run(); + } +#if DEBUG + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } +#else + catch (Exception e) + { + Console.ResetColor(); + Console.ForegroundColor = ConsoleColor.Red; + + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); + + 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 7adb06231ed9a..6d73b489c0b51 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.CompilerServices; @@ -13,8 +16,6 @@ using Internal.TypeSystem; using Internal.TypeSystem.Ecma; -using Internal.CommandLine; - using ILCompiler.Dataflow; using ILLink.Shared; @@ -25,346 +26,17 @@ namespace ILCompiler { internal sealed 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 _mstatFileName; - 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(); - - private IReadOnlyList _rdXmlFilePaths = Array.Empty(); - - private IReadOnlyList _linkTrimFilePaths = Array.Empty(); - - private IReadOnlyList _initAssemblies = Array.Empty(); - - private IReadOnlyList _appContextSwitches = Array.Empty(); - - private IReadOnlyList _runtimeOptions = Array.Empty(); - - private IReadOnlyList _featureSwitches = Array.Empty(); - - private IReadOnlyList _suppressedWarnings = Array.Empty(); - - private IReadOnlyList _directPInvokes = Array.Empty(); - - private IReadOnlyList _directPInvokeLists = Array.Empty(); - - private bool _resilient; + private readonly ILCompilerRootCommand _command; - private IReadOnlyList _rootedAssemblies = Array.Empty(); - private IReadOnlyList _conditionallyRootedAssemblies = Array.Empty(); - private IReadOnlyList _trimmedAssemblies = Array.Empty(); - private bool _rootDefaultAssemblies; - - public IReadOnlyList _mibcFilePaths = Array.Empty(); - - private IReadOnlyList _singleWarnEnabledAssemblies = Array.Empty(); - private IReadOnlyList _singleWarnDisabledAssemblies = Array.Empty(); - private bool _singleWarn; - private bool _noTrimWarn; - private bool _noAotWarn; - - private string _makeReproPath; - - private bool _help; - - private Program() + public Program(ILCompilerRootCommand command) { - } - - private static void Help(string helpText) - { - Console.WriteLine(); - Console.Write(".NET Native IL Compiler"); - Console.Write(" "); - Console.Write(typeof(Program).GetTypeInfo().Assembly.GetName().Version); - Console.WriteLine(); - Console.WriteLine(); - Console.WriteLine(helpText); - } + _command = command; - public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture arch) - { - 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; - 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(); - } - - } - - private void InitializeDefaultOptions() - { - ComputeDefaultOptions(out _targetOS, out _targetArchitecture); - } - - private ArgumentSyntax ParseCommandLine(string[] args) - { - var validReflectionDataOptions = new string[] { "all", "none" }; - - 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.DefineOptionList("descriptor", ref _linkTrimFilePaths, "ILLinkTrim.Descriptor file(s) for compilation"); - syntax.DefineOption("map", ref _mapFileName, "Generate a map file"); - syntax.DefineOption("mstat", ref _mstatFileName, "Generate an mstat 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("instruction-set", 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.DefineOption("notrimwarn", ref _noTrimWarn, "Disable warnings related to trimming"); - syntax.DefineOption("noaotwarn", ref _noAotWarn, "Disable warnings related to AOT"); - 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) - { - 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" }; - - ComputeDefaultOptions(out TargetOS defaultOs, out TargetArchitecture defaultArch); - - extraHelp.Add(string.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'", "--targetos", string.Join("', '", ValidOS), defaultOs.ToString().ToLowerInvariant())); - - 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) - { - StringBuilder archString = new StringBuilder(); - - archString.Append(arch); - archString.Append(": "); - - TargetArchitecture targetArch = 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()); - } - - 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; - } - - if (waitForDebugger) + if (command.Result.GetValueForOption(command.WaitForDebugger)) { Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); Console.ReadLine(); } - - _optimizationMode = OptimizationMode.None; - if (optimizeSpace) - { - if (optimizeTime) - Console.WriteLine("Warning: overriding -Ot with -Os"); - _optimizationMode = OptimizationMode.PreferSize; - } - 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 - - Helpers.MakeReproPackage(_makeReproPath, _outputFilePath, args, argSyntax, new[] { "-r", "-m", "--rdxml", "--directpinvokelist", "--descriptor" }); - } - - if (_reflectionData != null && Array.IndexOf(validReflectionDataOptions, _reflectionData) < 0) - { - Console.WriteLine($"Warning: option '{_reflectionData}' not recognized"); - } - - return argSyntax; } private IReadOnlyCollection CreateInitializerList(CompilerTypeSystemContext context) @@ -373,7 +45,7 @@ private IReadOnlyCollection CreateInitializerList(CompilerTypeSystem // Build a list of assemblies that have an initializer that needs to run before // any user code runs. - foreach (string initAssemblyName in _initAssemblies) + foreach (string initAssemblyName in Get(_command.InitAssemblies)) { ModuleDesc assembly = context.ResolveAssembly(new AssemblyName(initAssemblyName), throwIfNotFound: true); assembliesWithInitializers.Add(assembly); @@ -384,78 +56,35 @@ private IReadOnlyCollection CreateInitializerList(CompilerTypeSystem List initializerList = new List(libraryInitializers.LibraryInitializerMethods); // If there are any AppContext switches the user wishes to enable, generate code that sets them. - if (_appContextSwitches.Count > 0) + string[] appContextSwitches = Get(_command.AppContextSwitches); + if (appContextSwitches.Length > 0) { MethodDesc appContextInitMethod = new Internal.IL.Stubs.StartupCode.AppContextInitializerMethod( - context.GeneratedAssembly.GetGlobalModuleType(), _appContextSwitches); + context.GeneratedAssembly.GetGlobalModuleType(), appContextSwitches); initializerList.Add(appContextInitMethod); } return initializerList; } - private static TargetArchitecture GetTargetArchitectureFromArg(string archArg) + public int Run() { - 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"); - } - - private static TargetOS GetTargetOSFromArg(string osArg) - { - 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"); - } - - private int Run(string[] args) - { - InitializeDefaultOptions(); - - ArgumentSyntax syntax = ParseCommandLine(args); - if (_help) - { - Help(syntax.GetHelpText()); - return 1; - } - - if (_outputFilePath == null) + string outputFilePath = Get(_command.OutputFilePath); + if (outputFilePath == null) throw new CommandLineException("Output filename must be specified (/out )"); - // - // Set target Architecture and OS - // - if (_targetArchitectureStr != null) - { - _targetArchitecture = GetTargetArchitectureFromArg(_targetArchitectureStr); - } - if (_targetOSStr != null) - { - _targetOS = GetTargetOSFromArg(_targetOSStr); - } - - InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); + TargetArchitecture targetArchitecture = Get(_command.TargetArchitecture); + InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); + TargetOS targetOS = Get(_command.TargetOS); // The runtime expects certain baselines that the codegen can assume as well. - if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) + if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) { instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication } - else if (_targetArchitecture == TargetArchitecture.ARM64) + else if (targetArchitecture == TargetArchitecture.ARM64) { - if (_targetOS == TargetOS.OSX) + if (targetOS == TargetOS.OSX) { // For osx-arm64 we know that apple-m1 is a baseline instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); @@ -466,12 +95,13 @@ private int Run(string[] args) } } - if (_instructionSet != null) + string instructionSetArg = Get(_command.InstructionSet); + if (instructionSetArg != null) { - List instructionSetParams = new List(); + var instructionSetParams = new List(); // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = _instructionSet.Split(','); + string[] instructionSetParamsInput = instructionSetArg.Split(','); for (int i = 0; i < instructionSetParamsInput.Length; i++) { string instructionSet = instructionSetParamsInput[i]; @@ -510,10 +140,10 @@ private int Run(string[] args) (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)) + 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 @@ -536,7 +166,7 @@ private int Run(string[] args) optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); } } - else if (_targetArchitecture == TargetArchitecture.ARM64) + else if (targetArchitecture == TargetArchitecture.ARM64) { optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); @@ -552,12 +182,14 @@ private int Run(string[] args) optimisticInstructionSet.Add(supportedInstructionSet); var instructionSetSupport = new InstructionSetSupport(supportedInstructionSet, - unsupportedInstructionSet, - optimisticInstructionSet, - InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), - _targetArchitecture); + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(targetArchitecture), + targetArchitecture); - bool supportsReflection = _reflectionData != "none" && _systemModuleName == DefaultSystemModule; + string systemModuleName = Get(_command.SystemModuleName); + string reflectionData = Get(_command.ReflectionData); + bool supportsReflection = reflectionData != "none" && systemModuleName == Helpers.DefaultSystemModule; // // Initialize type system context @@ -567,9 +199,9 @@ private int Run(string[] args) var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); var targetAbi = TargetAbi.NativeAot; - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, targetAbi, simdVectorLength); + var targetDetails = new TargetDetails(targetArchitecture, targetOS, targetAbi, simdVectorLength); CompilerTypeSystemContext typeSystemContext = - new CompilerTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0, _maxGenericCycle); + 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 @@ -578,10 +210,10 @@ private int Run(string[] args) // See: https://github.com/dotnet/corert/issues/2785 // // When we undo this hack, replace the foreach with - // typeSystemContext.InputFilePaths = _inputFilePaths; + // typeSystemContext.InputFilePaths = _command.Result.GetValueForArgument(inputFilePaths); // Dictionary inputFilePaths = new Dictionary(); - foreach (var inputFile in _inputFilePaths) + foreach (var inputFile in _command.Result.GetValueForArgument(_command.InputFilePaths)) { try { @@ -595,29 +227,30 @@ private int Run(string[] args) } 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.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"); - typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(_systemModuleName)); + 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")) + string guard = Get(_command.Guard); + if (StringComparer.OrdinalIgnoreCase.Equals(guard, "cf")) { - if (_targetOS != TargetOS.Windows) + if (targetOS != TargetOS.Windows) { throw new CommandLineException($"Control flow guard only available on Windows"); } securityMitigationOptions = SecurityMitigationOptions.ControlFlowGuardAnnotations; } - else if (!string.IsNullOrEmpty(_guard)) + else if (!string.IsNullOrEmpty(guard)) { - throw new CommandLineException($"Unrecognized mitigation option '{_guard}'"); + throw new CommandLineException($"Unrecognized mitigation option '{guard}'"); } // @@ -629,6 +262,7 @@ private int Run(string[] args) CompilationModuleGroup compilationGroup; List compilationRoots = new List(); + bool multiFile = Get(_command.MultiFile); if (singleMethod != null) { // Compiling just a single method @@ -657,14 +291,16 @@ private int Run(string[] args) compilationRoots.Add(new ExportedMethodsRootProvider(module)); } + string[] runtimeOptions = Get(_command.RuntimeOptions); if (entrypointModule != null) { compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext))); - compilationRoots.Add(new RuntimeConfigurationRootProvider(_runtimeOptions)); + compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); } - if (_multiFile) + bool nativeLib = Get(_command.NativeLib); + if (multiFile) { List inputModules = new List(); @@ -684,7 +320,7 @@ private int Run(string[] args) } else { - if (entrypointModule == null && !_nativeLib) + if (entrypointModule == null && !nativeLib) throw new Exception("No entrypoint module"); if (!systemModuleIsInputModule) @@ -692,21 +328,21 @@ private int Run(string[] args) compilationGroup = new SingleFileCompilationModuleGroup(); } - if (_nativeLib) + 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 RuntimeConfigurationRootProvider(runtimeOptions)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); } - foreach (var rdXmlFilePath in _rdXmlFilePaths) + foreach (var rdXmlFilePath in Get(_command.RdXmlFilePaths)) { compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath)); } - foreach (var linkTrimFilePath in _linkTrimFilePaths) + foreach (var linkTrimFilePath in Get(_command.LinkTrimFilePaths)) { if (!File.Exists(linkTrimFilePath)) throw new CommandLineException($"'{linkTrimFilePath}' doesn't exist"); @@ -714,23 +350,9 @@ private int Run(string[] args) } } - _conditionallyRootedAssemblies = new List(_conditionallyRootedAssemblies.Select(ILLinkify)); - _trimmedAssemblies = new List(_trimmedAssemblies.Select(ILLinkify)); - - 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; - } - // Root whatever assemblies were specified on the command line - foreach (var rootedAssembly in _rootedAssemblies) + 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. @@ -738,7 +360,7 @@ static string ILLinkify(string rootedAssembly) ? typeSystemContext.GetModuleFromPath(rootedAssembly) : typeSystemContext.GetModuleForSimpleName(rootedAssembly); - // We only root the module type. The rest will fall out because we treat _rootedAssemblies + // 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, @@ -751,20 +373,24 @@ static string ILLinkify(string rootedAssembly) CompilationBuilder builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); - string compilationUnitPrefix = _multiFile ? Path.GetFileNameWithoutExtension(_outputFilePath) : ""; + string compilationUnitPrefix = multiFile ? Path.GetFileNameWithoutExtension(outputFilePath) : ""; builder.UseCompilationUnitPrefix(compilationUnitPrefix); - if (_mibcFilePaths.Count > 0) - ((RyuJitCompilationBuilder)builder).UseProfileData(_mibcFilePaths); - if (!string.IsNullOrEmpty(_jitPath)) - ((RyuJitCompilationBuilder)builder).UseJitPath(_jitPath); + string[] mibcFilePaths = Get(_command.MibcFilePaths); + if (mibcFilePaths.Length > 0) + ((RyuJitCompilationBuilder)builder).UseProfileData(mibcFilePaths); - PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, _directPInvokes, _directPInvokeLists); + 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)); ILProvider ilProvider = new NativeAotILProvider(); List> featureSwitches = new List>(); - foreach (var switchPair in _featureSwitches) + foreach (var switchPair in Get(_command.FeatureSwitches)) { string[] switchAndValue = switchPair.Split('='); if (switchAndValue.Length != 2 @@ -775,15 +401,16 @@ static string ILLinkify(string rootedAssembly) ilProvider = new FeatureSwitchManager(ilProvider, featureSwitches); var suppressedWarningCategories = new List(); - if (_noTrimWarn) + if (Get(_command.NoTrimWarn)) suppressedWarningCategories.Add(MessageSubCategory.TrimAnalysis); - if (_noAotWarn) + if (Get(_command.NoAotWarn)) suppressedWarningCategories.Add(MessageSubCategory.AotAnalysis); - var logger = new Logger(Console.Out, ilProvider, _isVerbose, ProcessWarningCodes(_suppressedWarnings), _singleWarn, _singleWarnEnabledAssemblies, _singleWarnDisabledAssemblies, suppressedWarningCategories); + var logger = new Logger(Console.Out, ilProvider, Get(_command.IsVerbose), ProcessWarningCodes(Get(_command.SuppressedWarnings)), + Get(_command.SingleWarn), Get(_command.SingleWarnEnabledAssemblies), Get(_command.SingleWarnDisabledAssemblies), suppressedWarningCategories); CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState(ilProvider, logger); - var stackTracePolicy = _emitStackTraceData ? + var stackTracePolicy = Get(_command.EmitStackTraceData) ? (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); MetadataBlockingPolicy mdBlockingPolicy; @@ -791,20 +418,19 @@ static string ILLinkify(string rootedAssembly) UsageBasedMetadataGenerationOptions metadataGenerationOptions = default; if (supportsReflection) { - mdBlockingPolicy = _noMetadataBlocking - ? (MetadataBlockingPolicy)new NoMetadataBlockingPolicy() - : new BlockedInternalsBlockingPolicy(typeSystemContext); + mdBlockingPolicy = Get(_command.NoMetadataBlocking) ? + new NoMetadataBlockingPolicy() : new BlockedInternalsBlockingPolicy(typeSystemContext); resBlockingPolicy = new ManifestResourceBlockingPolicy(featureSwitches); metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; - if (_completeTypesMetadata) + if (Get(_command.CompleteTypesMetadata)) metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly; - if (_scanReflection) + if (Get(_command.ScanReflection)) metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning; - if (_reflectionData == "all") + if (reflectionData == "all") metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts; - if (_rootDefaultAssemblies) + if (Get(_command.RootDefaultAssemblies)) metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.RootDefaultAssemblies; } else @@ -822,15 +448,16 @@ static string ILLinkify(string rootedAssembly) typeSystemContext, mdBlockingPolicy, resBlockingPolicy, - _metadataLogFileName, + Get(_command.MetadataLogFileName), stackTracePolicy, invokeThunkGenerationPolicy, flowAnnotations, metadataGenerationOptions, logger, featureSwitches, - _conditionallyRootedAssemblies.Concat(_rootedAssemblies), - _trimmedAssemblies); + Get(_command.ConditionallyRootedAssemblies), + rootedAssemblies, + Get(_command.TrimmedAssemblies)); InteropStateManager interopStateManager = new InteropStateManager(typeSystemContext.GeneratedAssembly); InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger); @@ -839,15 +466,15 @@ static string ILLinkify(string rootedAssembly) // 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); + bool useScanner = Get(_command.UseScanner) || + (_command.OptimizationMode != OptimizationMode.None && !multiFile); - useScanner &= !_noScanner; + useScanner &= !Get(_command.NoScanner); // Enable static data preinitialization in optimized builds. - bool preinitStatics = _preinitStatics || - (_optimizationMode != OptimizationMode.None && !_multiFile); - preinitStatics &= !_noPreinitStatics; + bool preinitStatics = Get(_command.PreinitStatics) || + (_command.OptimizationMode != OptimizationMode.None && !multiFile); + preinitStatics &= !Get(_command.NoPreinitStatics); var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics); builder @@ -859,6 +486,7 @@ static string ILLinkify(string rootedAssembly) List scannerCompiledMethods = null; #endif + int parallelism = Get(_command.Parallelism); if (useScanner) { // Run the scanner in a separate stack frame so that there's no dangling references to @@ -872,12 +500,14 @@ void RunScanner() ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() .UseCompilationRoots(compilationRoots) .UseMetadataManager(metadataManager) - .UseParallelism(_parallelism) + .UseParallelism(parallelism) .UseInteropStubManager(interopStubManager) .UseLogger(logger); - if (_scanDgmlLogFileName != null) - scannerBuilder.UseDependencyTracking(_generateFullScanDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + string scanDgmlLogFileName = Get(_command.ScanDgmlLogFileName); + if (scanDgmlLogFileName != null) + scannerBuilder.UseDependencyTracking(Get(_command.GenerateFullScanDgmlLog) ? + DependencyTrackingLevel.All : DependencyTrackingLevel.First); IILScanner scanner = scannerBuilder.ToILScanner(); @@ -888,8 +518,8 @@ void RunScanner() scannerConstructedTypes = new List(scanResults.ConstructedEETypes); #endif - if (_scanDgmlLogFileName != null) - scanResults.WriteDependencyLog(_scanDgmlLogFileName); + if (scanDgmlLogFileName != null) + scanResults.WriteDependencyLog(scanDgmlLogFileName); metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); @@ -920,47 +550,54 @@ void RunScanner() builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); } - DebugInformationProvider debugInfoProvider = _enableDebugInfo ? - (_ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(_ilDump, new EcmaOnlyDebugInformationProvider())) : + string ilDump = Get(_command.IlDump); + DebugInformationProvider debugInfoProvider = Get(_command.EnableDebugInfo) ? + (ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(ilDump, new EcmaOnlyDebugInformationProvider())) : new NullDebugInformationProvider(); - DependencyTrackingLevel trackingLevel = _dgmlLogFileName == null ? - DependencyTrackingLevel.None : (_generateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + 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(_codegenOptions) - .UseMethodBodyFolding(enable: _methodBodyFolding) - .UseParallelism(_parallelism) + .UseBackendOptions(Get(_command.CodegenOptions)) + .UseMethodBodyFolding(enable: Get(_command.MethodBodyFolding)) + .UseParallelism(parallelism) .UseMetadataManager(metadataManager) .UseInteropStubManager(interopStubManager) .UseLogger(logger) .UseDependencyTracking(trackingLevel) .UseCompilationRoots(compilationRoots) - .UseOptimizationMode(_optimizationMode) + .UseOptimizationMode(_command.OptimizationMode) .UseSecurityMitigationOptions(securityMitigationOptions) .UseDebugInfoProvider(debugInfoProvider) - .UseDwarf5(_useDwarf5); + .UseDwarf5(Get(_command.UseDwarf5)); - builder.UseResilience(_resilient); + builder.UseResilience(Get(_command.Resilient)); ICompilation compilation = builder.ToCompilation(); + string mapFileName = Get(_command.MapFileName); + string mstatFileName = Get(_command.MstatFileName); + List dumpers = new List(); - if (_mapFileName != null) - dumpers.Add(new XmlObjectDumper(_mapFileName)); + if (mapFileName != null) + dumpers.Add(new XmlObjectDumper(mapFileName)); - if (_mstatFileName != null) - dumpers.Add(new MstatObjectDumper(_mstatFileName, typeSystemContext)); + if (mstatFileName != null) + dumpers.Add(new MstatObjectDumper(mstatFileName, typeSystemContext)); - CompilationResults compilationResults = compilation.Compile(_outputFilePath, ObjectDumper.Compose(dumpers)); - if (_exportsFile != null) + CompilationResults compilationResults = compilation.Compile(outputFilePath, ObjectDumper.Compose(dumpers)); + string exportsFile = Get(_command.ExportsFile); + if (exportsFile != null) { - ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, _exportsFile); + ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile); foreach (var compilationRoot in compilationRoots) { if (compilationRoot is ExportedMethodsRootProvider provider) @@ -972,8 +609,8 @@ void RunScanner() typeSystemContext.LogWarnings(logger); - if (_dgmlLogFileName != null) - compilationResults.WriteDependencyLog(_dgmlLogFileName); + if (dgmlLogFileName != null) + compilationResults.WriteDependencyLog(dgmlLogFileName); #if DEBUG if (scannerConstructedTypes != null) @@ -1002,7 +639,7 @@ static bool IsRelatedToInvalidInput(MethodDesc method) // 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) + if (_command.OptimizationMode == OptimizationMode.None) { // Check that methods and types scanned are a subset of methods and types compiled @@ -1070,30 +707,34 @@ private static TypeDesc FindType(CompilerTypeSystemContext context, string typeN private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) { - if (_singleMethodName == null && _singleMethodTypeName == null && _singleMethodGenericArgs == null) + string singleMethodName = Get(_command.SingleMethodName); + string singleMethodTypeName = Get(_command.SingleMethodTypeName); + string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); + + if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) return null; - if (_singleMethodName == null || _singleMethodTypeName == null) + if (singleMethodName == null || singleMethodTypeName == null) throw new CommandLineException("Both method name and type name are required parameters for single method mode"); - TypeDesc owningType = FindType(context, _singleMethodTypeName); + TypeDesc owningType = FindType(context, singleMethodTypeName); // TODO: allow specifying signature to distinguish overloads - MethodDesc method = owningType.GetMethod(_singleMethodName, null); + MethodDesc method = owningType.GetMethod(singleMethodName, null); if (method == null) - throw new CommandLineException($"Method '{_singleMethodName}' not found in '{_singleMethodTypeName}'"); + throw new CommandLineException($"Method '{singleMethodName}' not found in '{singleMethodTypeName}'"); - if (method.HasInstantiation != (_singleMethodGenericArgs != null) || - (method.HasInstantiation && (method.Instantiation.Length != _singleMethodGenericArgs.Count))) + 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}'"); + $"Expected {method.Instantiation.Length} generic arguments for method '{singleMethodName}' on type '{singleMethodTypeName}'"); } if (method.HasInstantiation) { List genericArguments = new List(); - foreach (var argString in _singleMethodGenericArgs) + foreach (var argString in singleMethodGenericArgs) genericArguments.Add(FindType(context, argString)); method = method.MakeInstantiatedMethod(genericArguments.ToArray()); } @@ -1101,23 +742,6 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont return method; } - 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; - } - private static IEnumerable ProcessWarningCodes(IEnumerable warningCodes) { foreach (string value in warningCodes) @@ -1133,29 +757,14 @@ private static IEnumerable ProcessWarningCodes(IEnumerable warningC } } - private static int Main(string[] args) - { -#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("Error: " + e.Message); - Console.Error.WriteLine(e.ToString()); - return 1; - } -#endif - } + private T Get(Option option) => _command.Result.GetValueForOption(option); + + private static int Main(string[] args) => + new CommandLineBuilder(new ILCompilerRootCommand(args)) + .UseVersionOption("-v") + .UseHelp(context => context.HelpBuilder.CustomizeLayout(ILCompilerRootCommand.GetExtendedHelp)) + .UseParseErrorReporting() + .Build() + .Invoke(args); } } 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 94389b716e22f..b86d53165e51c 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs @@ -73,6 +73,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/CommandLineOptions.cs b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs deleted file mode 100644 index d298ae8a2019b..0000000000000 --- a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs +++ /dev/null @@ -1,273 +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 bool SupportIbc; - 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; - // Limit parallelism to 24 wide at most by default, more parallelism is unlikely to improve compilation speed - // as many portions of the process are single threaded, and is known to use excessive memory. - Parallelism = Math.Min(24, Environment.ProcessorCount); - - // On 32bit platforms restrict it more, as virtual address space is quite limited - if (!Environment.Is64BitProcess) - Parallelism = Math.Min(4, Parallelism); - - 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("support-ibc", ref SupportIbc, SR.SupportIbc); - 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..8569f9fbf1cb7 --- /dev/null +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -0,0 +1,376 @@ +// 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), 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 SupportIbc { get; } = + new(new[] { "--support-ibc" }, SR.SupportIbc); + 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 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" }, result => + { + if (result.Tokens.Count > 0) + return int.Parse(result.Tokens[0].Value); + + // Limit parallelism to 24 wide at most by default, more parallelism is unlikely to improve compilation speed + // as many portions of the process are single threaded, and is known to use excessive memory. + var parallelism = Math.Min(24, Environment.ProcessorCount); + + // On 32bit platforms restrict it more, as virtual address space is quite limited + if (!Environment.Is64BitProcess) + parallelism = Math.Min(4, parallelism); + + return parallelism; + }, true, 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 static bool IsArmel { 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(SupportIbc); + 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", "u", "unrooted-input-file-paths", "m", "mibc", "inputbubbleref" }); + } + + context.ExitCode = new Program(this).Run(); + } +#if DEBUG + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } +#else + catch (Exception e) + { + Console.ResetColor(); + Console.ForegroundColor = ConsoleColor.Red; + + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); + + 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 7c07d757d5015..9a407f9eeb0ae 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; @@ -12,7 +14,6 @@ using System.Runtime.InteropServices; using System.Text; -using Internal.CommandLine; using Internal.IL; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -20,224 +21,42 @@ 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"; - - 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); + private readonly Crossgen2RootCommand _command; // Modules and their names after loading - private Dictionary _allInputFilePaths = new Dictionary(); - private List _referenceableModules = new List(); - - private Dictionary _inputbubblereferenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); - - private CompilerTypeSystemContext _typeSystemContext; - private ReadyToRunMethodLayoutAlgorithm _methodLayout; - private ReadyToRunFileLayoutAlgorithm _fileLayout; - - private Program() - { - } - - public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture arch) - { - 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; - else - throw new NotImplementedException(); + private Dictionary _allInputFilePaths = new(); + private List _referenceableModules = new(); - 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; - case Architecture.LoongArch64: - arch = TargetArchitecture.LoongArch64; - break; - default: - throw new NotImplementedException(); - } - - } + private ReadyToRunCompilerContext _typeSystemContext; + private ulong _imageBase; - private void InitializeDefaultOptions() - { - ComputeDefaultOptions(out _targetOS, out _targetArchitecture); - } + 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 void ProcessCommandLine(string[] args) + public Program(Crossgen2RootCommand command) { - PerfEventSource.StartStopEvents.CommandLineProcessingStart(); - _commandLineOptions = new CommandLineOptions(args); - PerfEventSource.StartStopEvents.CommandLineProcessingStop(); + _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 (_commandLineOptions.Help || _commandLineOptions.Version) + if (command.Result.GetValueForOption(command.WaitForDebugger)) { - return; - } - - if (_commandLineOptions.WaitForDebugger) - { - Console.WriteLine(SR.WaitingForDebuggerAttach); + Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); Console.ReadLine(); } - - if (_commandLineOptions.CompileBubbleGenerics) - { - if (!_commandLineOptions.CompositeOrInputBubble) - { - Console.WriteLine(SR.WarningIgnoringBubbleGenerics); - _commandLineOptions.CompileBubbleGenerics = false; - } - } - - _optimizationMode = OptimizationMode.None; - if (_commandLineOptions.OptimizeDisabled) - { - 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); - - 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 - { - "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 - { - "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort, - "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder, - _ => throw new CommandLineException(SR.InvalidFileLayout) - }; - } - - } - - private string GetCompilerVersion() - { - return Assembly - .GetExecutingAssembly() - .GetCustomAttribute() - .InformationalVersion; - } - - 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 if (archArg.Equals("loongarch64", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.LoongArch64; - else - throw new CommandLineException(SR.TargetArchitectureUnsupported); - } - - private void ConfigureTarget() - { - // - // 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); - } } private InstructionSetSupport ConfigureInstructionSetSupport() @@ -262,12 +81,13 @@ private InstructionSetSupport ConfigureInstructionSetSupport() } } - if (_commandLineOptions.InstructionSet != null) + string instructionSetArg = Get(_command.InstructionSet); + if (instructionSetArg != null) { List instructionSetParams = new List(); // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = _commandLineOptions.InstructionSet.Split(","); + string[] instructionSetParamsInput = instructionSetArg.Split(","); for (int i = 0; i < instructionSetParamsInput.Length; i++) { string instructionSet = instructionSetParamsInput[i]; @@ -332,66 +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 void ConfigureImageBase(TargetDetails targetDetails) { bool is64BitTarget = targetDetails.PointerSize == sizeof(long); - if (_commandLineOptions.ImageBase != null) - _imageBase = is64BitTarget ? Convert.ToUInt64(_commandLineOptions.ImageBase, 16) : Convert.ToUInt32(_commandLineOptions.ImageBase, 16); + 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 (_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) + if (_outputFilePath == null && !_outNearInput) throw new CommandLineException(SR.MissingOutputFile); - if (_commandLineOptions.SingleFileCompilation && !_commandLineOptions.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, Crossgen2RootCommand.IsArmel ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); ConfigureImageBase(targetDetails); bool versionBubbleIncludesCoreLib = false; - if (_commandLineOptions.InputBubble) + Dictionary inputFilePathsArg = _command.Result.GetValueForArgument(_command.InputFilePaths); + Dictionary unrootedInputFilePathsArg = Get(_command.UnrootedInputFilePaths); + + if (_inputBubble) { versionBubbleIncludesCoreLib = true; } else { - if (!_commandLineOptions.SingleFileCompilation) + if (!_singleFileCompilation) { - foreach (var inputFile in _inputFilePaths) + foreach (var inputFile in inputFilePathsArg) { if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) { @@ -402,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) { @@ -418,12 +224,13 @@ private int Run(string[] args) // _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib); - string compositeRootPath = _commandLineOptions.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()) { @@ -437,7 +244,7 @@ private int Run(string[] args) // typeSystemContext.InputFilePaths = inFilePaths; // - foreach (var inputFile in _inputFilePaths) + foreach (var inputFile in inputFilePathsArg) { try { @@ -462,7 +269,7 @@ private int Run(string[] args) } } - foreach (var unrootedInputFile in _unrootedInputFilePaths) + foreach (var unrootedInputFile in unrootedInputFilePathsArg) { try { @@ -487,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 (_commandLineOptions.InputFilePaths.Count > 0) + if (inputFilePathsArg.Count > 0) { Console.WriteLine(SR.InputWasNotLoadable); return 2; @@ -499,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 { @@ -508,7 +316,7 @@ private int Run(string[] args) continue; _referenceableModules.Add(module); - if (_commandLineOptions.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 @@ -518,9 +326,9 @@ private int Run(string[] args) catch { } // Ignore non-managed pe files } - if (_commandLineOptions.InputBubble) + if (_inputBubble) { - foreach (var referenceFile in _inputbubblereferenceFilePaths.Values) + foreach (var referenceFile in inputBubbleReferenceFilePaths.Values) { try { @@ -536,11 +344,11 @@ private int Run(string[] args) } } - string systemModuleName = _commandLineOptions.SystemModule ?? DefaultSystemModule; + string systemModuleName = Get(_command.SystemModuleName) ?? Helpers.DefaultSystemModule; _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName)); CompilerTypeSystemContext typeSystemContext = _typeSystemContext; - if (_commandLineOptions.SingleFileCompilation) + if (_singleFileCompilation) { var singleCompilationInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -552,13 +360,13 @@ private int Run(string[] args) singleCompilationInputFilePaths.Add(inputFile.Key, inputFile.Value); typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; - if (!_commandLineOptions.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)); } @@ -567,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 (_commandLineOptions.InputBubble) + if (_inputBubble) { foreach (var inputFile in inputFilePaths) { @@ -591,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, - _commandLineOptions.SingleFileCompilation && _commandLineOptions.InputBubble + _singleFileCompilation&& _inputBubble ? ".ni.dll.tmp" : ".ni.dll"), ".exe" => Path.ChangeExtension(inFilePath, - _commandLineOptions.SingleFileCompilation && _commandLineOptions.InputBubble + _singleFileCompilation && _inputBubble ? ".ni.exe.tmp" : ".ni.exe"), _ => throw new CommandLineException(string.Format(SR.UnsupportedInputFileExtension, inputFileExtension)) }; - string outFile = _commandLineOptions.OutNearInput ? nearOutFilePath : _commandLineOptions.OutputFilePath; + + string outFile = _outNearInput ? nearOutFilePath : _outputFilePath; + string dgmlLogFileName = Get(_command.DgmlLogFileName); using (PerfEventSource.StartStopEvents.CompilationEvents()) { @@ -624,7 +436,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru versionBubbleModulesHash.Add(module); - if (!_commandLineOptions.CompositeOrInputBubble) + if (!_command.CompositeOrInputBubble) { break; } @@ -637,16 +449,17 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru versionBubbleModulesHash.Add(module); } - if (_commandLineOptions.CrossModuleInlining != null) + string[] crossModuleInlining = Get(_command.CrossModuleInlining); + if (crossModuleInlining.Length > 0) { - foreach (var crossModulePgoAssemblyName in _commandLineOptions.CrossModuleInlining) + foreach (var crossModulePgoAssemblyName in crossModuleInlining) { foreach (var module in _referenceableModules) { if (!versionBubbleModulesHash.Contains(module)) { if (crossModulePgoAssemblyName == "*" || - (String.Compare(crossModulePgoAssemblyName, module.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase) == 0)) + (String.Compare(crossModulePgoAssemblyName, module.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase) == 0)) { crossModuleInlineableCode.Add((EcmaModule)module); } @@ -662,44 +475,45 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru // Single method mode? MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); - var logger = new Logger(Console.Out, _commandLineOptions.Verbose); + var logger = new Logger(Console.Out, Get(_command.IsVerbose)); List mibcFiles = new List(); - foreach (var file in _commandLineOptions.MibcFilePaths) + foreach (var file in Get(_command.MibcFilePaths)) { mibcFiles.Add(file); } List versionBubbleModules = new List(versionBubbleModulesHash); - - if (!_commandLineOptions.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 = _commandLineOptions.Composite; - groupConfig.IsInputBubble = _commandLineOptions.InputBubble; + groupConfig.IsCompositeBuildMode = composite; + groupConfig.IsInputBubble = _inputBubble; groupConfig.CompilationModuleSet = inputModules; groupConfig.VersionBubbleModuleSet = versionBubbleModules; - groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = _commandLineOptions.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 = _commandLineOptions.CompileBubbleGenerics ? inputModules[0] : null; - if (_commandLineOptions.NonLocalGenericsModule == "*") + ModuleDesc nonLocalGenericsHome = compileBubbleGenerics ? inputModules[0] : null; + string nonLocalGenericsModule = Get(_command.NonLocalGenericsModule); + if (nonLocalGenericsModule == "*") { groupConfig.CompileAllPossibleCrossModuleCode = true; nonLocalGenericsHome = inputModules[0]; } - else if (_commandLineOptions.NonLocalGenericsModule == "") + else if (nonLocalGenericsModule == "") { // Nothing was specified } @@ -708,11 +522,11 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru 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); + if (nonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + nonLocalGenericsModule = Path.GetFileNameWithoutExtension(nonLocalGenericsModule); foreach (var module in inputModules) { - if (String.Compare(module.Assembly.GetName().Name, _commandLineOptions.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) { matchFound = true; nonLocalGenericsHome = module; @@ -725,7 +539,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru { foreach (var module in _referenceableModules) { - if (String.Compare(module.Assembly.GetName().Name, _commandLineOptions.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) { matchFound = true; break; @@ -734,11 +548,12 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru if (!matchFound) { - throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, _commandLineOptions.NonLocalGenericsModule)); + throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, nonLocalGenericsModule)); } } } + bool compileNoMethods = Get(_command.CompileNoMethods); if (singleMethod != null) { // Compiling just a single method @@ -747,7 +562,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru singleMethod); compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); } - else if (_commandLineOptions.CompileNoMethods) + else if (compileNoMethods) { compilationGroup = new NoMethodsCompilationModuleGroup(groupConfig); } @@ -759,10 +574,10 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru // Load any profiles generated by method call chain analyis CallChainProfile jsonProfile = null; - - if (!string.IsNullOrEmpty(_commandLineOptions.CallChainProfileFile)) + string callChainProfileFile = Get(_command.CallChainProfileFile); + if (!string.IsNullOrEmpty(callChainProfileFile)) { - jsonProfile = new CallChainProfile(_commandLineOptions.CallChainProfileFile, typeSystemContext, _referenceableModules); + jsonProfile = new CallChainProfile(callChainProfileFile, typeSystemContext, _referenceableModules); } // Examine profile guided information as appropriate @@ -788,13 +603,14 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru jsonProfile, typeSystemContext, compilationGroup, - _commandLineOptions.EmbedPgoData, - _commandLineOptions.SupportIbc, + Get(_command.EmbedPgoData), + Get(_command.SupportIbc), crossModuleInlineableCode.Count == 0 ? compilationGroup.VersionsWithMethodBody : compilationGroup.CrossModuleInlineable); - compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, _commandLineOptions.Partial); + bool partial = Get(_command.Partial); + compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, partial); - if ((singleMethod == null) && !_commandLineOptions.CompileNoMethods) + if ((singleMethod == null) && !compileNoMethods) { // For normal compilations add compilation roots. foreach (var module in rootingModules) @@ -802,9 +618,9 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru compilationRoots.Add(new ReadyToRunRootProvider( module, profileDataManager, - profileDrivenPartialNGen: _commandLineOptions.Partial)); + profileDrivenPartialNGen: partial)); - if (!_commandLineOptions.CompositeOrInputBubble) + if (!_command.CompositeOrInputBubble) { break; } @@ -812,18 +628,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 && !_commandLineOptions.OptimizeDisabled && !_commandLineOptions.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 (_commandLineOptions.CompositeKeyFile != null) + string compositeKeyFile = Get(_command.CompositeKeyFile); + if (compositeKeyFile != null) { - byte[] compositeStrongNameKey = File.ReadAllBytes(_commandLineOptions.CompositeKeyFile); + byte[] compositeStrongNameKey = File.ReadAllBytes(compositeKeyFile); if (!IsValidPublicKey(compositeStrongNameKey)) { throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey)); @@ -843,38 +659,38 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru ILProvider ilProvider = new ReadyToRunILProvider(compilationGroup); - DependencyTrackingLevel trackingLevel = _commandLineOptions.DgmlLogFileName == null ? - DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? + DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); - nodeFactoryFlags.OptimizeAsyncMethods = _commandLineOptions.AsyncMethodOptimization; + nodeFactoryFlags.OptimizeAsyncMethods = Get(_command.AsyncMethodOptimization); builder - .UseMapFile(_commandLineOptions.Map) - .UseMapCsvFile(_commandLineOptions.MapCsv) - .UsePdbFile(_commandLineOptions.Pdb, _commandLineOptions.PdbPath) - .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath, _commandLineOptions.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(_commandLineOptions.JitPath) + .UseJitPath(Get(_command.JitPath)) .UseInstructionSetSupport(instructionSetSupport) - .UseCustomPESectionAlignment(_commandLineOptions.CustomPESectionAlignment) - .UseVerifyTypeAndFieldLayout(_commandLineOptions.VerifyTypeAndFieldLayout) + .UseCustomPESectionAlignment(Get(_command.CustomPESectionAlignment)) + .UseVerifyTypeAndFieldLayout(Get(_command.VerifyTypeAndFieldLayout)) .GenerateOutputFile(outFile) .UseImageBase(_imageBase) .UseILProvider(ilProvider) - .UseBackendOptions(_commandLineOptions.CodegenOptions) + .UseBackendOptions(Get(_command.CodegenOptions)) .UseLogger(logger) - .UseParallelism(_commandLineOptions.Parallelism) - .UseResilience(_commandLineOptions.Resilient) + .UseParallelism(Get(_command.Parallelism)) + .UseResilience(Get(_command.Resilient)) .UseDependencyTracking(trackingLevel) .UseCompilationRoots(compilationRoots) .UseOptimizationMode(optimizationMode); - if (_commandLineOptions.PrintReproInstructions) + if (Get(_command.PrintReproInstructions)) builder.UsePrintReproInstructions(CreateReproArgumentString); compilation = builder.ToCompilation(); @@ -882,8 +698,8 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru } compilation.Compile(outFile); - if (_commandLineOptions.DgmlLogFileName != null) - compilation.WriteDependencyLog(_commandLineOptions.DgmlLogFileName); + if (dgmlLogFileName != null) + compilation.WriteDependencyLog(dgmlLogFileName); compilation.Dispose(); } @@ -918,13 +734,16 @@ private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) { - if (_commandLineOptions.SingleMethodName == null && _commandLineOptions.SingleMethodTypeName == null && _commandLineOptions.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 (_commandLineOptions.SingleMethodName == null || _commandLineOptions.SingleMethodTypeName == null) + if (singleMethodName == null || singleMethodTypeName == null) throw new CommandLineException(SR.TypeAndMethodNameNeeded); - TypeDesc owningType = FindType(context, _commandLineOptions.SingleMethodTypeName); + TypeDesc owningType = FindType(context, singleMethodTypeName); // TODO: allow specifying signature to distinguish overloads MethodDesc method = null; @@ -932,13 +751,13 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont int curIndex = 0; foreach (var searchMethod in owningType.GetMethods()) { - if (searchMethod.Name != _commandLineOptions.SingleMethodName) + if (searchMethod.Name != singleMethodName) continue; curIndex++; - if (_commandLineOptions.SingleMethodIndex != 0) + if (Get(_command.SingleMethodIndex) != 0) { - if (curIndex == _commandLineOptions.SingleMethodIndex) + if (curIndex == Get(_command.SingleMethodIndex)) { method = searchMethod; break; @@ -962,7 +781,7 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont curIndex = 0; foreach (var searchMethod in owningType.GetMethods()) { - if (searchMethod.Name != _commandLineOptions.SingleMethodName) + if (searchMethod.Name != singleMethodName) continue; curIndex++; @@ -972,19 +791,19 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont } if (method == null) - throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName)); + throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, singleMethodName, singleMethodTypeName)); - if (method.HasInstantiation != (_commandLineOptions.SingleMethodGenericArg != null) || - (method.HasInstantiation && (method.Instantiation.Length != _commandLineOptions.SingleMethodGenericArg.Count))) + if (method.HasInstantiation != (singleMethodGenericArgs != null) || + (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) { throw new CommandLineException( - string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName)); + string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, singleMethodName, singleMethodTypeName)); } if (method.HasInstantiation) { List genericArguments = new List(); - foreach (var argString in _commandLineOptions.SingleMethodGenericArg) + foreach (var argString in singleMethodGenericArgs) genericArguments.Add(FindType(context, argString)); method = method.MakeInstantiatedMethod(genericArguments.ToArray()); } @@ -992,7 +811,7 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont return method; } - private static string CreateReproArgumentString(MethodDesc method) + internal static string CreateReproArgumentString(MethodDesc method) { StringBuilder sb = new StringBuilder(); @@ -1151,31 +970,14 @@ internal static bool IsValidPublicKey(byte[] blob) return true; } + private T Get(Option option) => _command.Result.GetValueForOption(option); - private static int Main(string[] args) - { -#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 - - } + private static int Main(string[] args) => + new CommandLineBuilder(new Crossgen2RootCommand(args)) + .UseVersionOption("-v") + .UseHelp(context => context.HelpBuilder.CustomizeLayout(Crossgen2RootCommand.GetExtendedHelp)) + .UseParseErrorReporting() + .Build() + .Invoke(args); } } diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index b76655992d1d8..2a3ca76475437 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -393,4 +393,7 @@ Enable processing of Ibc data embedded in the input assembly. - \ No newline at end of file + + .NET Crossgen2 Compiler + + diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.props b/src/coreclr/tools/aot/crossgen2/crossgen2.props index 4a98176cabcbc..dee32ed5ba401 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.props +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.props @@ -28,6 +28,10 @@ + + + + @@ -35,21 +39,7 @@ - - - - - - - - - - - - - - - +