Use temporary ni.dll.tmp files with crossgen2 pipeline mode in order to not interfere...
[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.Collections.Immutable;
7 using System.IO;
8 using System.Reflection.Metadata;
9 using System.Reflection.PortableExecutable;
10 using System.Runtime.InteropServices;
11 using System.Text;
12
13 using Internal.CommandLine;
14 using Internal.IL;
15 using Internal.TypeSystem;
16 using Internal.TypeSystem.Ecma;
17
18 namespace ILCompiler
19 {
20     internal class Program
21     {
22         private const string DefaultSystemModule = "System.Private.CoreLib";
23
24         private CommandLineOptions _commandLineOptions;
25         public TargetOS _targetOS;
26         public TargetArchitecture _targetArchitecture;
27         private bool _armelAbi = false;
28         public OptimizationMode _optimizationMode;
29
30         // File names as strings in args
31         private Dictionary<string, string> _inputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
32         private Dictionary<string, string> _unrootedInputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
33         private Dictionary<string, string> _referenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
34
35         // Modules and their names after loading
36         private Dictionary<string, string> _allInputFilePaths = new Dictionary<string, string>();
37         private List<ModuleDesc> _referenceableModules = new List<ModuleDesc>();
38
39         private Dictionary<string, string> _inputbubblereferenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
40
41         private CompilerTypeSystemContext _typeSystemContext;
42         private ReadyToRunMethodLayoutAlgorithm _methodLayout;
43         private ReadyToRunFileLayoutAlgorithm _fileLayout;
44
45         private Program()
46         {
47         }
48
49         public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture arch)
50         {
51             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
52                 os = TargetOS.Windows;
53             else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
54                 os = TargetOS.Linux;
55             else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
56                 os = TargetOS.OSX;
57             else
58                 throw new NotImplementedException();
59
60             switch (RuntimeInformation.ProcessArchitecture)
61             {
62                 case Architecture.X86:
63                     arch = TargetArchitecture.X86;
64                     break;
65                 case Architecture.X64:
66                     arch = TargetArchitecture.X64;
67                     break;
68                 case Architecture.Arm:
69                     arch = TargetArchitecture.ARM;
70                     break;
71                 case Architecture.Arm64:
72                     arch = TargetArchitecture.ARM64;
73                     break;
74                 default:
75                     throw new NotImplementedException();
76             }
77
78         }
79
80         private void InitializeDefaultOptions()
81         {
82             ComputeDefaultOptions(out _targetOS, out _targetArchitecture);
83         }
84
85         private void ProcessCommandLine(string[] args)
86         {
87             PerfEventSource.StartStopEvents.CommandLineProcessingStart();
88             _commandLineOptions = new CommandLineOptions(args);
89             PerfEventSource.StartStopEvents.CommandLineProcessingStop();
90
91             if (_commandLineOptions.Help)
92             {
93                 return;
94             }
95
96             if (_commandLineOptions.WaitForDebugger)
97             {
98                 Console.WriteLine(SR.WaitingForDebuggerAttach);
99                 Console.ReadLine();
100             }
101
102             if (_commandLineOptions.CompileBubbleGenerics)
103             {
104                 if (!_commandLineOptions.CompositeOrInputBubble)
105                 {
106                     Console.WriteLine(SR.WarningIgnoringBubbleGenerics);
107                     _commandLineOptions.CompileBubbleGenerics = false;
108                 }
109             }
110
111             _optimizationMode = OptimizationMode.None;
112             if (_commandLineOptions.OptimizeDisabled)
113             {
114                 if (_commandLineOptions.Optimize || _commandLineOptions.OptimizeSpace || _commandLineOptions.OptimizeTime)
115                     Console.WriteLine(SR.WarningOverridingOptimize);
116             }
117             else if (_commandLineOptions.OptimizeSpace)
118             {
119                 if (_commandLineOptions.OptimizeTime)
120                     Console.WriteLine(SR.WarningOverridingOptimizeSpace);
121                 _optimizationMode = OptimizationMode.PreferSize;
122             }
123             else if (_commandLineOptions.OptimizeTime)
124                 _optimizationMode = OptimizationMode.PreferSpeed;
125             else if (_commandLineOptions.Optimize)
126                 _optimizationMode = OptimizationMode.Blended;
127
128             foreach (var input in _commandLineOptions.InputFilePaths)
129                 Helpers.AppendExpandedPaths(_inputFilePaths, input, true);
130
131             foreach (var input in _commandLineOptions.UnrootedInputFilePaths)
132                 Helpers.AppendExpandedPaths(_unrootedInputFilePaths, input, true);
133
134             foreach (var reference in _commandLineOptions.ReferenceFilePaths)
135                 Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false);
136
137             foreach (var reference in _commandLineOptions.InputBubbleReferenceFilePaths)
138               Helpers.AppendExpandedPaths(_inputbubblereferenceFilePaths, reference, false);
139
140
141             int alignment = _commandLineOptions.CustomPESectionAlignment;
142             if (alignment != 0)
143             {
144                 // Must be a power of two and >= 4096
145                 if (alignment < 4096 || (alignment & (alignment - 1)) != 0)
146                     throw new CommandLineException(SR.InvalidCustomPESectionAlignment);
147             }
148
149             if (_commandLineOptions.MethodLayout != null)
150             {
151                 _methodLayout = _commandLineOptions.MethodLayout.ToLowerInvariant() switch
152                 {
153                     "defaultsort" => ReadyToRunMethodLayoutAlgorithm.DefaultSort,
154                     "exclusiveweight" => ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight,
155                     "hotcold" => ReadyToRunMethodLayoutAlgorithm.HotCold,
156                     "hotwarmcold" => ReadyToRunMethodLayoutAlgorithm.HotWarmCold,
157                     "callfrequency" => ReadyToRunMethodLayoutAlgorithm.CallFrequency,
158                     "pettishansen" => ReadyToRunMethodLayoutAlgorithm.PettisHansen,
159                     "random" => ReadyToRunMethodLayoutAlgorithm.Random,
160                     _ => throw new CommandLineException(SR.InvalidMethodLayout)
161                 };
162             }
163
164             if (_commandLineOptions.FileLayout != null)
165             {
166                 _fileLayout = _commandLineOptions.FileLayout.ToLowerInvariant() switch
167                 {
168                     "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort,
169                     "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder,
170                     _ => throw new CommandLineException(SR.InvalidFileLayout)
171                 };
172             }
173
174         }
175
176         public static TargetArchitecture GetTargetArchitectureFromArg(string archArg, out bool armelAbi)
177         {
178             armelAbi = false;
179             if (archArg.Equals("x86", StringComparison.OrdinalIgnoreCase))
180                 return TargetArchitecture.X86;
181             else if (archArg.Equals("x64", StringComparison.OrdinalIgnoreCase))
182                 return  TargetArchitecture.X64;
183             else if (archArg.Equals("arm", StringComparison.OrdinalIgnoreCase))
184                 return  TargetArchitecture.ARM;
185             else if (archArg.Equals("armel", StringComparison.OrdinalIgnoreCase))
186             {
187                 armelAbi = true;
188                 return TargetArchitecture.ARM;
189             }
190             else if (archArg.Equals("arm64", StringComparison.OrdinalIgnoreCase))
191                 return TargetArchitecture.ARM64;
192             else
193                 throw new CommandLineException(SR.TargetArchitectureUnsupported);
194         }
195
196         private void ConfigureTarget()
197         {
198             //
199             // Set target Architecture and OS
200             //
201             if (_commandLineOptions.TargetArch != null)
202             {
203                 _targetArchitecture = GetTargetArchitectureFromArg(_commandLineOptions.TargetArch, out _armelAbi);
204             }
205             if (_commandLineOptions.TargetOS != null)
206             {
207                 if (_commandLineOptions.TargetOS.Equals("windows", StringComparison.OrdinalIgnoreCase))
208                     _targetOS = TargetOS.Windows;
209                 else if (_commandLineOptions.TargetOS.Equals("linux", StringComparison.OrdinalIgnoreCase))
210                     _targetOS = TargetOS.Linux;
211                 else if (_commandLineOptions.TargetOS.Equals("osx", StringComparison.OrdinalIgnoreCase))
212                     _targetOS = TargetOS.OSX;
213                 else
214                     throw new CommandLineException(SR.TargetOSUnsupported);
215             }
216         }
217
218         private InstructionSetSupport ConfigureInstructionSetSupport()
219         {
220             InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture);
221
222             // Ready to run images are built with certain instruction set baselines
223             if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64))
224             {
225                 instructionSetSupportBuilder.AddSupportedInstructionSet("sse");
226                 instructionSetSupportBuilder.AddSupportedInstructionSet("sse2");
227             }
228             else if (_targetArchitecture == TargetArchitecture.ARM64)
229             {
230                 instructionSetSupportBuilder.AddSupportedInstructionSet("base");
231                 instructionSetSupportBuilder.AddSupportedInstructionSet("neon");
232             }
233
234
235             if (_commandLineOptions.InstructionSet != null)
236             {
237                 List<string> instructionSetParams = new List<string>();
238
239                 // Normalize instruction set format to include implied +.
240                 string[] instructionSetParamsInput = _commandLineOptions.InstructionSet.Split(",");
241                 for (int i = 0; i < instructionSetParamsInput.Length; i++)
242                 {
243                     string instructionSet = instructionSetParamsInput[i];
244
245                     if (String.IsNullOrEmpty(instructionSet))
246                         throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, ""));
247
248                     char firstChar = instructionSet[0];
249                     if ((firstChar != '+') && (firstChar != '-'))
250                     {
251                         instructionSet =  "+" + instructionSet;
252                     }
253                     instructionSetParams.Add(instructionSet);
254                 }
255
256                 Dictionary<string, bool> instructionSetSpecification = new Dictionary<string, bool>();
257                 foreach (string instructionSetSpecifier in instructionSetParams)
258                 {
259                     string instructionSet = instructionSetSpecifier.Substring(1, instructionSetSpecifier.Length - 1);
260
261                     bool enabled = instructionSetSpecifier[0] == '+' ? true : false;
262                     if (enabled)
263                     {
264                         if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet))
265                             throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet));
266                     }
267                     else
268                     {
269                         if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet))
270                             throw new CommandLineException(String.Format(SR.InstructionSetMustNotBe, instructionSet));
271                     }
272                 }
273             }
274
275             instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet,
276                 (string specifiedInstructionSet, string impliedInstructionSet) =>
277                     throw new CommandLineException(String.Format(SR.InstructionSetInvalidImplication, specifiedInstructionSet, impliedInstructionSet)));
278
279             InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture);
280
281             // Ready to run images are built with certain instruction sets that are optimistically assumed to be present
282             if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64))
283             {
284                 // For ReadyToRun we set these hardware features as enabled always, as most
285                 // of hardware in the wild supports them. Note that we do not indicate support for AVX, or any other
286                 // instruction set which uses the VEX encodings as the presence of those makes otherwise acceptable
287                 // code be unusable on hardware which does not support VEX encodings, as well as emulators that do not
288                 // support AVX instructions. As the jit generates logic that depends on these features it will call
289                 // notifyInstructionSetUsage, which will result in generation of a fixup to verify the behavior of
290                 // code.
291                 //
292                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse");
293                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse2");
294                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.1");
295                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2");
296                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes");
297                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul");
298                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt");
299                 optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt");
300             }
301
302             optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _,
303                 (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException());
304             optimisticInstructionSet.Remove(unsupportedInstructionSet);
305             optimisticInstructionSet.Add(supportedInstructionSet);
306
307             return new InstructionSetSupport(supportedInstructionSet,
308                                                                   unsupportedInstructionSet,
309                                                                   optimisticInstructionSet,
310                                                                   InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture),
311                                                                   _targetArchitecture);
312         }
313
314         private int Run(string[] args)
315         {
316             InitializeDefaultOptions();
317
318             ProcessCommandLine(args);
319
320             if (_commandLineOptions.Help)
321             {
322                 Console.WriteLine(_commandLineOptions.HelpText);
323                 return 1;
324             }
325
326             if (_commandLineOptions.OutputFilePath == null && !_commandLineOptions.OutNearInput)
327                 throw new CommandLineException(SR.MissingOutputFile);
328
329             if (_commandLineOptions.SingleFileCompilation && !_commandLineOptions.OutNearInput)
330                 throw new CommandLineException(SR.MissingOutNearInput);
331
332             ConfigureTarget();
333             InstructionSetSupport instructionSetSupport = ConfigureInstructionSetSupport();
334
335             SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes;
336
337             var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.CoreRTArmel : TargetAbi.CoreRT, instructionSetSupport.GetVectorTSimdVector());
338
339             bool versionBubbleIncludesCoreLib = false;
340             if (_commandLineOptions.InputBubble)
341             {
342                 versionBubbleIncludesCoreLib = true;
343             }
344             else
345             {
346                 if (!_commandLineOptions.SingleFileCompilation)
347                 {
348                     foreach (var inputFile in _inputFilePaths)
349                     {
350                         if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0)
351                         {
352                             versionBubbleIncludesCoreLib = true;
353                             break;
354                         }
355                     }
356                 }
357                 if (!versionBubbleIncludesCoreLib)
358                 {
359                     foreach (var inputFile in _unrootedInputFilePaths)
360                     {
361                         if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0)
362                         {
363                             versionBubbleIncludesCoreLib = true;
364                             break;
365                         }
366                     }
367                 }
368             }
369
370             //
371             // Initialize type system context
372             //
373             _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib);
374
375             string compositeRootPath = _commandLineOptions.CompositeRootPath;
376
377             // Collections for already loaded modules
378             Dictionary<string, string> inputFilePaths = new Dictionary<string, string>();
379             Dictionary<string, string> unrootedInputFilePaths = new Dictionary<string, string>();
380             HashSet<ModuleDesc> versionBubbleModulesHash = new HashSet<ModuleDesc>();
381
382             using (PerfEventSource.StartStopEvents.LoadingEvents())
383             {
384                 //
385                 // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since
386                 // some tests contain a mixture of both managed and native binaries.
387                 //
388                 // See: https://github.com/dotnet/corert/issues/2785
389                 //
390                 // When we undo this this hack, replace this foreach with
391                 //  typeSystemContext.InputFilePaths = inFilePaths;
392                 //
393
394                 foreach (var inputFile in _inputFilePaths)
395                 {
396                     try
397                     {
398                         var module = _typeSystemContext.GetModuleFromPath(inputFile.Value);
399                         _allInputFilePaths.Add(inputFile.Key, inputFile.Value);
400                         inputFilePaths.Add(inputFile.Key, inputFile.Value);
401                         _referenceableModules.Add(module);
402                         if (compositeRootPath == null)
403                         {
404                             compositeRootPath = Path.GetDirectoryName(inputFile.Value);
405                         }
406                     }
407                     catch (TypeSystemException.BadImageFormatException)
408                     {
409                         // Keep calm and carry on.
410                     }
411                 }
412
413                 foreach (var unrootedInputFile in _unrootedInputFilePaths)
414                 {
415                     try
416                     {
417                         var module = _typeSystemContext.GetModuleFromPath(unrootedInputFile.Value);
418                         if (!_allInputFilePaths.ContainsKey(unrootedInputFile.Key))
419                         {
420                             _allInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value);
421                             unrootedInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value);
422                             _referenceableModules.Add(module);
423                             if (compositeRootPath == null)
424                             {
425                                 compositeRootPath = Path.GetDirectoryName(unrootedInputFile.Value);
426                             }
427                         }
428                     }
429                     catch (TypeSystemException.BadImageFormatException)
430                     {
431                         // Keep calm and carry on.
432                     }
433                 }
434
435                 CheckManagedCppInputFiles(_allInputFilePaths.Values);
436
437                 _typeSystemContext.InputFilePaths = _allInputFilePaths;
438                 _typeSystemContext.ReferenceFilePaths = _referenceFilePaths;
439
440                 if (_typeSystemContext.InputFilePaths.Count == 0)
441                 {
442                     if (_commandLineOptions.InputFilePaths.Count > 0)
443                     {
444                         Console.WriteLine(SR.InputWasNotLoadable);
445                         return 2;
446                     }
447                     throw new CommandLineException(SR.NoInputFiles);
448                 }
449
450                 foreach (var referenceFile in _referenceFilePaths.Values)
451                 {
452                     try
453                     {
454                         EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile);
455                         _referenceableModules.Add(module);
456                         if (_commandLineOptions.InputBubble && _inputbubblereferenceFilePaths.Count == 0)
457                         {
458                             // In large version bubble mode add reference paths to the compilation group
459                             // Consider bubble as large if no explicit bubble references were passed
460                             versionBubbleModulesHash.Add(module);
461                         }
462                     }
463                     catch { } // Ignore non-managed pe files
464                 }
465
466                 if (_commandLineOptions.InputBubble)
467                 {
468                     foreach (var referenceFile in _inputbubblereferenceFilePaths.Values)
469                     {
470                         try
471                         {
472                             EcmaModule module = _typeSystemContext.GetModuleFromPath(referenceFile);
473                             versionBubbleModulesHash.Add(module);
474                         }
475                         catch { } // Ignore non-managed pe files
476                     }
477                 }
478             }
479
480             string systemModuleName = _commandLineOptions.SystemModule ?? DefaultSystemModule;
481             _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName));
482             CompilerTypeSystemContext typeSystemContext = _typeSystemContext;
483
484             if (_commandLineOptions.SingleFileCompilation)
485             {
486                 var singleCompilationInputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
487
488                 foreach (var inputFile in inputFilePaths)
489                 {
490                     var singleCompilationVersionBubbleModulesHash = new HashSet<ModuleDesc>(versionBubbleModulesHash);
491
492                     singleCompilationInputFilePaths.Clear();
493                     singleCompilationInputFilePaths.Add(inputFile.Key, inputFile.Value);
494                     typeSystemContext.InputFilePaths = singleCompilationInputFilePaths;
495
496                     if (!_commandLineOptions.InputBubble)
497                     {
498                         bool singleCompilationVersionBubbleIncludesCoreLib = versionBubbleIncludesCoreLib || (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0);
499
500                         typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext);
501                         typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName));
502                     }
503
504                     RunSingleCompilation(singleCompilationInputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, singleCompilationVersionBubbleModulesHash, typeSystemContext);
505                 }
506
507                 // 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
508                 // See https://github.com/dotnet/runtime/issues/55663#issuecomment-898161751 for more details
509                 if (_commandLineOptions.InputBubble)
510                 {
511                     foreach (var inputFile in inputFilePaths)
512                     {
513                         var tmpOutFile = inputFile.Value.Replace(".dll", ".ni.dll.tmp");
514                         var outFile = inputFile.Value.Replace(".dll", ".ni.dll");
515                         Console.WriteLine($@"Moving R2R PE file: {tmpOutFile} to {outFile}");
516                         System.IO.File.Move(tmpOutFile, outFile);
517                     }
518                 }
519             }
520             else
521             {
522                 RunSingleCompilation(inputFilePaths, instructionSetSupport, compositeRootPath, unrootedInputFilePaths, versionBubbleModulesHash, typeSystemContext);
523             }
524
525             return 0;
526         }
527
528         private void RunSingleCompilation(Dictionary<string, string> inFilePaths, InstructionSetSupport instructionSetSupport, string compositeRootPath, Dictionary<string, string> unrootedInputFilePaths, HashSet<ModuleDesc> versionBubbleModulesHash, CompilerTypeSystemContext typeSystemContext)
529         {
530             //
531             // Initialize output filename
532             //
533             var suffixStr = _commandLineOptions.SingleFileCompilation && _commandLineOptions.InputBubble ? ".ni.dll.tmp" : ".ni.dll";
534             var outFile = _commandLineOptions.OutNearInput ? inFilePaths.First().Value.Replace(".dll", suffixStr) : _commandLineOptions.OutputFilePath;
535
536             using (PerfEventSource.StartStopEvents.CompilationEvents())
537             {
538                 ICompilation compilation;
539                 using (PerfEventSource.StartStopEvents.LoadingEvents())
540                 {
541
542                     List<EcmaModule> inputModules = new List<EcmaModule>();
543                     List<EcmaModule> rootingModules = new List<EcmaModule>();
544
545                     foreach (var inputFile in inFilePaths)
546                     {
547                         EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value);
548                         inputModules.Add(module);
549                         rootingModules.Add(module);
550                         versionBubbleModulesHash.Add(module);
551
552
553                         if (!_commandLineOptions.CompositeOrInputBubble)
554                         {
555                             break;
556                         }
557                     }
558
559                     foreach (var unrootedInputFile in unrootedInputFilePaths)
560                     {
561                         EcmaModule module = typeSystemContext.GetModuleFromPath(unrootedInputFile.Value);
562                         inputModules.Add(module);
563                         versionBubbleModulesHash.Add(module);
564                     }
565
566                     //
567                     // Initialize compilation group and compilation roots
568                     //
569
570                     // Single method mode?
571                     MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext);
572
573                     var logger = new Logger(Console.Out, _commandLineOptions.Verbose);
574
575                     List<string> mibcFiles = new List<string>();
576                     foreach (var file in _commandLineOptions.MibcFilePaths)
577                     {
578                         mibcFiles.Add(file);
579                     }
580
581                     List<ModuleDesc> versionBubbleModules = new List<ModuleDesc>(versionBubbleModulesHash);
582
583                     if (!_commandLineOptions.Composite && inputModules.Count != 1)
584                     {
585                         throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules)));
586                     }
587
588                     ReadyToRunCompilationModuleGroupBase compilationGroup;
589                     List<ICompilationRootProvider> compilationRoots = new List<ICompilationRootProvider>();
590                     if (singleMethod != null)
591                     {
592                         // Compiling just a single method
593                         compilationGroup = new SingleMethodCompilationModuleGroup(
594                             typeSystemContext,
595                             _commandLineOptions.Composite,
596                             _commandLineOptions.InputBubble,
597                             inputModules,
598                             versionBubbleModules,
599                             _commandLineOptions.CompileBubbleGenerics,
600                             singleMethod);
601                         compilationRoots.Add(new SingleMethodRootProvider(singleMethod));
602                     }
603                     else if (_commandLineOptions.CompileNoMethods)
604                     {
605                         compilationGroup = new NoMethodsCompilationModuleGroup(
606                             typeSystemContext,
607                             _commandLineOptions.Composite,
608                             _commandLineOptions.InputBubble,
609                             inputModules,
610                             versionBubbleModules,
611                             _commandLineOptions.CompileBubbleGenerics);
612                     }
613                     else
614                     {
615                         // Single assembly compilation.
616                         compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(
617                             typeSystemContext,
618                             _commandLineOptions.Composite,
619                             _commandLineOptions.InputBubble,
620                             inputModules,
621                             versionBubbleModules,
622                             _commandLineOptions.CompileBubbleGenerics);
623                     }
624
625                     // Load any profiles generated by method call chain analyis
626                     CallChainProfile jsonProfile = null;
627
628                     if (!string.IsNullOrEmpty(_commandLineOptions.CallChainProfileFile))
629                     {
630                         jsonProfile = new CallChainProfile(_commandLineOptions.CallChainProfileFile, typeSystemContext, _referenceableModules);
631                     }
632
633                     // Examine profile guided information as appropriate
634                     ProfileDataManager profileDataManager =
635                         new ProfileDataManager(logger,
636                         _referenceableModules,
637                         inputModules,
638                         versionBubbleModules,
639                         _commandLineOptions.CompileBubbleGenerics ? inputModules[0] : null,
640                         mibcFiles,
641                         jsonProfile,
642                         typeSystemContext,
643                         compilationGroup,
644                         _commandLineOptions.EmbedPgoData);
645
646                     if (_commandLineOptions.Partial)
647                         compilationGroup.ApplyProfilerGuidedCompilationRestriction(profileDataManager);
648                     else
649                         compilationGroup.ApplyProfilerGuidedCompilationRestriction(null);
650
651                     if ((singleMethod == null) && !_commandLineOptions.CompileNoMethods)
652                     {
653                         // For normal compilations add compilation roots.
654                         foreach (var module in rootingModules)
655                         {
656                             compilationRoots.Add(new ReadyToRunRootProvider(
657                                 module,
658                                 profileDataManager,
659                                 profileDrivenPartialNGen: _commandLineOptions.Partial));
660
661                             if (!_commandLineOptions.CompositeOrInputBubble)
662                             {
663                                 break;
664                             }
665                         }
666                     }
667                     // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize
668                     // or produce debuggable code if an explicit optimization level was not specified on the command line
669                     OptimizationMode optimizationMode = _optimizationMode;
670                     if (optimizationMode == OptimizationMode.None && !_commandLineOptions.OptimizeDisabled && !_commandLineOptions.Composite)
671                     {
672                         System.Diagnostics.Debug.Assert(inputModules.Count == 1);
673                         optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended;
674                     }
675
676                     CompositeImageSettings compositeImageSettings = new CompositeImageSettings();
677
678                     if (_commandLineOptions.CompositeKeyFile != null)
679                     {
680                         ImmutableArray<byte> compositeStrongNameKey = File.ReadAllBytes(_commandLineOptions.CompositeKeyFile).ToImmutableArray();
681                         if (!IsValidPublicKey(compositeStrongNameKey))
682                         {
683                             throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey));
684                         }
685
686                         compositeImageSettings.PublicKey = compositeStrongNameKey;
687                     }
688
689                     //
690                     // Compile
691                     //
692
693                     ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder(
694                         typeSystemContext, compilationGroup, _allInputFilePaths.Values, compositeRootPath);
695                     string compilationUnitPrefix = "";
696                     builder.UseCompilationUnitPrefix(compilationUnitPrefix);
697
698                     ILProvider ilProvider = new ReadyToRunILProvider();
699
700                     DependencyTrackingLevel trackingLevel = _commandLineOptions.DgmlLogFileName == null ?
701                         DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First);
702
703                     builder
704                         .UseIbcTuning(_commandLineOptions.Tuning)
705                         .UseResilience(_commandLineOptions.Resilient)
706                         .UseMapFile(_commandLineOptions.Map)
707                         .UseMapCsvFile(_commandLineOptions.MapCsv)
708                         .UsePdbFile(_commandLineOptions.Pdb, _commandLineOptions.PdbPath)
709                         .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath, _commandLineOptions.PerfMapFormatVersion)
710                         .UseProfileFile(jsonProfile != null)
711                         .UseParallelism(_commandLineOptions.Parallelism)
712                         .UseProfileData(profileDataManager)
713                         .FileLayoutAlgorithms(_methodLayout, _fileLayout)
714                         .UseCompositeImageSettings(compositeImageSettings)
715                         .UseJitPath(_commandLineOptions.JitPath)
716                         .UseInstructionSetSupport(instructionSetSupport)
717                         .UseCustomPESectionAlignment(_commandLineOptions.CustomPESectionAlignment)
718                         .UseVerifyTypeAndFieldLayout(_commandLineOptions.VerifyTypeAndFieldLayout)
719                         .GenerateOutputFile(outFile)
720                         .UseILProvider(ilProvider)
721                         .UseBackendOptions(_commandLineOptions.CodegenOptions)
722                         .UseLogger(logger)
723                         .UseDependencyTracking(trackingLevel)
724                         .UseCompilationRoots(compilationRoots)
725                         .UseOptimizationMode(optimizationMode);
726
727                     if (_commandLineOptions.PrintReproInstructions)
728                         builder.UsePrintReproInstructions(CreateReproArgumentString);
729
730                     compilation = builder.ToCompilation();
731
732                 }
733                 compilation.Compile(outFile);
734
735                 if (_commandLineOptions.DgmlLogFileName != null)
736                     compilation.WriteDependencyLog(_commandLineOptions.DgmlLogFileName);
737
738                 compilation.Dispose();
739             }
740         }
741
742         private void CheckManagedCppInputFiles(IEnumerable<string> inputPaths)
743         {
744             foreach (string inputFilePath in inputPaths)
745             {
746                 EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFilePath);
747                 if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0)
748                 {
749                     throw new CommandLineException(string.Format(SR.ManagedCppNotSupported, inputFilePath));
750                 }
751             }
752         }
753
754         private TypeDesc FindType(CompilerTypeSystemContext context, string typeName)
755         {
756             ModuleDesc systemModule = context.SystemModule;
757
758             TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) =>
759             {
760                 return (MetadataType)context.GetCanonType(typeDefName)
761                     ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound);
762             });
763             if (foundType == null)
764                 throw new CommandLineException(string.Format(SR.TypeNotFound, typeName));
765
766             return foundType;
767         }
768
769         private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context)
770         {
771             if (_commandLineOptions.SingleMethodName == null && _commandLineOptions.SingleMethodTypeName == null && _commandLineOptions.SingleMethodGenericArg == null)
772                 return null;
773
774             if (_commandLineOptions.SingleMethodName == null || _commandLineOptions.SingleMethodTypeName == null)
775                 throw new CommandLineException(SR.TypeAndMethodNameNeeded);
776
777             TypeDesc owningType = FindType(context, _commandLineOptions.SingleMethodTypeName);
778
779             // TODO: allow specifying signature to distinguish overloads
780             MethodDesc method = null;
781             bool printMethodList = false;
782             int curIndex = 0;
783             foreach (var searchMethod in owningType.GetMethods())
784             {
785                 if (searchMethod.Name != _commandLineOptions.SingleMethodName)
786                     continue;
787
788                 curIndex++;
789                 if (_commandLineOptions.SingleMethodIndex != 0)
790                 {
791                     if (curIndex == _commandLineOptions.SingleMethodIndex)
792                     {
793                         method = searchMethod;
794                         break;
795                     }
796                 }
797                 else
798                 {
799                     if (method == null)
800                     {
801                         method = searchMethod;
802                     }
803                     else
804                     {
805                         printMethodList = true;
806                     }
807                 }
808             }
809
810             if (printMethodList)
811             {
812                 curIndex = 0;
813                 foreach (var searchMethod in owningType.GetMethods())
814                 {
815                     if (searchMethod.Name != _commandLineOptions.SingleMethodName)
816                         continue;
817
818                     curIndex++;
819                     Console.WriteLine($"{curIndex} - {searchMethod}");
820                 }
821                 throw new CommandLineException(SR.SingleMethodIndexNeeded);
822             }
823
824             if (method == null)
825                 throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName));
826
827             if (method.HasInstantiation != (_commandLineOptions.SingleMethodGenericArg != null) ||
828                 (method.HasInstantiation && (method.Instantiation.Length != _commandLineOptions.SingleMethodGenericArg.Count)))
829             {
830                 throw new CommandLineException(
831                     string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName));
832             }
833
834             if (method.HasInstantiation)
835             {
836                 List<TypeDesc> genericArguments = new List<TypeDesc>();
837                 foreach (var argString in _commandLineOptions.SingleMethodGenericArg)
838                     genericArguments.Add(FindType(context, argString));
839                 method = method.MakeInstantiatedMethod(genericArguments.ToArray());
840             }
841
842             return method;
843         }
844
845         private static string CreateReproArgumentString(MethodDesc method)
846         {
847             StringBuilder sb = new StringBuilder();
848
849             var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)method.Context.SystemModule);
850
851             sb.Append($"--singlemethodtypename \"{formatter.FormatName(method.OwningType, true)}\"");
852             sb.Append($" --singlemethodname \"{method.Name}\"");
853             {
854                 int curIndex = 0;
855                 foreach (var searchMethod in method.OwningType.GetMethods())
856                 {
857                     if (searchMethod.Name != method.Name)
858                         continue;
859
860                     curIndex++;
861                     if (searchMethod == method.GetMethodDefinition())
862                     {
863                         sb.Append($" --singlemethodindex {curIndex}");
864                     }
865                 }
866             }
867
868             for (int i = 0; i < method.Instantiation.Length; i++)
869                 sb.Append($" --singlemethodgenericarg \"{formatter.FormatName(method.Instantiation[i], true)}\"");
870
871             return sb.ToString();
872         }
873
874         private static bool DumpReproArguments(CodeGenerationFailedException ex)
875         {
876             Console.WriteLine(SR.DumpReproInstructions);
877
878             MethodDesc failingMethod = ex.Method;
879             Console.WriteLine(CreateReproArgumentString(failingMethod));
880             return false;
881         }
882
883         private enum AlgorithmClass
884         {
885             Signature = 1,
886             Hash = 4,
887         }
888
889         private enum AlgorithmSubId
890         {
891             Sha1Hash = 4,
892             MacHash = 5,
893             RipeMdHash = 6,
894             RipeMd160Hash = 7,
895             Ssl3ShaMD5Hash = 8,
896             HmacHash = 9,
897             Tls1PrfHash = 10,
898             HashReplacOwfHash = 11,
899             Sha256Hash = 12,
900             Sha384Hash = 13,
901             Sha512Hash = 14,
902         }
903
904         private struct AlgorithmId
905         {
906             // From wincrypt.h
907             private const int AlgorithmClassOffset = 13;
908             private const int AlgorithmClassMask = 0x7;
909             private const int AlgorithmSubIdOffset = 0;
910             private const int AlgorithmSubIdMask = 0x1ff;
911
912             private readonly uint _flags;
913
914             public const int RsaSign = 0x00002400;
915             public const int Sha = 0x00008004;
916
917             public bool IsSet
918             {
919                 get { return _flags != 0; }
920             }
921
922             public AlgorithmClass Class
923             {
924                 get { return (AlgorithmClass)((_flags >> AlgorithmClassOffset) & AlgorithmClassMask); }
925             }
926
927             public AlgorithmSubId SubId
928             {
929                 get { return (AlgorithmSubId)((_flags >> AlgorithmSubIdOffset) & AlgorithmSubIdMask); }
930             }
931
932             public AlgorithmId(uint flags)
933             {
934                 _flags = flags;
935             }
936         }
937
938         private static readonly ImmutableArray<byte> s_ecmaKey = ImmutableArray.Create(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 });
939
940         private const int SnPublicKeyBlobSize = 13;
941
942         // From wincrypt.h
943         private const byte PublicKeyBlobId = 0x06;
944         private const byte PrivateKeyBlobId = 0x07;
945
946         // internal for testing
947         internal const int s_publicKeyHeaderSize = SnPublicKeyBlobSize - 1;
948
949         // From StrongNameInternal.cpp
950         // Checks to see if a public key is a valid instance of a PublicKeyBlob as
951         // defined in StongName.h
952         internal static bool IsValidPublicKey(ImmutableArray<byte> blob)
953         {
954             // The number of public key bytes must be at least large enough for the header and one byte of data.
955             if (blob.IsDefault || blob.Length < s_publicKeyHeaderSize + 1)
956             {
957                 return false;
958             }
959
960             var blobReader = new BinaryReader(new MemoryStream(blob.ToArray()));
961
962             // Signature algorithm ID
963             var sigAlgId = blobReader.ReadUInt32();
964             // Hash algorithm ID
965             var hashAlgId = blobReader.ReadUInt32();
966             // Size of public key data in bytes, not including the header
967             var publicKeySize = blobReader.ReadUInt32();
968             // publicKeySize bytes of public key data
969             var publicKey = blobReader.ReadByte();
970
971             // The number of public key bytes must be the same as the size of the header plus the size of the public key data.
972             if (blob.Length != s_publicKeyHeaderSize + publicKeySize)
973             {
974                 return false;
975             }
976
977             // Check for the ECMA key, which does not obey the invariants checked below.
978             if (System.Linq.Enumerable.SequenceEqual(blob, s_ecmaKey))
979             {
980                 return true;
981             }
982
983             // The public key must be in the wincrypto PUBLICKEYBLOB format
984             if (publicKey != PublicKeyBlobId)
985             {
986                 return false;
987             }
988
989             var signatureAlgorithmId = new AlgorithmId(sigAlgId);
990             if (signatureAlgorithmId.IsSet && signatureAlgorithmId.Class != AlgorithmClass.Signature)
991             {
992                 return false;
993             }
994
995             var hashAlgorithmId = new AlgorithmId(hashAlgId);
996             if (hashAlgorithmId.IsSet && (hashAlgorithmId.Class != AlgorithmClass.Hash || hashAlgorithmId.SubId < AlgorithmSubId.Sha1Hash))
997             {
998                 return false;
999             }
1000
1001             return true;
1002         }
1003
1004
1005         private static int Main(string[] args)
1006         {
1007 #if DEBUG
1008             try
1009             {
1010                 return new Program().Run(args);
1011             }
1012             catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex))
1013             {
1014                 throw new NotSupportedException(); // Unreachable
1015             }
1016 #else
1017             try
1018             {
1019                 return new Program().Run(args);
1020             }
1021             catch (Exception e)
1022             {
1023                 Console.Error.WriteLine(string.Format(SR.ProgramError, e.Message));
1024                 Console.Error.WriteLine(e.ToString());
1025                 return 1;
1026             }
1027 #endif
1028
1029         }
1030     }
1031 }