aad491a639d17ae4a37e8cbe60692e0d9ab7fb09
[platform/upstream/dotnet/runtime.git] / src / coreclr / tools / aot / crossgen2 / Program.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 using System;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Reflection.Metadata;
8 using System.Reflection.PortableExecutable;
9 using System.Runtime.InteropServices;
10 using System.Text;
11
12 using Internal.CommandLine;
13 using Internal.IL;
14 using Internal.TypeSystem;
15 using Internal.TypeSystem.Ecma;
16
17 namespace ILCompiler
18 {
19     internal class Program
20     {
21         private const string DefaultSystemModule = "System.Private.CoreLib";
22
23         private CommandLineOptions _commandLineOptions;
24         public TargetOS _targetOS;
25         public TargetArchitecture _targetArchitecture;
26         private bool _armelAbi = false;
27         public OptimizationMode _optimizationMode;
28         private Dictionary<string, string> _inputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
29         private Dictionary<string, string> _unrootedInputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
30         private Dictionary<string, string> _referenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
31         private CompilerTypeSystemContext _typeSystemContext;
32         private ReadyToRunMethodLayoutAlgorithm _methodLayout;
33         private ReadyToRunFileLayoutAlgorithm _fileLayout;
34
35         private Program()
36         {
37         }
38
39         private void InitializeDefaultOptions()
40         {
41             // We could offer this as a command line option, but then we also need to
42             // load a different RyuJIT, so this is a future nice to have...
43             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
44                 _targetOS = TargetOS.Windows;
45             else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
46                 _targetOS = TargetOS.Linux;
47             else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
48                 _targetOS = TargetOS.OSX;
49             else
50                 throw new NotImplementedException();
51
52             switch (RuntimeInformation.ProcessArchitecture)
53             {
54                 case Architecture.X86:
55                     _targetArchitecture = TargetArchitecture.X86;
56                     break;
57                 case Architecture.X64:
58                     _targetArchitecture = TargetArchitecture.X64;
59                     break;
60                 case Architecture.Arm:
61                     _targetArchitecture = TargetArchitecture.ARM;
62                     break;
63                 case Architecture.Arm64:
64                     _targetArchitecture = TargetArchitecture.ARM64;
65                     break;
66                 default:
67                     throw new NotImplementedException();
68             }
69         }
70
71         private void ProcessCommandLine(string[] args)
72         {
73             PerfEventSource.StartStopEvents.CommandLineProcessingStart();
74             _commandLineOptions = new CommandLineOptions(args);
75             PerfEventSource.StartStopEvents.CommandLineProcessingStop();
76
77             if (_commandLineOptions.Help)
78             {
79                 return;
80             }
81
82             if (_commandLineOptions.WaitForDebugger)
83             {
84                 Console.WriteLine(SR.WaitingForDebuggerAttach);
85                 Console.ReadLine();
86             }
87
88             if (_commandLineOptions.CompileBubbleGenerics)
89             {
90                 if (!_commandLineOptions.CompositeOrInputBubble)
91                 {
92                     Console.WriteLine(SR.WarningIgnoringBubbleGenerics);
93                     _commandLineOptions.CompileBubbleGenerics = false;
94                 }
95             }
96
97             _optimizationMode = OptimizationMode.None;
98             if (_commandLineOptions.OptimizeDisabled)
99             {
100                 if (_commandLineOptions.Optimize || _commandLineOptions.OptimizeSpace || _commandLineOptions.OptimizeTime)
101                     Console.WriteLine(SR.WarningOverridingOptimize);
102             }
103             else if (_commandLineOptions.OptimizeSpace)
104             {
105                 if (_commandLineOptions.OptimizeTime)
106                     Console.WriteLine(SR.WarningOverridingOptimizeSpace);
107                 _optimizationMode = OptimizationMode.PreferSize;
108             }
109             else if (_commandLineOptions.OptimizeTime)
110                 _optimizationMode = OptimizationMode.PreferSpeed;
111             else if (_commandLineOptions.Optimize)
112                 _optimizationMode = OptimizationMode.Blended;
113
114             foreach (var input in _commandLineOptions.InputFilePaths)
115                 Helpers.AppendExpandedPaths(_inputFilePaths, input, true);
116
117             foreach (var input in _commandLineOptions.UnrootedInputFilePaths)
118                 Helpers.AppendExpandedPaths(_unrootedInputFilePaths, input, true);
119
120             foreach (var reference in _commandLineOptions.ReferenceFilePaths)
121                 Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false);
122
123
124             int alignment = _commandLineOptions.CustomPESectionAlignment;
125             if (alignment != 0)
126             {
127                 // Must be a power of two and >= 4096
128                 if (alignment < 4096 || (alignment & (alignment - 1)) != 0)
129                     throw new CommandLineException(SR.InvalidCustomPESectionAlignment);
130             }
131
132             if (_commandLineOptions.MethodLayout != null)
133             {
134                 _methodLayout = _commandLineOptions.MethodLayout.ToLowerInvariant() switch
135                 {
136                     "defaultsort" => ReadyToRunMethodLayoutAlgorithm.DefaultSort,
137                     "exclusiveweight" => ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight,
138                     "hotcold" => ReadyToRunMethodLayoutAlgorithm.HotCold,
139                     "hotwarmcold" => ReadyToRunMethodLayoutAlgorithm.HotWarmCold,
140                     "callfrequency" => ReadyToRunMethodLayoutAlgorithm.CallFrequency,
141                     _ => throw new CommandLineException(SR.InvalidMethodLayout)
142                 };
143             }
144
145             if (_commandLineOptions.FileLayout != null)
146             {
147                 _fileLayout = _commandLineOptions.FileLayout.ToLowerInvariant() switch
148                 {
149                     "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort,
150                     "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder,
151                     _ => throw new CommandLineException(SR.InvalidFileLayout)
152                 };
153             }
154
155         }
156
157         private void ConfigureTarget()
158         {
159             //
160             // Set target Architecture and OS
161             //
162             if (_commandLineOptions.TargetArch != null)
163             {
164                 if (_commandLineOptions.TargetArch.Equals("x86", StringComparison.OrdinalIgnoreCase))
165                     _targetArchitecture = TargetArchitecture.X86;
166                 else if (_commandLineOptions.TargetArch.Equals("x64", StringComparison.OrdinalIgnoreCase))
167                     _targetArchitecture = TargetArchitecture.X64;
168                 else if (_commandLineOptions.TargetArch.Equals("arm", StringComparison.OrdinalIgnoreCase))
169                     _targetArchitecture = TargetArchitecture.ARM;
170                 else if (_commandLineOptions.TargetArch.Equals("armel", StringComparison.OrdinalIgnoreCase))
171                 {
172                     _targetArchitecture = TargetArchitecture.ARM;
173                     _armelAbi = true;
174                 }
175                 else if (_commandLineOptions.TargetArch.Equals("arm64", StringComparison.OrdinalIgnoreCase))
176                     _targetArchitecture = TargetArchitecture.ARM64;
177                 else
178                     throw new CommandLineException(SR.TargetArchitectureUnsupported);
179             }
180             if (_commandLineOptions.TargetOS != null)
181             {
182                 if (_commandLineOptions.TargetOS.Equals("windows", StringComparison.OrdinalIgnoreCase))
183                     _targetOS = TargetOS.Windows;
184                 else if (_commandLineOptions.TargetOS.Equals("linux", StringComparison.OrdinalIgnoreCase))
185                     _targetOS = TargetOS.Linux;
186                 else if (_commandLineOptions.TargetOS.Equals("osx", StringComparison.OrdinalIgnoreCase))
187                     _targetOS = TargetOS.OSX;
188                 else
189                     throw new CommandLineException(SR.TargetOSUnsupported);
190             }
191         }
192
193         private InstructionSetSupport ConfigureInstructionSetSupport()
194         {
195             InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture);
196
197             // Ready to run images are built with certain instruction set baselines
198             if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64))
199             {
200                 instructionSetSupportBuilder.AddSupportedInstructionSet("sse");
201                 instructionSetSupportBuilder.AddSupportedInstructionSet("sse2");
202             }
203             else if (_targetArchitecture == TargetArchitecture.ARM64)
204             {
205                 instructionSetSupportBuilder.AddSupportedInstructionSet("base");
206                 instructionSetSupportBuilder.AddSupportedInstructionSet("neon");
207             }
208
209
210             if (_commandLineOptions.InstructionSet != null)
211             {
212                 List<string> instructionSetParams = new List<string>();
213
214                 // At this time, instruction sets may only be specified with --input-bubble, as
215                 // we do not yet have a stable ABI for all vector parameter/return types.
216                 if (!_commandLineOptions.InputBubble)
217                     throw new CommandLineException(SR.InstructionSetWithoutInputBubble);
218
219                 // Normalize instruction set format to include implied +.
220                 string[] instructionSetParamsInput = _commandLineOptions.InstructionSet.Split(",");
221                 for (int i = 0; i < instructionSetParamsInput.Length; i++)
222                 {
223                     string instructionSet = instructionSetParamsInput[i];
224
225                     if (String.IsNullOrEmpty(instructionSet))
226                         throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, ""));
227
228                     char firstChar = instructionSet[0];
229                     if ((firstChar != '+') && (firstChar != '-'))
230                     {
231                         instructionSet =  "+" + instructionSet;
232                     }
233                     instructionSetParams.Add(instructionSet);
234                 }
235
236                 Dictionary<string, bool> instructionSetSpecification = new Dictionary<string, bool>();
237                 foreach (string instructionSetSpecifier in instructionSetParams)
238                 {
239                     string instructionSet = instructionSetSpecifier.Substring(1, instructionSetSpecifier.Length - 1);
240
241                     bool enabled = instructionSetSpecifier[0] == '+' ? true : false;
242                     if (enabled)
243                     {
244                         if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet))
245                             throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet));
246                     }
247                     else
248                     {
249                         if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet))
250                             throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet));
251                     }
252                 }
253             }
254
255             instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet,
256                 (string specifiedInstructionSet, string impliedInstructionSet) =>
257                     throw new CommandLineException(String.Format(SR.InstructionSetInvalidImplication, specifiedInstructionSet, impliedInstructionSet)));
258
259             InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture);
260
261             // Ready to run images are built with certain instruction sets that are optimistically assumed to be present
262             if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64))
263             {
264                 // For ReadyToRun we set these hardware features as enabled always, as most
265                 // of hardware in the wild supports them. Note that we do not indicate support for AVX, or any other
266                 // instruction set which uses the VEX encodings as the presence of those makes otherwise acceptable
267                 // code be unusable on hardware which does not support VEX encodings, as well as emulators that do not
268                 // support AVX instructions. As the jit generates logic that depends on these features it will call
269                 // notifyInstructionSetUsage, which will result in generation of a fixup to verify the behavior of
270                 // code.
271                 //
272                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse");
273                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse2");
274                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.1");
275                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2");
276                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes");
277                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul");
278                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt");
279                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt");
280             }
281
282             optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _,
283                 (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException());
284             optimisticInstructionSet.Remove(unsupportedInstructionSet);
285             optimisticInstructionSet.Add(supportedInstructionSet);
286
287             return new InstructionSetSupport(supportedInstructionSet,
288                                                                   unsupportedInstructionSet,
289                                                                   optimisticInstructionSet,
290                                                                   InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture),
291                                                                   _targetArchitecture);
292         }
293
294         private int Run(string[] args)
295         {
296             using (PerfEventSource.StartStopEvents.CompilationEvents())
297             {
298                 ICompilation compilation;
299                 using (PerfEventSource.StartStopEvents.LoadingEvents())
300                 {
301                     InitializeDefaultOptions();
302
303                     ProcessCommandLine(args);
304
305                     if (_commandLineOptions.Help)
306                     {
307                         Console.WriteLine(_commandLineOptions.HelpText);
308                         return 1;
309                     }
310
311                     if (_commandLineOptions.OutputFilePath == null)
312                         throw new CommandLineException(SR.MissingOutputFile);
313
314                     ConfigureTarget();
315                     InstructionSetSupport instructionSetSupport = ConfigureInstructionSetSupport();
316
317                     //
318                     // Initialize type system context
319                     //
320
321                     SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes;
322
323                     var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.CoreRTArmel : TargetAbi.CoreRT, instructionSetSupport.GetVectorTSimdVector());
324
325                     bool versionBubbleIncludesCoreLib = false;
326                     if (_commandLineOptions.InputBubble)
327                     {
328                         versionBubbleIncludesCoreLib = true;
329                     }
330                     else
331                     {
332                         foreach (var inputFile in _inputFilePaths)
333                         {
334                             if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0)
335                             {
336                                 versionBubbleIncludesCoreLib = true;
337                                 break;
338                             }
339                         }
340                         if (!versionBubbleIncludesCoreLib)
341                         {
342                             foreach (var inputFile in _unrootedInputFilePaths)
343                             {
344                                 if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0)
345                                 {
346                                     versionBubbleIncludesCoreLib = true;
347                                     break;
348                                 }
349                             }
350                         }
351                     }
352
353                     _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib);
354
355                     string compositeRootPath = _commandLineOptions.CompositeRootPath;
356
357                     //
358                     // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since
359                     // some tests contain a mixture of both managed and native binaries.
360                     //
361                     // See: https://github.com/dotnet/corert/issues/2785
362                     //
363                     // When we undo this this hack, replace this foreach with
364                     //  typeSystemContext.InputFilePaths = _inputFilePaths;
365                     //
366                     Dictionary<string, string> allInputFilePaths = new Dictionary<string, string>();
367                     Dictionary<string, string> inputFilePaths = new Dictionary<string, string>();
368                     List<ModuleDesc> referenceableModules = new List<ModuleDesc>();
369                     foreach (var inputFile in _inputFilePaths)
370                     {
371                         try
372                         {
373                             var module = _typeSystemContext.GetModuleFromPath(inputFile.Value);
374                             allInputFilePaths.Add(inputFile.Key, inputFile.Value);
375                             inputFilePaths.Add(inputFile.Key, inputFile.Value);
376                             referenceableModules.Add(module);
377                             if (compositeRootPath == null)
378                             {
379                                 compositeRootPath = Path.GetDirectoryName(inputFile.Value);
380                             }
381                         }
382                         catch (TypeSystemException.BadImageFormatException)
383                         {
384                             // Keep calm and carry on.
385                         }
386                     }
387
388                     Dictionary<string, string> unrootedInputFilePaths = new Dictionary<string, string>();
389                     foreach (var unrootedInputFile in _unrootedInputFilePaths)
390                     {
391                         try
392                         {
393                             var module = _typeSystemContext.GetModuleFromPath(unrootedInputFile.Value);
394                             if (!allInputFilePaths.ContainsKey(unrootedInputFile.Key))
395                             {
396                                 allInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value);
397                                 unrootedInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value);
398                                 referenceableModules.Add(module);
399                                 if (compositeRootPath == null)
400                                 {
401                                     compositeRootPath = Path.GetDirectoryName(unrootedInputFile.Value);
402                                 }
403                             }
404                         }
405                         catch (TypeSystemException.BadImageFormatException)
406                         {
407                             // Keep calm and carry on.
408                         }
409                     }
410
411                     CheckManagedCppInputFiles(allInputFilePaths.Values);
412
413                     _typeSystemContext.InputFilePaths = allInputFilePaths;
414                     _typeSystemContext.ReferenceFilePaths = _referenceFilePaths;
415
416                     List<EcmaModule> inputModules = new List<EcmaModule>();
417                     List<EcmaModule> rootingModules = new List<EcmaModule>();
418                     HashSet<ModuleDesc> versionBubbleModulesHash = new HashSet<ModuleDesc>();
419                     Guid? inputModuleMvid = null;
420
421                     foreach (var inputFile in inputFilePaths)
422                     {
423                         EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFile.Value);
424                         inputModules.Add(module);
425                         rootingModules.Add(module);
426                         versionBubbleModulesHash.Add(module);
427
428                         if (!_commandLineOptions.Composite && !inputModuleMvid.HasValue)
429                         {
430                             inputModuleMvid = module.MetadataReader.GetGuid(module.MetadataReader.GetModuleDefinition().Mvid);
431                         }
432
433                         if (!_commandLineOptions.CompositeOrInputBubble)
434                         {
435                             break;
436                         }
437                     }
438
439                     foreach (var unrootedInputFile in unrootedInputFilePaths)
440                     {
441                         EcmaModule module = _typeSystemContext.GetModuleFromPath(unrootedInputFile.Value);
442                         inputModules.Add(module);
443                         versionBubbleModulesHash.Add(module);
444                     }
445
446                     string systemModuleName = _commandLineOptions.SystemModule ?? DefaultSystemModule;
447                     _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName));
448
449                     if (_typeSystemContext.InputFilePaths.Count == 0)
450                     {
451                         if (_commandLineOptions.InputFilePaths.Count > 0)
452                         {
453                             Console.WriteLine(SR.InputWasNotLoadable);
454                             return 2;
455                         }
456                         throw new CommandLineException(SR.NoInputFiles);
457                     }
458
459                     //
460                     // Initialize compilation group and compilation roots
461                     //
462
463                     // Single method mode?
464                     MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(_typeSystemContext);
465
466                     var logger = new Logger(Console.Out, _commandLineOptions.Verbose);
467
468                     List<string> mibcFiles = new List<string>();
469                     foreach (var file in _commandLineOptions.MibcFilePaths)
470                     {
471                         mibcFiles.Add(file);
472                     }
473
474                     foreach (var referenceFile in _referenceFilePaths.Values)
475                     {
476                         try
477                         {
478                             EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile);
479                             if (versionBubbleModulesHash.Contains(module))
480                             {
481                                 // Ignore reference assemblies that have also been passed as inputs
482                                 continue;
483                             }
484                             referenceableModules.Add(module);
485                             if (_commandLineOptions.InputBubble)
486                             {
487                                 // In large version bubble mode add reference paths to the compilation group
488                                 versionBubbleModulesHash.Add(module);
489                             }
490                         }
491                         catch { } // Ignore non-managed pe files
492                     }
493
494                     List<ModuleDesc> versionBubbleModules = new List<ModuleDesc>(versionBubbleModulesHash);
495
496                     if (!_commandLineOptions.Composite && inputModules.Count != 1)
497                     {
498                         throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules)));
499                     }
500
501                     ReadyToRunCompilationModuleGroupBase compilationGroup;
502                     List<ICompilationRootProvider> compilationRoots = new List<ICompilationRootProvider>();
503                     if (singleMethod != null)
504                     {
505                         // Compiling just a single method
506                         compilationGroup = new SingleMethodCompilationModuleGroup(
507                             _typeSystemContext,
508                             _commandLineOptions.Composite,
509                             _commandLineOptions.InputBubble,
510                             inputModules,
511                             versionBubbleModules,
512                             _commandLineOptions.CompileBubbleGenerics,
513                             singleMethod);
514                         compilationRoots.Add(new SingleMethodRootProvider(singleMethod));
515                     }
516                     else if (_commandLineOptions.CompileNoMethods)
517                     {
518                         compilationGroup = new NoMethodsCompilationModuleGroup(
519                             _typeSystemContext,
520                             _commandLineOptions.Composite,
521                             _commandLineOptions.InputBubble,
522                             inputModules,
523                             versionBubbleModules,
524                             _commandLineOptions.CompileBubbleGenerics);
525                     }
526                     else
527                     {
528                         // Single assembly compilation.
529                         compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(
530                             _typeSystemContext,
531                             _commandLineOptions.Composite,
532                             _commandLineOptions.InputBubble,
533                             inputModules,
534                             versionBubbleModules,
535                             _commandLineOptions.CompileBubbleGenerics);
536                     }
537
538                     // Load any profiles generated by method call chain analyis
539                     CallChainProfile jsonProfile = null;
540
541                     if (!string.IsNullOrEmpty(_commandLineOptions.CallChainProfileFile))
542                     {
543                         jsonProfile = new CallChainProfile(_commandLineOptions.CallChainProfileFile, _typeSystemContext, referenceableModules);
544                     }
545
546                     // Examine profile guided information as appropriate
547                     ProfileDataManager profileDataManager =
548                         new ProfileDataManager(logger,
549                         referenceableModules,
550                         inputModules,
551                         versionBubbleModules,
552                         _commandLineOptions.CompileBubbleGenerics ? inputModules[0] : null,
553                         mibcFiles,
554                         jsonProfile,
555                         _typeSystemContext,
556                         compilationGroup,
557                         _commandLineOptions.EmbedPgoData);
558
559                     if (_commandLineOptions.Partial)
560                         compilationGroup.ApplyProfilerGuidedCompilationRestriction(profileDataManager);
561                     else
562                         compilationGroup.ApplyProfilerGuidedCompilationRestriction(null);
563
564                     if ((singleMethod == null) && !_commandLineOptions.CompileNoMethods)
565                     {
566                         // For normal compilations add compilation roots.
567                         foreach (var module in rootingModules)
568                         {
569                             compilationRoots.Add(new ReadyToRunRootProvider(
570                                 module,
571                                 profileDataManager,
572                                 profileDrivenPartialNGen: _commandLineOptions.Partial));
573
574                             if (!_commandLineOptions.CompositeOrInputBubble)
575                             {
576                                 break;
577                             }
578                         }
579                     }
580                     // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize
581                     // or produce debuggable code if an explicit optimization level was not specified on the command line 
582                     if (_optimizationMode == OptimizationMode.None && !_commandLineOptions.OptimizeDisabled && !_commandLineOptions.Composite)
583                     {
584                         System.Diagnostics.Debug.Assert(inputModules.Count == 1);
585                         _optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended;
586                     }
587
588                     //
589                     // Compile
590                     //
591
592                     ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder(
593                         _typeSystemContext, compilationGroup, allInputFilePaths.Values, compositeRootPath);
594                     string compilationUnitPrefix = "";
595                     builder.UseCompilationUnitPrefix(compilationUnitPrefix);
596
597                     ILProvider ilProvider = new ReadyToRunILProvider();
598
599                     DependencyTrackingLevel trackingLevel = _commandLineOptions.DgmlLogFileName == null ?
600                         DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First);
601
602                     builder
603                         .UseIbcTuning(_commandLineOptions.Tuning)
604                         .UseResilience(_commandLineOptions.Resilient)
605                         .UseMapFile(_commandLineOptions.Map)
606                         .UseMapCsvFile(_commandLineOptions.MapCsv)
607                         .UsePdbFile(_commandLineOptions.Pdb, _commandLineOptions.PdbPath)
608                         .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath, inputModuleMvid)
609                         .UseProfileFile(jsonProfile != null)
610                         .UseParallelism(_commandLineOptions.Parallelism)
611                         .UseProfileData(profileDataManager)
612                         .FileLayoutAlgorithms(_methodLayout, _fileLayout)
613                         .UseJitPath(_commandLineOptions.JitPath)
614                         .UseInstructionSetSupport(instructionSetSupport)
615                         .UseCustomPESectionAlignment(_commandLineOptions.CustomPESectionAlignment)
616                         .UseVerifyTypeAndFieldLayout(_commandLineOptions.VerifyTypeAndFieldLayout)
617                         .GenerateOutputFile(_commandLineOptions.OutputFilePath)
618                         .UseILProvider(ilProvider)
619                         .UseBackendOptions(_commandLineOptions.CodegenOptions)
620                         .UseLogger(logger)
621                         .UseDependencyTracking(trackingLevel)
622                         .UseCompilationRoots(compilationRoots)
623                         .UseOptimizationMode(_optimizationMode);
624
625                     if (_commandLineOptions.PrintReproInstructions)
626                         builder.UsePrintReproInstructions(CreateReproArgumentString);
627
628                     compilation = builder.ToCompilation();
629
630                 }
631                 compilation.Compile(_commandLineOptions.OutputFilePath);
632
633                 if (_commandLineOptions.DgmlLogFileName != null)
634                     compilation.WriteDependencyLog(_commandLineOptions.DgmlLogFileName);
635
636                 compilation.Dispose();
637             }
638
639             return 0;
640         }
641
642         private void CheckManagedCppInputFiles(IEnumerable<string> inputPaths)
643         {
644             foreach (string inputFilePath in inputPaths)
645             {
646                 EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFilePath);
647                 if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0)
648                 {
649                     throw new CommandLineException(string.Format(SR.ManagedCppNotSupported, inputFilePath));
650                 }
651             }
652         }
653
654         private TypeDesc FindType(CompilerTypeSystemContext context, string typeName)
655         {
656             ModuleDesc systemModule = context.SystemModule;
657
658             TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) =>
659             {
660                 return (MetadataType)context.GetCanonType(typeDefName)
661                     ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound);
662             });
663             if (foundType == null)
664                 throw new CommandLineException(string.Format(SR.TypeNotFound, typeName));
665
666             return foundType;
667         }
668
669         private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context)
670         {
671             if (_commandLineOptions.SingleMethodName == null && _commandLineOptions.SingleMethodTypeName == null && _commandLineOptions.SingleMethodGenericArg == null)
672                 return null;
673
674             if (_commandLineOptions.SingleMethodName == null || _commandLineOptions.SingleMethodTypeName == null)
675                 throw new CommandLineException(SR.TypeAndMethodNameNeeded);
676
677             TypeDesc owningType = FindType(context, _commandLineOptions.SingleMethodTypeName);
678
679             // TODO: allow specifying signature to distinguish overloads
680             MethodDesc method = null;
681             bool printMethodList = false;
682             int curIndex = 0;
683             foreach (var searchMethod in owningType.GetMethods())
684             {
685                 if (searchMethod.Name != _commandLineOptions.SingleMethodName)
686                     continue;
687
688                 curIndex++;
689                 if (_commandLineOptions.SingleMethodIndex != 0)
690                 {
691                     if (curIndex == _commandLineOptions.SingleMethodIndex)
692                     {
693                         method = searchMethod;
694                         break;
695                     }
696                 }
697                 else
698                 {
699                     if (method == null)
700                     {
701                         method = searchMethod;
702                     }
703                     else
704                     {
705                         printMethodList = true;
706                     }
707                 }
708             }
709
710             if (printMethodList)
711             {
712                 curIndex = 0;
713                 foreach (var searchMethod in owningType.GetMethods())
714                 {
715                     if (searchMethod.Name != _commandLineOptions.SingleMethodName)
716                         continue;
717
718                     curIndex++;
719                     Console.WriteLine($"{curIndex} - {searchMethod}");
720                 }
721                 throw new CommandLineException(SR.SingleMethodIndexNeeded);
722             }
723
724             if (method == null)
725                 throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName));
726
727             if (method.HasInstantiation != (_commandLineOptions.SingleMethodGenericArg != null) ||
728                 (method.HasInstantiation && (method.Instantiation.Length != _commandLineOptions.SingleMethodGenericArg.Count)))
729             {
730                 throw new CommandLineException(
731                     string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName));
732             }
733
734             if (method.HasInstantiation)
735             {
736                 List<TypeDesc> genericArguments = new List<TypeDesc>();
737                 foreach (var argString in _commandLineOptions.SingleMethodGenericArg)
738                     genericArguments.Add(FindType(context, argString));
739                 method = method.MakeInstantiatedMethod(genericArguments.ToArray());
740             }
741
742             return method;
743         }
744
745         private static string CreateReproArgumentString(MethodDesc method)
746         {
747             StringBuilder sb = new StringBuilder();
748
749             var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)method.Context.SystemModule);
750
751             sb.Append($"--singlemethodtypename \"{formatter.FormatName(method.OwningType, true)}\"");
752             sb.Append($" --singlemethodname \"{method.Name}\"");
753             {
754                 int curIndex = 0;
755                 foreach (var searchMethod in method.OwningType.GetMethods())
756                 {
757                     if (searchMethod.Name != method.Name)
758                         continue;
759
760                     curIndex++;
761                     if (searchMethod == method.GetMethodDefinition())
762                     {
763                         sb.Append($" --singlemethodindex {curIndex}");
764                     }
765                 }
766             }
767
768             for (int i = 0; i < method.Instantiation.Length; i++)
769                 sb.Append($" --singlemethodgenericarg \"{formatter.FormatName(method.Instantiation[i], true)}\"");
770
771             return sb.ToString();
772         }
773
774         private static bool DumpReproArguments(CodeGenerationFailedException ex)
775         {
776             Console.WriteLine(SR.DumpReproInstructions);
777
778             MethodDesc failingMethod = ex.Method;
779             Console.WriteLine(CreateReproArgumentString(failingMethod));
780             return false;
781         }
782
783         private static int Main(string[] args)
784         {
785 #if DEBUG
786             try
787             {
788                 return new Program().Run(args);
789             }
790             catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex))
791             {
792                 throw new NotSupportedException(); // Unreachable
793             }
794 #else
795             try
796             {
797                 return new Program().Run(args);
798             }
799             catch (Exception e)
800             {
801                 Console.Error.WriteLine(string.Format(SR.ProgramError, e.Message));
802                 Console.Error.WriteLine(e.ToString());
803                 return 1;
804             }
805 #endif
806         }
807     }
808 }