From b0157b1a5074331928ff0e0ee578dac8a648b8e5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tom=C3=A1=C5=A1=20Rylek?= Date: Wed, 19 Feb 2020 11:21:09 +0100 Subject: [PATCH] Initial Crossgen2 changes for composite R2R support (#31663) This change adds the new command-line option "--composite", a new type of input files "--unrooted-input-file" for files that should be made part of the composite image but only rooted as hit by the dependency analysis, not automatically as the normal input files (to reduce the size of framework built along with the app) and new Crossgen2 logic for producing the additional R2R metadata needed by composite images including rewriting of input MSIL by injecting a "component R2R header" on them that forwards the native code to the composite image. With a set of runtime changes out for a separate PR I'm able to make first steps in running composite R2R in the CoreCLR runtime. Thanks Tomas --- .../DependencyAnalysis/SortableDependencyNode.cs | 1 + .../CodeGen/ReadyToRunObjectWriter.cs | 79 +++++++++++-- .../Compiler/CompilationModuleGroup.ReadyToRun.cs | 23 ++++ .../ReadyToRun/AssemblyTableNode.cs | 59 ++++++++++ .../ReadyToRun/AttributePresenceFilterNode.cs | 3 +- .../DependencyAnalysis/ReadyToRun/HeaderNode.cs | 83 +++++++++++--- .../ReadyToRun/InliningInfoNode.cs | 26 +++-- .../ReadyToRun/InstanceEntryPointTableNode.cs | 21 ++-- .../ReadyToRun/ManifestMetadataTableNode.cs | 66 +++++++---- .../ReadyToRun/MethodEntryPointTableNode.cs | 17 ++- .../ReadyToRun/OwnerCompositeExecutableNode.cs | 46 ++++++++ .../ReadyToRun/SignatureContext.cs | 18 ++- .../ReadyToRun/TypesTableNode.cs | 23 +++- .../ReadyToRun/Win32ResourcesNode.cs | 18 ++- .../ReadyToRunCodegenNodeFactory.cs | 120 ++++++++++--------- .../Compiler/ReadyToRunCodegenCompilation.cs | 80 ++++++++++--- .../ReadyToRunCodegenCompilationBuilder.cs | 47 +++----- .../ReadyToRunCompilationModuleGroupBase.cs | 17 ++- ...adyToRunSingleAssemblyCompilationModuleGroup.cs | 10 +- .../Compiler/ReadyToRunTableManager.cs | 20 +--- .../Compiler/SingleMethodCompilationModuleGroup.cs | 5 +- .../ILCompiler.ReadyToRun/IBC/IBCProfileData.cs | 8 +- .../ILCompiler.ReadyToRun.csproj | 2 + .../ObjectWriter/R2RPEBuilder.cs | 127 +++++++++++---------- .../ObjectWriter/SectionBuilder.cs | 10 +- .../ObjectWriter/TargetExtensions.cs | 42 +++++++ .../ReadyToRunReader.cs | 3 +- .../crossgen2/crossgen2/CommandLineOptions.cs | 12 +- .../src/tools/crossgen2/crossgen2/Program.cs | 75 +++++++++--- .../crossgen2/crossgen2/Properties/Resources.resx | 9 ++ 30 files changed, 782 insertions(+), 288 deletions(-) create mode 100644 src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AssemblyTableNode.cs create mode 100644 src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/OwnerCompositeExecutableNode.cs diff --git a/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index 02ae880..cda380e 100644 --- a/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -59,6 +59,7 @@ namespace ILCompiler.DependencyAnalysis // CorHeaderNode, ReadyToRunHeaderNode, + ReadyToRunAssemblyHeaderNode, ImportSectionsTableNode, ImportSectionNode, MethodEntrypointTableNode, diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs index 02445a1..2da9f24 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection.PortableExecutable; using ILCompiler.DependencyAnalysis.ReadyToRun; @@ -14,6 +15,7 @@ using ILCompiler.PEWriter; using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; using System.Security.Cryptography; namespace ILCompiler.DependencyAnalysis @@ -23,11 +25,33 @@ namespace ILCompiler.DependencyAnalysis /// internal class ReadyToRunObjectWriter { - // Nodefactory for which ObjectWriter is instantiated for. + /// + /// Nodefactory for which ObjectWriter is instantiated for. + /// private readonly NodeFactory _nodeFactory; + + /// + /// Output executable path. + /// private readonly string _objectFilePath; + + /// + /// Set to non-null when rewriting MSIL assemblies during composite R2R build; + /// we basically publish the input assemblies into the composite build output folder + /// using the same ReadyToRunObjectWriter as we're using for emitting the "actual" + /// R2R executable, just in this special mode in which we emit a minimal R2R header + /// with forwarding information pointing at the composite module with native code. + /// + private readonly EcmaModule _componentModule; + + /// + /// Nodes to emit into the output executable as collected by the dependency analysis. + /// private readonly IEnumerable _nodes; - private readonly PEReader _inputPeReader; + + /// + /// True when the executable generator should output a map file. + /// private readonly bool _generateMapFile; #if DEBUG @@ -48,12 +72,12 @@ namespace ILCompiler.DependencyAnalysis Dictionary _previouslyWrittenNodeNames = new Dictionary(); #endif - public ReadyToRunObjectWriter(PEReader inputPeReader, string objectFilePath, IEnumerable nodes, NodeFactory factory, bool generateMapFile) + public ReadyToRunObjectWriter(string objectFilePath, EcmaModule componentModule, IEnumerable nodes, NodeFactory factory, bool generateMapFile) { _objectFilePath = objectFilePath; + _componentModule = componentModule; _nodes = nodes; _nodeFactory = factory; - _inputPeReader = inputPeReader; _generateMapFile = generateMapFile; } @@ -79,10 +103,40 @@ namespace ILCompiler.DependencyAnalysis if (mapFile != null) mapFile.WriteLine($@"R2R object emission started: {DateTime.Now}"); + PEHeaderBuilder headerBuilder; + int timeDateStamp; + ISymbolNode r2rHeaderExportSymbol; + + if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) + { + headerBuilder = PEHeaderProvider.Create( + imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll, + dllCharacteristics: default(DllCharacteristics), + Subsystem.Unknown, + _nodeFactory.Target); + // TODO: generate a non-zero timestamp: https://github.com/dotnet/runtime/issues/32507 + timeDateStamp = 0; + r2rHeaderExportSymbol = _nodeFactory.Header; + } + else + { + PEReader inputPeReader = (_componentModule != null ? _componentModule.PEReader : _nodeFactory.CompilationModuleGroup.CompilationModuleSet.First().PEReader); + headerBuilder = PEHeaderProvider.Copy(inputPeReader.PEHeaders, _nodeFactory.Target); + timeDateStamp = inputPeReader.PEHeaders.CoffHeader.TimeDateStamp; + r2rHeaderExportSymbol = null; + } + + Func getRuntimeFunctionsTable = null; + if (_componentModule == null) + { + getRuntimeFunctionsTable = GetRuntimeFunctionsTable; + } R2RPEBuilder r2rPeBuilder = new R2RPEBuilder( _nodeFactory.Target, - _inputPeReader, - GetRuntimeFunctionsTable); + headerBuilder, + r2rHeaderExportSymbol, + Path.GetFileName(_objectFilePath), + getRuntimeFunctionsTable); NativeDebugDirectoryEntryNode nativeDebugDirectoryEntryNode = null; @@ -131,8 +185,11 @@ namespace ILCompiler.DependencyAnalysis EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, mapFile); } - r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size); - r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size); + if (!_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode || _componentModule != null) + { + r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size); + r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size); + } if (_nodeFactory.Win32ResourcesNode != null) { @@ -142,7 +199,7 @@ namespace ILCompiler.DependencyAnalysis using (var peStream = File.Create(_objectFilePath)) { - r2rPeBuilder.Write(peStream); + r2rPeBuilder.Write(peStream, timeDateStamp); // Compute MD5 hash of the output image and store that in the native DebugDirectory entry using (var md5Hash = MD5.Create()) @@ -229,10 +286,10 @@ namespace ILCompiler.DependencyAnalysis r2rPeBuilder.AddObjectData(data, section, name, mapFile); } - public static void EmitObject(PEReader inputPeReader, string objectFilePath, IEnumerable nodes, NodeFactory factory, bool generateMapFile) + public static void EmitObject(string objectFilePath, EcmaModule componentModule, IEnumerable nodes, NodeFactory factory, bool generateMapFile) { Console.WriteLine($@"Emitting R2R PE file: {objectFilePath}"); - ReadyToRunObjectWriter objectWriter = new ReadyToRunObjectWriter(inputPeReader, objectFilePath, nodes, factory, generateMapFile); + ReadyToRunObjectWriter objectWriter = new ReadyToRunObjectWriter(objectFilePath, componentModule, nodes, factory, generateMapFile); objectWriter.EmitPortableExecutable(); } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs index 158188c..83fc679 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs @@ -5,6 +5,9 @@ using Internal.TypeSystem; using ILCompiler.DependencyAnalysis.ReadyToRun; using Internal.ReadyToRunConstants; +using Internal.TypeSystem.Ecma; +using System.Collections.Generic; +using System.Linq; namespace ILCompiler { @@ -58,5 +61,25 @@ namespace ILCompiler /// Gets the flags to be stored in the generated ReadyToRun module header. /// public abstract ReadyToRunFlags GetReadyToRunFlags(); + + /// + /// When set to true, unconditionally add module overrides to all signatures. This is needed in composite + /// build mode so that import cells and instance entry point table are caller module-agnostic. + /// + public bool EnforceOwningType(EcmaModule module) + { + return IsCompositeBuildMode || module != CompilationModuleSet.Single(); + } + + /// + /// Returns true when the compiler is running in composite build mode i.e. building an arbitrary number of + /// input MSIL assemblies into a single output R2R binary. + /// + public abstract bool IsCompositeBuildMode { get; } + + /// + /// List of input modules to use for the compilation. + /// + public abstract IEnumerable CompilationModuleSet { get; } } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AssemblyTableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AssemblyTableNode.cs new file mode 100644 index 0000000..9a0bdb2 --- /dev/null +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AssemblyTableNode.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection.Metadata.Ecma335; + +using Internal.JitInterface; +using Internal.NativeFormat; +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + public class AssemblyTableNode : HeaderTableNode + { + private readonly List _assemblyHeaders; + + public AssemblyTableNode(TargetDetails target) + : base(target) + { + _assemblyHeaders = new List(); + } + + public void Add(AssemblyHeaderNode componentAssemblyHeader) + { + _assemblyHeaders.Add(componentAssemblyHeader); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append("__ReadyToRunAssemblyTable"); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + foreach (AssemblyHeaderNode assemblyHeader in _assemblyHeaders) + { + // TODO: IMAGE_DATA_DIRECTORY CorHeader - no support for embedded MSIL yet + builder.EmitInt(0); + builder.EmitInt(0); + // IMAGE_DATA_DIRECTORY ReadyToRunHeader + builder.EmitReloc(assemblyHeader, RelocType.IMAGE_REL_BASED_ADDR32NB); + builder.EmitReloc(assemblyHeader, RelocType.IMAGE_REL_SYMBOL_SIZE); + } + return builder.ToObjectData(); + } + + public override int ClassCode => 513314416; + } +} diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs index b7dc1a0..eef7490 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs @@ -29,7 +29,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append("__ReadyToRunAttributePresenceFilter"); + sb.Append("__ReadyToRunAttributePresenceFilter__"); + sb.Append(_module.Assembly.GetName().Name); } private struct CustomAttributeEntry diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs index 54ba55d..f157900 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs @@ -42,7 +42,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun } } - public class HeaderNode : ObjectNode, ISymbolDefinitionNode + public abstract class HeaderNode : ObjectNode, ISymbolDefinitionNode { struct HeaderItem { @@ -73,16 +73,15 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun _items.Add(new HeaderItem(id, node, startSymbol)); } - public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) - { - sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append("__ReadyToRunHeader"); - } public int Offset => 0; public override bool IsShareable => false; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + protected abstract void AppendMangledHeaderName(NameMangler nameMangler, Utf8StringBuilder sb); + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) => AppendMangledHeaderName(nameMangler, sb); + public override bool StaticDependenciesAreComputed => true; public override ObjectNodeSection Section @@ -102,23 +101,18 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun builder.RequireInitialPointerAlignment(); builder.AddSymbol(this); + EmitHeaderPrefix(ref builder); + // Don't bother sorting if we're not emitting the contents if (!relocsOnly) _items.Sort((x, y) => Comparer.Default.Compare((int)x.Id, (int)y.Id)); - // ReadyToRunHeader.Magic - builder.EmitInt((int)(ReadyToRunHeaderConstants.Signature)); - - // ReadyToRunHeader.MajorVersion - builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMajorVersion)); - builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMinorVersion)); - // ReadyToRunHeader.Flags builder.EmitInt((int)_flags); // ReadyToRunHeader.NumberOfSections ObjectDataBuilder.Reservation sectionCountReservation = builder.ReserveInt(); - + int count = 0; foreach (var item in _items) { @@ -127,20 +121,75 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun continue; builder.EmitInt((int)item.Id); - + builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_BASED_ADDR32NB); builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_SYMBOL_SIZE); - + count++; } builder.EmitInt(sectionCountReservation, count); - + return builder.ToObjectData(); } + protected abstract void EmitHeaderPrefix(ref ObjectDataBuilder builder); + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + } + + public class GlobalHeaderNode : HeaderNode + { + public GlobalHeaderNode(TargetDetails target, ReadyToRunFlags flags) + : base(target, flags) + { + } + + protected override void AppendMangledHeaderName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append("__ReadyToRunHeader"); + } + + protected override void EmitHeaderPrefix(ref ObjectDataBuilder builder) + { + // ReadyToRunHeader.Magic + builder.EmitInt((int)(ReadyToRunHeaderConstants.Signature)); + + // ReadyToRunHeader.MajorVersion + builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMajorVersion)); + builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMinorVersion)); + } public override int ClassCode => (int)ObjectNodeOrder.ReadyToRunHeaderNode; } + + public class AssemblyHeaderNode : HeaderNode + { + private readonly int _index; + + public AssemblyHeaderNode(TargetDetails target, ReadyToRunFlags flags, int index) + : base(target, flags) + { + _index = index; + } + + protected override void EmitHeaderPrefix(ref ObjectDataBuilder builder) + { + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _index - ((AssemblyHeaderNode)other)._index; + } + + protected override void AppendMangledHeaderName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append("__ReadyToRunAssemblyHeader__"); + sb.Append(_index.ToString()); + } + + public override int ClassCode => (int)ObjectNodeOrder.ReadyToRunAssemblyHeaderNode; + } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs index f1c5696..89eb57a 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs @@ -20,18 +20,19 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun /// public class InliningInfoNode : HeaderTableNode { - private readonly EcmaModule _globalContext; + private readonly EcmaModule _module; - public InliningInfoNode(TargetDetails target, EcmaModule globalContext) + public InliningInfoNode(TargetDetails target, EcmaModule module) : base(target) { - _globalContext = globalContext; + _module = module; } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append("__ReadyToRunInliningInfoTable"); + sb.Append("__ReadyToRunInliningInfoTable__"); + sb.Append(_module.Assembly.GetName().Name); } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -48,7 +49,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { MethodDesc[] inlinees = methodNode.InlinedMethods; MethodDesc inliner = methodNode.Method; - MethodDesc inlinerDefinition = inliner.GetTypicalMethodDefinition(); + EcmaMethod inlinerDefinition = (EcmaMethod)inliner.GetTypicalMethodDefinition(); + if (inlinerDefinition.Module != _module) + { + // Only encode inlining info for inliners within the active module + continue; + } foreach (MethodDesc inlinee in inlinees) { @@ -91,7 +97,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun var sig = new VertexSequence(); - bool isForeignInlinee = inlinee.Module != _globalContext; + bool isForeignInlinee = inlinee.Module != _module; sig.Append(new UnsignedConstant((uint)(inlineeRid << 1 | (isForeignInlinee ? 1 : 0)))); if (isForeignInlinee) { @@ -123,7 +129,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun int ridDelta = inlinerRid - baseRid; baseRid = inlinerRid; Debug.Assert(ridDelta >= 0); - bool isForeignInliner = inliner.Module != _globalContext; + bool isForeignInliner = inliner.Module != _module; sig.Append(new UnsignedConstant((uint)(ridDelta << 1 | (isForeignInliner ? 1 : 0)))); if (isForeignInliner) { @@ -144,6 +150,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun definedSymbols: new ISymbolDefinitionNode[] { this }); } + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + InliningInfoNode otherInliningInfo = (InliningInfoNode)other; + return _module.Assembly.GetName().Name.CompareTo(otherInliningInfo._module.Assembly.GetName().Name); + } + public override int ClassCode => -87382891; } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs index e2e5b21..6bb6ade 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection.Metadata.Ecma335; using Internal.JitInterface; @@ -19,9 +20,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class InstanceEntryPointTableNode : HeaderTableNode { - public InstanceEntryPointTableNode(TargetDetails target) - : base(target) + private readonly NodeFactory _factory; + + public InstanceEntryPointTableNode(NodeFactory factory) + : base(factory.Target) { + _factory = factory; } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) @@ -52,20 +56,17 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { int methodIndex = factory.RuntimeFunctionsTable.GetIndex(method); - bool enforceOwningType = false; - ModuleToken moduleToken = factory.SignatureContext.GetModuleTokenForMethod(method.Method.GetTypicalMethodDefinition()); - if (moduleToken.Module != factory.SignatureContext.GlobalContext) - { - enforceOwningType = true; - } + // In composite R2R format, always enforce owning type to let us share generic instantiations among modules + EcmaMethod typicalMethod = (EcmaMethod)method.Method.GetTypicalMethodDefinition(); + ModuleToken moduleToken = new ModuleToken(typicalMethod.Module, typicalMethod.Handle); ArraySignatureBuilder signatureBuilder = new ArraySignatureBuilder(); signatureBuilder.EmitMethodSignature( new MethodWithToken(method.Method, moduleToken, constrainedType: null), enforceDefEncoding: true, - enforceOwningType, + enforceOwningType: _factory.CompilationModuleGroup.EnforceOwningType(moduleToken.Module), factory.SignatureContext, - isUnboxingStub: false, + isUnboxingStub: false, isInstantiatingStub: false); byte[] signature = signatureBuilder.ToArray(); BlobVertex signatureBlob; diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs index 8774227..73ce2bf 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs @@ -10,6 +10,7 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using Internal.Text; +using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using Debug = System.Diagnostics.Debug; @@ -41,7 +42,6 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun /// private readonly Dictionary _moduleIdToAssemblyNameMap; - /// /// Registered signature emitters. /// @@ -63,36 +63,48 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun private bool _emissionCompleted; /// - /// Name of the input assembly. - /// - private string _inputModuleName; - - /// /// Node factory for the compilation /// private readonly NodeFactory _nodeFactory; - public ManifestMetadataTableNode(EcmaModule inputModule, NodeFactory nodeFactory) - : base(inputModule.Context.Target) + public ManifestMetadataTableNode(NodeFactory nodeFactory) + : base(nodeFactory.Target) { _assemblyRefToModuleIdMap = new Dictionary(); _moduleIdToAssemblyNameMap = new Dictionary(); _signatureEmitters = new List(); _nodeFactory = nodeFactory; - _inputModuleName = inputModule.Assembly.GetName().Name; - - _assemblyRefCount = inputModule.MetadataReader.GetTableRowCount(TableIndex.AssemblyRef); - for (int assemblyRefIndex = 1; assemblyRefIndex <= _assemblyRefCount; assemblyRefIndex++) + if (!_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode) { - AssemblyReferenceHandle assemblyRefHandle = MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex); - AssemblyReference assemblyRef = inputModule.MetadataReader.GetAssemblyReference(assemblyRefHandle); - string assemblyName = inputModule.MetadataReader.GetString(assemblyRef.Name); - _assemblyRefToModuleIdMap[assemblyName] = assemblyRefIndex; + MetadataReader mdReader = _nodeFactory.CompilationModuleGroup.CompilationModuleSet.Single().MetadataReader; + _assemblyRefCount = mdReader.GetTableRowCount(TableIndex.AssemblyRef); + for (int assemblyRefIndex = 1; assemblyRefIndex <= _assemblyRefCount; assemblyRefIndex++) + { + AssemblyReferenceHandle assemblyRefHandle = MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex); + AssemblyReference assemblyRef = mdReader.GetAssemblyReference(assemblyRefHandle); + string assemblyName = mdReader.GetString(assemblyRef.Name); + _assemblyRefToModuleIdMap[assemblyName] = assemblyRefIndex; + } } // AssemblyRefCount + 1 corresponds to ROWID 0 in the manifest metadata _nextModuleId = _assemblyRefCount + 2; + + if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode) + { + // Fill in entries for all input modules right away to make sure they have parallel indices + int nextExpectedId = 2; + foreach (EcmaModule inputModule in _nodeFactory.CompilationModuleGroup.CompilationModuleSet) + { + int acquiredId = ModuleToIndexInternal(inputModule); + if (acquiredId != nextExpectedId) + { + throw new InternalCompilerErrorException($"Manifest metadata consistency error - acquired ID {acquiredId}, expected {nextExpectedId}"); + } + nextExpectedId++; + } + } } public void RegisterEmitter(ISignatureEmitter emitter) @@ -109,6 +121,11 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun throw new InvalidOperationException("Cannot get ModuleToIndex mapping until marking is complete."); } + return ModuleToIndexInternal(module); + } + + private int ModuleToIndexInternal(EcmaModule module) + { AssemblyName assemblyName = module.Assembly.GetName(); int assemblyRefIndex; if (!_assemblyRefToModuleIdMap.TryGetValue(assemblyName.Name, out assemblyRefIndex)) @@ -159,8 +176,9 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun MetadataBuilder metadataBuilder = new MetadataBuilder(); + string manifestMetadataAssemblyName = "ManifestMetadata"; metadataBuilder.AddAssembly( - metadataBuilder.GetOrAddString(_inputModuleName), + metadataBuilder.GetOrAddString(manifestMetadataAssemblyName), new Version(0, 0, 0, 0), culture: default(StringHandle), publicKey: default(BlobHandle), @@ -169,17 +187,17 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun metadataBuilder.AddModule( 0, - metadataBuilder.GetOrAddString(_inputModuleName), + metadataBuilder.GetOrAddString(manifestMetadataAssemblyName), default(GuidHandle), default(GuidHandle), default(GuidHandle)); // Module type metadataBuilder.AddTypeDefinition( - default(TypeAttributes), - default(StringHandle), - metadataBuilder.GetOrAddString(""), - baseType: default(EntityHandle), - fieldList: MetadataTokens.FieldDefinitionHandle(1), - methodList: MetadataTokens.MethodDefinitionHandle(1)); + default(TypeAttributes), + default(StringHandle), + metadataBuilder.GetOrAddString(""), + baseType: default(EntityHandle), + fieldList: MetadataTokens.FieldDefinitionHandle(1), + methodList: MetadataTokens.MethodDefinitionHandle(1)); foreach (var idAndAssemblyName in _moduleIdToAssemblyNameMap.OrderBy(x => x.Key)) { diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs index 6bbfc632..0df4157 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs @@ -18,6 +18,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class MethodEntryPointTableNode : HeaderTableNode { + private readonly EcmaModule _module; + private struct EntryPoint { public static EntryPoint Null = new EntryPoint(-1, null); @@ -34,15 +36,17 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun } } - public MethodEntryPointTableNode(TargetDetails target) + public MethodEntryPointTableNode(EcmaModule module, TargetDetails target) : base(target) { + _module = module; } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append("__ReadyToRunMethodEntryPointTable"); + sb.Append("__ReadyToRunMethodEntryPointTable__"); + sb.Append(_module.Assembly.GetName().Name); } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -56,7 +60,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun foreach (MethodWithGCInfo method in factory.EnumerateCompiledMethods()) { - if (method.Method is EcmaMethod ecmaMethod) + if (method.Method is EcmaMethod ecmaMethod && ecmaMethod.Module == _module) { // Strip away the token type bits, keep just the low 24 bits RID uint rid = SignatureBuilder.RidFromToken((mdToken)MetadataTokens.GetToken(ecmaMethod.Handle)); @@ -112,6 +116,13 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun alignment: 8, definedSymbols: new ISymbolDefinitionNode[] { this }); } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + MethodEntryPointTableNode otherMethodEntryPointTable = (MethodEntryPointTableNode)other; + return _module.Assembly.GetName().Name.CompareTo(otherMethodEntryPointTable._module.Assembly.GetName().Name); + } + protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override int ClassCode => (int)ObjectNodeOrder.MethodEntrypointTableNode; } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/OwnerCompositeExecutableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/OwnerCompositeExecutableNode.cs new file mode 100644 index 0000000..bc79b44 --- /dev/null +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/OwnerCompositeExecutableNode.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + /// + /// R2R header section holding the name of the composite R2R executable with native code + /// for this component module. This section gets put into R2R headers emitted when + /// rewriting input MSIL into standalone MSIL components of a composite R2R image. + /// It is used by the runtime as forwarding information to locate the composite R2R image + /// with the native code for a given MSIL assembly. + /// + internal class OwnerCompositeExecutableNode : HeaderTableNode + { + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override int ClassCode => 240420333; + + private readonly string _ownerExecutableName; + + public OwnerCompositeExecutableNode(TargetDetails target, string ownerExecutableName) + : base(target) + { + _ownerExecutableName = ownerExecutableName; + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__ReadyToRunHeader_OwnerCompositeExecutable"); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.RequireInitialPointerAlignment(); + builder.AddSymbol(this); + builder.EmitBytes(Encoding.UTF8.GetBytes(_ownerExecutableName)); + return builder.ToObjectData(); + } + } +} diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs index 096daf4..901cc90 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs @@ -97,17 +97,31 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun public override int GetHashCode() { - return GlobalContext.GetHashCode() - ^ (LocalContext.GetHashCode() * 31); + return (GlobalContext?.GetHashCode() ?? 0) ^ ((LocalContext?.GetHashCode() ?? 0) * 31); } public int CompareTo(SignatureContext other, TypeSystemComparer comparer) { + if (GlobalContext == null || other.GlobalContext == null) + { + return (GlobalContext != null ? 1 : other.GlobalContext != null ? -1 : 0); + } + int result = GlobalContext.CompareTo(other.GlobalContext); if (result != 0) return result; + if (LocalContext == null || other.LocalContext == null) + { + return (LocalContext != null ? 1 : other.LocalContext != null ? -1 : 0); + } + return LocalContext.CompareTo(other.LocalContext); } + + public override string ToString() + { + return (GlobalContext != null ? GlobalContext.Assembly.GetName().Name : ""); + } } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypesTableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypesTableNode.cs index 26a204e..502da24 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypesTableNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypesTableNode.cs @@ -18,13 +18,19 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class TypesTableNode : HeaderTableNode { - public TypesTableNode(TargetDetails target) - : base(target) {} + private readonly EcmaModule _module; + + public TypesTableNode(TargetDetails target, EcmaModule module) + : base(target) + { + _module = module; + } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append("__ReadyToRunAvailableTypesTable"); + sb.Append("__ReadyToRunAvailableTypesTable__"); + sb.Append(_module.Assembly.GetName().Name); } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -40,8 +46,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun section.Place(typesHashtable); ReadyToRunTableManager r2rManager = (ReadyToRunTableManager)factory.MetadataManager; - - foreach (TypeInfo defTypeInfo in r2rManager.GetDefinedTypes()) + foreach (TypeInfo defTypeInfo in r2rManager.GetDefinedTypes(_module)) { TypeDefinitionHandle defTypeHandle = defTypeInfo.Handle; int hashCode = 0; @@ -60,7 +65,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun typesHashtable.Append(unchecked((uint)hashCode), section.Place(new UnsignedConstant(((uint)MetadataTokens.GetRowNumber(defTypeInfo.Handle) << 1) | 0))); } - foreach (TypeInfo expTypeInfo in r2rManager.GetExportedTypes()) + foreach (TypeInfo expTypeInfo in r2rManager.GetExportedTypes(_module)) { ExportedTypeHandle expTypeHandle = expTypeInfo.Handle; int hashCode = 0; @@ -90,6 +95,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun definedSymbols: new ISymbolDefinitionNode[] { this }); } + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + TypesTableNode otherTypesTable = (TypesTableNode)other; + return _module.Assembly.GetName().Name.CompareTo(otherTypesTable._module.Assembly.GetName().Name); + } + public override int ClassCode => -944318825; } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs index bfaff8c..ea37c34 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs @@ -17,6 +17,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun public Win32ResourcesNode(ResourceData resourceData) { _resourceData = resourceData; + _size = -1; } public override ObjectNodeSection Section => ObjectNodeSection.TextSection; @@ -36,6 +37,11 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { + return GetDataInternal(); + } + + private ObjectData GetDataInternal() + { ObjectDataBuilder builder = new ObjectDataBuilder(); builder.AddSymbol(this); _resourceData.WriteResources(this, ref builder); @@ -48,6 +54,16 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun return "____Win32Resources"; } - public int Size => _size; + public int Size + { + get + { + if (_size < 0) + { + GetDataInternal(); + } + return _size; + } + } } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index c962c66..6a3e4ef 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using ILCompiler.DependencyAnalysis.ReadyToRun; using ILCompiler.DependencyAnalysisFramework; @@ -68,6 +69,7 @@ namespace ILCompiler.DependencyAnalysis public IMethodNode MethodEntrypoint(MethodDesc method) { + EcmaModule module = ((EcmaMethod)method.GetTypicalMethodDefinition()).Module; ModuleToken moduleToken = Resolver.GetModuleTokenForMethod(method, throwIfNotFound: true); return MethodEntrypoint( new MethodWithToken(method, moduleToken, constrainedType: null), @@ -174,28 +176,33 @@ namespace ILCompiler.DependencyAnalysis CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, NameMangler nameMangler, - ModuleTokenResolver moduleTokenResolver, - SignatureContext signatureContext, CopiedCorHeaderNode corHeaderNode, DebugDirectoryNode debugDirectoryNode, ResourceData win32Resources, - AttributePresenceFilterNode attributePresenceFilterNode, - HeaderNode headerNode) + ReadyToRunFlags flags) { TypeSystemContext = context; CompilationModuleGroup = compilationModuleGroup; Target = context.Target; NameMangler = nameMangler; MetadataManager = new ReadyToRunTableManager(context); - Resolver = moduleTokenResolver; - SignatureContext = signatureContext; CopiedCorHeaderNode = corHeaderNode; DebugDirectoryNode = debugDirectoryNode; - AttributePresenceFilter = attributePresenceFilterNode; - Header = headerNode; + Resolver = new ModuleTokenResolver(compilationModuleGroup, TypeSystemContext); + Header = new GlobalHeaderNode(Target, flags); if (!win32Resources.IsEmpty) Win32ResourcesNode = new Win32ResourcesNode(win32Resources); + if (CompilationModuleGroup.IsCompositeBuildMode) + { + // Create a null top-level signature context to force producing module overrides for all signaturess + SignatureContext = new SignatureContext(null, Resolver); + } + else + { + SignatureContext = new SignatureContext(CompilationModuleGroup.CompilationModuleSet.Single(), Resolver); + } + CreateNodeCaches(); } @@ -240,9 +247,9 @@ namespace ILCompiler.DependencyAnalysis _importMethods = new NodeCache(CreateMethodEntrypoint); - _localMethodCache = new NodeCache(key => + _localMethodCache = new NodeCache(key => { - return new MethodWithGCInfo(key.Method.Method); + return new MethodWithGCInfo(key); }); _methodSignatures = new NodeCache(key => @@ -330,7 +337,7 @@ namespace ILCompiler.DependencyAnalysis public Win32ResourcesNode Win32ResourcesNode; - public HeaderNode Header; + public GlobalHeaderNode Header; public RuntimeFunctionsTableNode RuntimeFunctionsTable; @@ -338,14 +345,10 @@ namespace ILCompiler.DependencyAnalysis public ProfileDataSectionNode ProfileDataSection; - public MethodEntryPointTableNode MethodEntryPointTable; - public InstanceEntryPointTableNode InstanceEntryPointTable; public ManifestMetadataTableNode ManifestMetadataTable; - public TypesTableNode TypesTable; - public ImportSectionsTableNode ImportSectionsTable; public Import ModuleImport; @@ -356,10 +359,6 @@ namespace ILCompiler.DependencyAnalysis public DebugInfoTableNode DebugInfoTable; - public InliningInfoNode InliningInfoTable; - - public AttributePresenceFilterNode AttributePresenceFilter; - public ImportSectionNode EagerImports; public ImportSectionNode MethodImports; @@ -428,20 +427,14 @@ namespace ILCompiler.DependencyAnalysis return _importMethods.GetOrAdd(key); } - private NodeCache _localMethodCache = new NodeCache(); + private NodeCache _localMethodCache; private MethodWithGCInfo CreateMethodEntrypointNodeHelper(MethodWithToken targetMethod) { Debug.Assert(CompilationModuleGroup.ContainsMethodBody(targetMethod.Method, false)); MethodDesc localMethod = targetMethod.Method.GetCanonMethodTarget(CanonicalFormKind.Specific); - - TypeAndMethod localMethodKey = new TypeAndMethod(localMethod.OwningType, - new MethodWithToken(localMethod, default(ModuleToken), constrainedType: null), - isUnboxingStub: false, - isInstantiatingStub: false, - isPrecodeImportRequired: false); - return _localMethodCache.GetOrAdd(localMethodKey); + return _localMethodCache.GetOrAdd(localMethod); } public IEnumerable EnumerateCompiledMethods() @@ -454,9 +447,9 @@ namespace ILCompiler.DependencyAnalysis { methodCodeNode = localMethodImport.MethodCodeNode; } - if (methodCodeNode == null && methodNode is PrecodeMethodImport PrecodeMethodImport) + if (methodCodeNode == null && methodNode is PrecodeMethodImport precodeMethodImport) { - methodCodeNode = PrecodeMethodImport.MethodCodeNode; + methodCodeNode = precodeMethodImport.MethodCodeNode; } if (methodCodeNode != null && !methodCodeNode.IsEmpty) @@ -558,19 +551,53 @@ namespace ILCompiler.DependencyAnalysis Header.Add(Internal.Runtime.ReadyToRunSectionType.ExceptionInfo, exceptionInfoLookupTableNode, exceptionInfoLookupTableNode); graph.AddRoot(exceptionInfoLookupTableNode, "ExceptionInfoLookupTable is always generated"); - MethodEntryPointTable = new MethodEntryPointTableNode(Target); - Header.Add(Internal.Runtime.ReadyToRunSectionType.MethodDefEntryPoints, MethodEntryPointTable, MethodEntryPointTable); - - ManifestMetadataTable = new ManifestMetadataTableNode(SignatureContext.GlobalContext, this); + ManifestMetadataTable = new ManifestMetadataTableNode(this); Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable); - Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex); - InstanceEntryPointTable = new InstanceEntryPointTableNode(Target); - Header.Add(Internal.Runtime.ReadyToRunSectionType.InstanceMethodEntryPoints, InstanceEntryPointTable, InstanceEntryPointTable); + AssemblyTableNode assemblyTable = null; + + if (CompilationModuleGroup.CompilationModuleSet.Skip(1).Any()) + { + assemblyTable = new AssemblyTableNode(Target); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ComponentAssemblies, assemblyTable, assemblyTable); + } + + // Generate per assembly header tables + int assemblyIndex = -1; + foreach (EcmaModule inputModule in CompilationModuleGroup.CompilationModuleSet) + { + assemblyIndex++; + HeaderNode tableHeader = Header; + if (assemblyTable != null) + { + AssemblyHeaderNode perAssemblyHeader = new AssemblyHeaderNode(Target, ReadyToRunFlags.READYTORUN_FLAG_Component, assemblyIndex); + assemblyTable.Add(perAssemblyHeader); + tableHeader = perAssemblyHeader; + } - TypesTable = new TypesTableNode(Target); - Header.Add(Internal.Runtime.ReadyToRunSectionType.AvailableTypes, TypesTable, TypesTable); + MethodEntryPointTableNode methodEntryPointTable = new MethodEntryPointTableNode(inputModule, Target); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodDefEntryPoints, methodEntryPointTable, methodEntryPointTable); + + TypesTableNode typesTable = new TypesTableNode(Target, inputModule); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AvailableTypes, typesTable, typesTable); + + InliningInfoNode inliningInfoTable = new InliningInfoNode(Target, inputModule); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.InliningInfo2, inliningInfoTable, inliningInfoTable); + + // Core library attributes are checked FAR more often than other dlls + // attributes, so produce a highly efficient table for determining if they are + // present. Other assemblies *MAY* benefit from this feature, but it doesn't show + // as useful at this time. + if (inputModule == TypeSystemContext.SystemModule) + { + AttributePresenceFilterNode attributePresenceTable = new AttributePresenceFilterNode(inputModule); + Header.Add(Internal.Runtime.ReadyToRunSectionType.AttributePresence, attributePresenceTable, attributePresenceTable); + } + } + + InstanceEntryPointTable = new InstanceEntryPointTableNode(this); + Header.Add(Internal.Runtime.ReadyToRunSectionType.InstanceMethodEntryPoints, InstanceEntryPointTable, InstanceEntryPointTable); ImportSectionsTable = new ImportSectionsTableNode(this); Header.Add(Internal.Runtime.ReadyToRunSectionType.ImportSections, ImportSectionsTable, ImportSectionsTable.StartSymbol); @@ -578,18 +605,6 @@ namespace ILCompiler.DependencyAnalysis DebugInfoTable = new DebugInfoTableNode(Target); Header.Add(Internal.Runtime.ReadyToRunSectionType.DebugInfo, DebugInfoTable, DebugInfoTable); - InliningInfoTable = new InliningInfoNode(Target, SignatureContext.GlobalContext); - Header.Add(Internal.Runtime.ReadyToRunSectionType.InliningInfo2, InliningInfoTable, InliningInfoTable); - - // Core library attributes are checked FAR more often than other dlls - // attributes, so produce a highly efficient table for determining if they are - // present. Other assemblies *MAY* benefit from this feature, but it doesn't show - // as useful at this time. - if (this.AttributePresenceFilter != null) - { - Header.Add(Internal.Runtime.ReadyToRunSectionType.AttributePresence, AttributePresenceFilter, AttributePresenceFilter); - } - EagerImports = new ImportSectionNode( "EagerImports", CorCompileImportType.CORCOMPILE_IMPORT_TYPE_UNKNOWN, @@ -673,7 +688,10 @@ namespace ILCompiler.DependencyAnalysis graph.AddRoot(PrecodeImports, "Precode helper imports are always generated"); graph.AddRoot(StringImports, "String imports are always generated"); graph.AddRoot(Header, "ReadyToRunHeader is always generated"); - graph.AddRoot(CopiedCorHeaderNode, "MSIL COR header is always generated"); + if (!CompilationModuleGroup.IsCompositeBuildMode) + { + graph.AddRoot(CopiedCorHeaderNode, "MSIL COR header is always generated for single-file R2R files"); + } graph.AddRoot(DebugDirectoryNode, "Debug Directory will always contain at least one entry"); if (Win32ResourcesNode != null) diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 8359e93..03be7ba 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -18,6 +18,8 @@ using Internal.TypeSystem; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.ReadyToRun; using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem.Ecma; +using System.Linq; namespace ILCompiler { @@ -187,9 +189,9 @@ namespace ILCompiler private readonly ConditionalWeakTable _corInfoImpls; /// - /// Name of the compilation input MSIL file. + /// Input MSIL file names. /// - private readonly string _inputFilePath; + private readonly IEnumerable _inputFiles; private bool _resilient; @@ -206,40 +208,86 @@ namespace ILCompiler ILProvider ilProvider, Logger logger, DevirtualizationManager devirtualizationManager, - string inputFilePath, - IEnumerable modulesBeingInstrumented, + IEnumerable inputFiles, bool resilient, bool generateMapFile, int parallelism) - : base(dependencyGraph, nodeFactory, roots, ilProvider, devirtualizationManager, modulesBeingInstrumented, logger) + : base( + dependencyGraph, + nodeFactory, + roots, + ilProvider, + devirtualizationManager, + modulesBeingInstrumented: nodeFactory.CompilationModuleGroup.CompilationModuleSet, + logger) { _resilient = resilient; _parallelism = parallelism; _generateMapFile = generateMapFile; SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory); - - _inputFilePath = inputFilePath; - _corInfoImpls = new ConditionalWeakTable(); + _inputFiles = inputFiles; } public override void Compile(string outputFile) { - using (FileStream inputFile = File.OpenRead(_inputFilePath)) - { - PEReader inputPeReader = new PEReader(inputFile); + _dependencyGraph.ComputeMarkedNodes(); + var nodes = _dependencyGraph.MarkedNodeList; - _dependencyGraph.ComputeMarkedNodes(); - var nodes = _dependencyGraph.MarkedNodeList; + using (PerfEventSource.StartStopEvents.EmittingEvents()) + { + NodeFactory.SetMarkingComplete(); + ReadyToRunObjectWriter.EmitObject(outputFile, componentModule: null, nodes, NodeFactory, _generateMapFile); + CompilationModuleGroup moduleGroup = _nodeFactory.CompilationModuleGroup; - using (PerfEventSource.StartStopEvents.EmittingEvents()) + if (moduleGroup.IsCompositeBuildMode) { - NodeFactory.SetMarkingComplete(); - ReadyToRunObjectWriter.EmitObject(inputPeReader, outputFile, nodes, NodeFactory, _generateMapFile); + // In composite mode with standalone MSIL we rewrite all input MSIL assemblies to the + // output folder, adding a format R2R header to them with forwarding information to + // the composite executable. + string outputDirectory = Path.GetDirectoryName(outputFile); + string ownerExecutableName = Path.GetFileName(outputFile); + foreach (string inputFile in _inputFiles) + { + string standaloneMsilOutputFile = Path.Combine(outputDirectory, Path.GetFileName(inputFile)); + RewriteComponentFile(inputFile: inputFile, outputFile: standaloneMsilOutputFile, ownerExecutableName: ownerExecutableName); + } } } } + private void RewriteComponentFile(string inputFile, string outputFile, string ownerExecutableName) + { + EcmaModule inputModule = NodeFactory.TypeSystemContext.GetModuleFromPath(inputFile); + + CopiedCorHeaderNode copiedCorHeader = new CopiedCorHeaderNode(inputModule); + DebugDirectoryNode debugDirectory = new DebugDirectoryNode(inputModule); + NodeFactory componentFactory = new NodeFactory( + _nodeFactory.TypeSystemContext, + _nodeFactory.CompilationModuleGroup, + _nodeFactory.NameMangler, + copiedCorHeader, + debugDirectory, + win32Resources: new Win32Resources.ResourceData(inputModule), + Internal.ReadyToRunConstants.ReadyToRunFlags.READYTORUN_FLAG_Component); + + IComparer> comparer = new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer()); + DependencyAnalyzerBase componentGraph = new DependencyAnalyzer, NodeFactory>(componentFactory, comparer); + + componentGraph.AddRoot(componentFactory.Header, "Component module R2R header"); + OwnerCompositeExecutableNode ownerExecutableNode = new OwnerCompositeExecutableNode(_nodeFactory.Target, ownerExecutableName); + componentGraph.AddRoot(ownerExecutableNode, "Owner composite executable name"); + componentGraph.AddRoot(copiedCorHeader, "Copied COR header"); + componentGraph.AddRoot(debugDirectory, "Debug directory"); + if (componentFactory.Win32ResourcesNode != null) + { + componentGraph.AddRoot(componentFactory.Win32ResourcesNode, "Win32 resources"); + } + componentGraph.ComputeMarkedNodes(); + componentFactory.Header.Add(Internal.Runtime.ReadyToRunSectionType.OwnerCompositeExecutable, ownerExecutableNode, ownerExecutableNode); + ReadyToRunObjectWriter.EmitObject(outputFile, componentModule: inputModule, componentGraph.MarkedNodeList, componentFactory, generateMapFile: false); + } + public override void WriteDependencyLog(string outputFileName) { using (FileStream dgmlOutput = new FileStream(outputFileName, FileMode.Create)) diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index 0cec5bf..1fc68ea 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.ReadyToRun; using ILCompiler.DependencyAnalysisFramework; @@ -19,12 +20,12 @@ namespace ILCompiler { public sealed class ReadyToRunCodegenCompilationBuilder : CompilationBuilder { - private readonly string _inputFilePath; - private readonly EcmaModule _inputModule; + private readonly IEnumerable _inputFiles; private bool _ibcTuning; private bool _resilient; private bool _generateMapFile; private int _parallelism; + private string _jitPath; // These need to provide reasonable defaults so that the user can optionally skip @@ -32,11 +33,10 @@ namespace ILCompiler private KeyValuePair[] _ryujitOptions = Array.Empty>(); private ILProvider _ilProvider = new ReadyToRunILProvider(); - public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group, string inputFilePath) + public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group, IEnumerable inputFiles) : base(context, group, new CoreRTNameMangler()) { - _inputFilePath = inputFilePath; - _inputModule = context.GetModuleFromPath(_inputFilePath); + _inputFiles = inputFiles; // R2R field layout needs compilation group information ((ReadyToRunCompilerContext)context).SetCompilationGroup(group); @@ -108,26 +108,17 @@ namespace ILCompiler return this; } - public override ICompilation ToCompilation() { - ModuleTokenResolver moduleTokenResolver = new ModuleTokenResolver(_compilationGroup, _context); - SignatureContext signatureContext = new SignatureContext(_inputModule, moduleTokenResolver); - CopiedCorHeaderNode corHeaderNode = new CopiedCorHeaderNode(_inputModule); - AttributePresenceFilterNode attributePresenceFilterNode = null; - DebugDirectoryNode debugDirectoryNode = new DebugDirectoryNode(_inputModule); - - // Core library attributes are checked FAR more often than other dlls - // attributes, so produce a highly efficient table for determining if they are - // present. Other assemblies *MAY* benefit from this feature, but it doesn't show - // as useful at this time. - if (_inputModule == _inputModule.Context.SystemModule) - { - attributePresenceFilterNode = new AttributePresenceFilterNode(_inputModule); - } + // TODO: only copy COR headers for single-assembly build and for composite build with embedded MSIL + IEnumerable inputModules = _compilationGroup.CompilationModuleSet; + CopiedCorHeaderNode corHeaderNode = (_compilationGroup.IsCompositeBuildMode ? null : new CopiedCorHeaderNode(inputModules.First())); + // TODO: proper support for multiple input files + DebugDirectoryNode debugDirectoryNode = new DebugDirectoryNode(inputModules.First()); // Produce a ResourceData where the IBC PROFILE_DATA entry has been filtered out - ResourceData win32Resources = new ResourceData(_inputModule, (object type, object name, ushort language) => + // TODO: proper support for multiple input files + ResourceData win32Resources = new ResourceData(inputModules.First(), (object type, object name, ushort language) => { if (!(type is string) || !(name is string)) return true; @@ -144,23 +135,20 @@ namespace ILCompiler }); ReadyToRunFlags flags = ReadyToRunFlags.READYTORUN_FLAG_NonSharedPInvokeStubs; - if (_inputModule.IsPlatformNeutral) + if (inputModules.All(module => module.IsPlatformNeutral)) + { flags |= ReadyToRunFlags.READYTORUN_FLAG_PlatformNeutralSource; + } flags |= _compilationGroup.GetReadyToRunFlags(); - var header = new HeaderNode(_context.Target, flags); - NodeFactory factory = new NodeFactory( _context, _compilationGroup, _nameMangler, - moduleTokenResolver, - signatureContext, corHeaderNode, debugDirectoryNode, win32Resources, - attributePresenceFilterNode, - header); + flags); IComparer> comparer = new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer()); DependencyAnalyzerBase graph = CreateDependencyGraph(factory, comparer); @@ -199,8 +187,7 @@ namespace ILCompiler _ilProvider, _logger, new DependencyAnalysis.ReadyToRun.DevirtualizationManager(_compilationGroup), - _inputFilePath, - new ModuleDesc[] { _inputModule }, + _inputFiles, _resilient, _generateMapFile, _parallelism); diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index 19e2e42..d313f1f 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Concurrent; using System.Collections.Generic; using Internal.TypeSystem; @@ -9,27 +10,31 @@ using Internal.TypeSystem.Ecma; using Internal.TypeSystem.Interop; using ILCompiler.DependencyAnalysis.ReadyToRun; using Debug = System.Diagnostics.Debug; -using System; namespace ILCompiler { public abstract class ReadyToRunCompilationModuleGroupBase : CompilationModuleGroup { - protected readonly HashSet _compilationModuleSet; + protected readonly HashSet _compilationModuleSet; private readonly HashSet _versionBubbleModuleSet; private Dictionary _typeRefsInCompilationModuleSet; private readonly bool _compileGenericDependenciesFromVersionBubbleModuleSet; + private readonly bool _isCompositeBuildMode; private readonly ConcurrentDictionary _containsTypeLayoutCache = new ConcurrentDictionary(); private readonly ConcurrentDictionary _versionsWithTypeCache = new ConcurrentDictionary(); private readonly ConcurrentDictionary _versionsWithMethodCache = new ConcurrentDictionary(); public ReadyToRunCompilationModuleGroupBase( TypeSystemContext context, - IEnumerable compilationModuleSet, + bool isCompositeBuildMode, + IEnumerable compilationModuleSet, IEnumerable versionBubbleModuleSet, bool compileGenericDependenciesFromVersionBubbleModuleSet) { - _compilationModuleSet = new HashSet(compilationModuleSet); + _compilationModuleSet = new HashSet(compilationModuleSet); + _isCompositeBuildMode = isCompositeBuildMode; + + Debug.Assert(_isCompositeBuildMode || _compilationModuleSet.Count == 1); _versionBubbleModuleSet = new HashSet(versionBubbleModuleSet); _versionBubbleModuleSet.UnionWith(_compilationModuleSet); @@ -213,6 +218,10 @@ namespace ILCompiler return _typeRefsInCompilationModuleSet.TryGetValue(type, out token); } + public sealed override bool IsCompositeBuildMode => _isCompositeBuildMode; + + public sealed override IEnumerable CompilationModuleSet => _compilationModuleSet; + private bool ComputeTypeVersionsWithCode(TypeDesc type) { if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs index 4991a41..f4e85d1 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using Internal.TypeSystem; + using Internal.ReadyToRunConstants; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; using Debug = System.Diagnostics.Debug; @@ -16,11 +18,13 @@ namespace ILCompiler private bool _profileGuidedCompileRestrictionSet; public ReadyToRunSingleAssemblyCompilationModuleGroup( - TypeSystemContext context, - IEnumerable compilationModuleSet, + TypeSystemContext context, + bool isCompositeBuildMode, + IEnumerable compilationModuleSet, IEnumerable versionBubbleModuleSet, bool compileGenericDependenciesFromVersionBubbleModuleSet) : base(context, + isCompositeBuildMode, compilationModuleSet, versionBubbleModuleSet, compileGenericDependenciesFromVersionBubbleModuleSet) diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs index bef02c8..aac7899 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs @@ -78,27 +78,19 @@ namespace ILCompiler public ReadyToRunTableManager(CompilerTypeSystemContext typeSystemContext) : base(typeSystemContext) {} - public IEnumerable> GetDefinedTypes() + public IEnumerable> GetDefinedTypes(EcmaModule module) { - foreach (string inputFile in _typeSystemContext.InputFilePaths.Values) + foreach (TypeDefinitionHandle typeDefHandle in module.MetadataReader.TypeDefinitions) { - EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFile); - foreach (TypeDefinitionHandle typeDefHandle in module.MetadataReader.TypeDefinitions) - { - yield return new TypeInfo(module.MetadataReader, typeDefHandle); - } + yield return new TypeInfo(module.MetadataReader, typeDefHandle); } } - public IEnumerable> GetExportedTypes() + public IEnumerable> GetExportedTypes(EcmaModule module) { - foreach (string inputFile in _typeSystemContext.InputFilePaths.Values) + foreach (ExportedTypeHandle exportedTypeHandle in module.MetadataReader.ExportedTypes) { - EcmaModule module = _typeSystemContext.GetModuleFromPath(inputFile); - foreach (ExportedTypeHandle exportedTypeHandle in module.MetadataReader.ExportedTypes) - { - yield return new TypeInfo(module.MetadataReader, exportedTypeHandle); - } + yield return new TypeInfo(module.MetadataReader, exportedTypeHandle); } } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs index 5811caf..11c0b2e 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using Internal.ReadyToRunConstants; using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; namespace ILCompiler { @@ -18,11 +19,13 @@ namespace ILCompiler public SingleMethodCompilationModuleGroup( TypeSystemContext context, - IEnumerable compilationModuleSet, + bool isCompositeBuildMode, + IEnumerable compilationModuleSet, IEnumerable versionBubbleModuleSet, bool compileGenericDependenciesFromVersionBubbleModuleSet, MethodDesc method) : base(context, + isCompositeBuildMode, compilationModuleSet, versionBubbleModuleSet, compileGenericDependenciesFromVersionBubbleModuleSet) diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IBC/IBCProfileData.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IBC/IBCProfileData.cs index a51c962..eaf1673 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IBC/IBCProfileData.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IBC/IBCProfileData.cs @@ -15,10 +15,10 @@ namespace ILCompiler.IBC { foreach (MethodProfileData data in methodData) { - if (_methodData.ContainsKey(data.Method)) - throw new Exception("Multiple copies of data for the same method"); - - _methodData.Add(data.Method, data); + if (!_methodData.ContainsKey(data.Method)) + { + _methodData.Add(data.Method, data); + } } _partialNGen = partialNGen; } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 8bdc4f1..65d5ccb 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -99,6 +99,7 @@ + @@ -130,6 +131,7 @@ + diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs index 8b41614..944f8b2 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs @@ -96,19 +96,7 @@ namespace ILCompiler.PEWriter private TargetDetails _target; /// - /// PE reader representing the input MSIL PE file we're copying to the output composite PE file. - /// - private PEReader _peReader; - - /// - /// Custom sections explicitly injected by the caller. - /// - private HashSet _customSections; - - /// - /// Complete list of section names includes the sections present in the input MSIL file - /// (.text, optionally .rsrc and .reloc) and extra questions injected during the R2R PE - /// creation. + /// Complete list of sections to emit into the output R2R executable. /// private ImmutableArray
_sections; @@ -164,16 +152,17 @@ namespace ILCompiler.PEWriter /// Constructor initializes the various control structures and combines the section list. /// /// Target environment specifier - /// Input MSIL PE file reader + /// PE file header builder /// Callback to retrieve the runtime functions table public R2RPEBuilder( TargetDetails target, - PEReader peReader, + PEHeaderBuilder peHeaderBuilder, + ISymbolNode r2rHeaderExportSymbol, + string outputFileSimpleName, Func getRuntimeFunctionsTable) - : base(PEHeaderCopier.Copy(peReader.PEHeaders, target), deterministicIdProvider: null) + : base(peHeaderBuilder, deterministicIdProvider: null) { _target = target; - _peReader = peReader; _getRuntimeFunctionsTable = getRuntimeFunctionsTable; _sectionRvaDeltas = new List(); @@ -182,10 +171,11 @@ namespace ILCompiler.PEWriter _textSectionIndex = _sectionBuilder.AddSection(TextSectionName, SectionCharacteristics.ContainsCode | SectionCharacteristics.MemExecute | SectionCharacteristics.MemRead, 512); _dataSectionIndex = _sectionBuilder.AddSection(DataSectionName, SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemWrite | SectionCharacteristics.MemRead, 512); - _customSections = new HashSet(); - foreach (SectionInfo section in _sectionBuilder.GetSections()) + if (r2rHeaderExportSymbol != null) { - _customSections.Add(section.SectionName); + _sectionBuilder.AddSection(R2RPEBuilder.ExportDataSectionName, SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead, 512); + _sectionBuilder.AddExportSymbol("RTR_HEADER", 1, r2rHeaderExportSymbol); + _sectionBuilder.SetDllNameForExportDirectoryTable(outputFileSimpleName); } if (_sectionBuilder.FindSection(R2RPEBuilder.RelocSectionName) == null) @@ -196,7 +186,7 @@ namespace ILCompiler.PEWriter SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead | SectionCharacteristics.MemDiscardable, - peReader.PEHeaders.PEHeader.SectionAlignment); + PEHeaderConstants.SectionAlignment); } ImmutableArray
.Builder sectionListBuilder = ImmutableArray.CreateBuilder
(); @@ -270,16 +260,18 @@ namespace ILCompiler.PEWriter /// Emit built sections into the R2R PE file. /// /// Output stream for the final R2R PE file - public void Write(Stream outputStream) + /// Timestamp to set in the PE header of the output R2R executable + public void Write(Stream outputStream, int timeDateStamp) { BlobBuilder outputPeFile = new BlobBuilder(); Serialize(outputPeFile); _sectionBuilder.RelocateOutputFile(outputPeFile, Header.ImageBase, outputStream); + UpdateSectionRVAs(outputStream); ApplyMachineOSOverride(outputStream); - CopyTimeStampFromInputImage(outputStream); + SetPEHeaderTimeStamp(outputStream, timeDateStamp); _written = true; } @@ -398,12 +390,13 @@ namespace ILCompiler.PEWriter } /// - /// Copy over the timestamp from IL image for determinism. + /// Set PE header timestamp in the output R2R image to a given value. /// /// Output stream representing the R2R PE executable - private void CopyTimeStampFromInputImage(Stream outputStream) + /// Timestamp to set in the R2R PE header + private void SetPEHeaderTimeStamp(Stream outputStream, int timeDateStamp) { - byte[] patchedTimestamp = BitConverter.GetBytes(_peReader.PEHeaders.CoffHeader.TimeDateStamp); + byte[] patchedTimestamp = BitConverter.GetBytes(timeDateStamp); int seekSize = DosHeaderSize + PESignatureSize + @@ -423,10 +416,13 @@ namespace ILCompiler.PEWriter _sectionBuilder.UpdateDirectories(builder); - RuntimeFunctionsTableNode runtimeFunctionsTable = _getRuntimeFunctionsTable(); - builder.ExceptionTable = new DirectoryEntry( - relativeVirtualAddress: _sectionBuilder.GetSymbolRVA(runtimeFunctionsTable), - size: runtimeFunctionsTable.TableSize); + if (_getRuntimeFunctionsTable != null) + { + RuntimeFunctionsTableNode runtimeFunctionsTable = _getRuntimeFunctionsTable(); + builder.ExceptionTable = new DirectoryEntry( + relativeVirtualAddress: _sectionBuilder.GetSymbolRVA(runtimeFunctionsTable), + size: runtimeFunctionsTable.TableSize); + } return builder; } @@ -549,9 +545,11 @@ namespace ILCompiler.PEWriter } /// - /// Simple helper for copying the various global values in the PE header. + /// Simple helper for filling in PE header information by either copying over + /// data from a pre-existing input PE header (used for single-assembly R2R files) + /// or by explicitly specifying the image characteristics (for composite R2R). /// - static class PEHeaderCopier + static class PEHeaderProvider { /// /// Copy PE headers into a PEHeaderBuilder used by PEBuilder. @@ -560,25 +558,33 @@ namespace ILCompiler.PEWriter /// Target architecture to set in the header public static PEHeaderBuilder Copy(PEHeaders peHeaders, TargetDetails target) { - // Default base addresses used by Roslyn - const ulong DefaultExeBaseAddress64Bit = 0x1_4000_0000; - const ulong DefaultDllBaseAddress64Bit = 0x1_8000_0000; + return Create( + peHeaders.CoffHeader.Characteristics, + peHeaders.PEHeader.DllCharacteristics, + peHeaders.PEHeader.Subsystem, + target); + } + /// + /// Fill in PE header information into a PEHeaderBuilder used by PEBuilder. + /// + /// Relocs are not present in the PE executable + /// Extra DLL characteristics to apply + /// Targeting subsystem + /// Target architecture to set in the header + public static PEHeaderBuilder Create(Characteristics imageCharacteristics, DllCharacteristics dllCharacteristics, Subsystem subsystem, TargetDetails target) + { bool is64BitTarget = target.PointerSize == sizeof(long); - Characteristics imageCharacteristics = peHeaders.CoffHeader.Characteristics; - if (is64BitTarget) - { - imageCharacteristics &= ~Characteristics.Bit32Machine; - imageCharacteristics |= Characteristics.LargeAddressAware; - } + imageCharacteristics &= ~(Characteristics.Bit32Machine | Characteristics.LargeAddressAware); + imageCharacteristics |= (is64BitTarget ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine); - ulong imageBase = peHeaders.PEHeader.ImageBase; + ulong imageBase = PE32HeaderConstants.ImageBase; if (target.IsWindows && is64BitTarget && (imageBase <= uint.MaxValue)) { // Base addresses below 4 GiB are reserved for WoW on x64 and disallowed on ARM64. // If the input assembly was compiled for anycpu, its base address is 32-bit and we need to fix it. - imageBase = (imageCharacteristics & Characteristics.Dll) != 0 ? DefaultDllBaseAddress64Bit : DefaultExeBaseAddress64Bit; + imageBase = (imageCharacteristics & Characteristics.Dll) != 0 ? PE64HeaderConstants.DllImageBase : PE64HeaderConstants.ExeImageBase; } int fileAlignment = 0x200; @@ -596,11 +602,10 @@ namespace ILCompiler.PEWriter sectionAlignment = fileAlignment; } - DllCharacteristics dllCharacteristics = DllCharacteristics.DynamicBase | DllCharacteristics.NxCompatible; + dllCharacteristics &= (DllCharacteristics.NxCompatible | DllCharacteristics.TerminalServerAware | DllCharacteristics.AppContainer); - // Copy over selected DLL characteristics bits from IL image - dllCharacteristics |= peHeaders.PEHeader.DllCharacteristics & - (DllCharacteristics.TerminalServerAware | DllCharacteristics.AppContainer); + // In Crossgen1, this is under a debug-specific condition 'if (0 == CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NoASLRForNgen))' + dllCharacteristics |= DllCharacteristics.DynamicBase; if (is64BitTarget) { @@ -615,23 +620,21 @@ namespace ILCompiler.PEWriter machine: target.MachineFromTarget(), sectionAlignment: sectionAlignment, fileAlignment: fileAlignment, - imageBase: imageBase, - majorLinkerVersion: 11, - minorLinkerVersion: 0, - majorOperatingSystemVersion: 5, - // Win2k = 5.0 for 32-bit images, Win2003 = 5.2 for 64-bit images - minorOperatingSystemVersion: is64BitTarget ? (ushort)2 : (ushort)0, - majorImageVersion: peHeaders.PEHeader.MajorImageVersion, - minorImageVersion: peHeaders.PEHeader.MinorImageVersion, - majorSubsystemVersion: peHeaders.PEHeader.MajorSubsystemVersion, - minorSubsystemVersion: peHeaders.PEHeader.MinorSubsystemVersion, - subsystem: peHeaders.PEHeader.Subsystem, + majorLinkerVersion: PEHeaderConstants.MajorLinkerVersion, + minorLinkerVersion: PEHeaderConstants.MinorLinkerVersion, + majorOperatingSystemVersion: PEHeaderConstants.MajorOperatingSystemVersion, + minorOperatingSystemVersion: PEHeaderConstants.MinorOperatingSystemVersion, + majorImageVersion: PEHeaderConstants.MajorImageVersion, + minorImageVersion: PEHeaderConstants.MinorImageVersion, + majorSubsystemVersion: PEHeaderConstants.MajorSubsystemVersion, + minorSubsystemVersion: PEHeaderConstants.MinorSubsystemVersion, + subsystem: subsystem, dllCharacteristics: dllCharacteristics, imageCharacteristics: imageCharacteristics, - sizeOfStackReserve: peHeaders.PEHeader.SizeOfStackReserve, - sizeOfStackCommit: peHeaders.PEHeader.SizeOfStackCommit, - sizeOfHeapReserve: 0, - sizeOfHeapCommit: 0); + sizeOfStackReserve: (is64BitTarget ? PE64HeaderConstants.SizeOfStackReserve : PE32HeaderConstants.SizeOfStackReserve), + sizeOfStackCommit: (is64BitTarget ? PE64HeaderConstants.SizeOfStackCommit : PE32HeaderConstants.SizeOfStackCommit), + sizeOfHeapReserve: (is64BitTarget ? PE64HeaderConstants.SizeOfHeapReserve : PE32HeaderConstants.SizeOfHeapReserve), + sizeOfHeapCommit: (is64BitTarget ? PE64HeaderConstants.SizeOfHeapCommit : PE32HeaderConstants.SizeOfHeapCommit)); } } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs index 4a8bb8d..904bea5 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs @@ -245,7 +245,10 @@ namespace ILCompiler.PEWriter DirectoryEntry _relocationDirectoryEntry; /// - /// Symbol representing the ready-to-run header table. + /// Symbol representing the ready-to-run COR (MSIL) header table. + /// Only present in single-file R2R executables. Composite R2R + /// executables don't have a COR header and locate the ReadyToRun + /// header directly using the well-known export symbol RTR_HEADER. /// ISymbolNode _corHeaderSymbol; @@ -524,11 +527,6 @@ namespace ILCompiler.PEWriter } } - if (_exportSymbols.Count != 0 && FindSection(R2RPEBuilder.ExportDataSectionName) == null) - { - sectionList.Add(new SectionInfo(R2RPEBuilder.ExportDataSectionName, SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead)); - } - return sectionList; } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs index 58c88ed..d4ba468 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs @@ -22,6 +22,48 @@ namespace ILCompiler.PEWriter NetBSD = 0x1993, } + /// + /// Constants for emission of Windows PE file mostly copied from CoreCLR pewriter.cpp. + /// + public static class PEHeaderConstants + { + public const int SectionAlignment = 0x1000; + + public const byte MajorLinkerVersion = 11; + public const byte MinorLinkerVersion = 0; + + public const byte MajorOperatingSystemVersion = 4; + public const byte MinorOperatingSystemVersion = 0; + + public const ushort MajorImageVersion = 0; + public const ushort MinorImageVersion = 0; + + public const ushort MajorSubsystemVersion = 4; + public const ushort MinorSubsystemVersion = 0; + } + + public static class PE32HeaderConstants + { + public const uint ImageBase = 0x0040_0000; + + public const uint SizeOfStackReserve = 0x100000; + public const uint SizeOfStackCommit = 0x1000; + public const uint SizeOfHeapReserve = 0x100000; + public const uint SizeOfHeapCommit = 0x1000; + } + + public static class PE64HeaderConstants + { + // Default base addresses used by Roslyn + public const ulong ExeImageBase = 0x1_4000_0000; + public const ulong DllImageBase = 0x1_8000_0000; + + public const ulong SizeOfStackReserve = 0x400000; + public const ulong SizeOfStackCommit = 0x4000; + public const ulong SizeOfHeapReserve = 0x100000; + public const ulong SizeOfHeapCommit = 0x2000; + } + public static class TargetExtensions { /// diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 132f31c..d02ed92 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; using System.IO; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; @@ -355,7 +356,7 @@ namespace ILCompiler.Reflection.ReadyToRun { int runtimeFunctionSize = CalculateRuntimeFunctionSize(); uint nRuntimeFunctions = (uint)(runtimeFunctionSection.Size / runtimeFunctionSize); - int runtimeFunctionOffset = GetOffset(runtimeFunctionSection.RelativeVirtualAddress); + int runtimeFunctionOffset = PEReader.GetOffset(runtimeFunctionSection.RelativeVirtualAddress); bool[] isEntryPoint = new bool[nRuntimeFunctions]; // initialize R2RMethods diff --git a/src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs b/src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs index cfd2ad4..93791b1 100644 --- a/src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs +++ b/src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs @@ -11,6 +11,7 @@ namespace ILCompiler public class CommandLineOptions { public FileInfo[] InputFilePaths { get; set; } + public FileInfo[] UnrootedInputFilePaths { get; set; } public FileInfo[] Mibc { get; set; } public string[] Reference { get; set; } public FileInfo OutputFilePath { get; set; } @@ -20,6 +21,7 @@ namespace ILCompiler public bool InputBubble { get; set; } public bool CompileBubbleGenerics { get; set; } public bool Verbose { get; set; } + public bool Composite { get; set; } public FileInfo DgmlLogFileName { get; set; } public bool GenerateFullDgmlLog { get; set; } @@ -55,8 +57,15 @@ namespace ILCompiler Description = SR.InputFilesToCompile, Arity = arbitraryArity, }, + new Option(new[] { "--unrooted-input-file-paths", "-u" }, SR.UnrootedInputFilesToCompile) + { + Argument = new Argument() + { + Arity = arbitraryArity + } + }, new Option(new[] { "--reference", "-r" }, SR.ReferenceFiles) - { + { Argument = new Argument() { Arity = arbitraryArity @@ -83,6 +92,7 @@ namespace ILCompiler }, new Option(new[] { "--optimize-time", "--Ot" }, SR.OptimizeSpeedOption), new Option(new[] { "--inputbubble" }, SR.InputBubbleOption), + new Option(new[] { "--composite" }, SR.CompositeBuildMode), new Option(new[] { "--tuning" }, SR.TuningImageOption) { Argument = new Argument() diff --git a/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs b/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs index 5912708..6f98ee6 100644 --- a/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs +++ b/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs @@ -29,6 +29,7 @@ namespace ILCompiler public TargetArchitecture _targetArchitecture; public OptimizationMode _optimizationMode; private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + private Dictionary _unrootedInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); private Dictionary _referenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); private Program(CommandLineOptions commandLineOptions) @@ -102,6 +103,9 @@ namespace ILCompiler foreach (var input in _commandLineOptions.InputFilePaths ?? Enumerable.Empty()) Helpers.AppendExpandedPaths(_inputFilePaths, input.FullName, true); + foreach (var input in _commandLineOptions.UnrootedInputFilePaths ?? Enumerable.Empty()) + Helpers.AppendExpandedPaths(_unrootedInputFilePaths, input.FullName, true); + foreach (var reference in _commandLineOptions.Reference ?? Enumerable.Empty()) Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false); } @@ -168,6 +172,7 @@ namespace ILCompiler // When we undo this this hack, replace this foreach with // typeSystemContext.InputFilePaths = _inputFilePaths; // + Dictionary allInputFilePaths = new Dictionary(); Dictionary inputFilePaths = new Dictionary(); List referenceableModules = new List(); foreach (var inputFile in _inputFilePaths) @@ -175,6 +180,7 @@ namespace ILCompiler try { var module = typeSystemContext.GetModuleFromPath(inputFile.Value); + allInputFilePaths.Add(inputFile.Key, inputFile.Value); inputFilePaths.Add(inputFile.Key, inputFile.Value); referenceableModules.Add(module); } @@ -184,16 +190,37 @@ namespace ILCompiler } } - typeSystemContext.InputFilePaths = inputFilePaths; + Dictionary unrootedInputFilePaths = new Dictionary(); + foreach (var unrootedInputFile in _unrootedInputFilePaths) + { + try + { + var module = typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); + if (!allInputFilePaths.ContainsKey(unrootedInputFile.Key)) + { + allInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); + unrootedInputFilePaths.Add(unrootedInputFile.Key, unrootedInputFile.Value); + referenceableModules.Add(module); + } + } + catch (TypeSystemException.BadImageFormatException) + { + // Keep calm and carry on. + } + } + + typeSystemContext.InputFilePaths = allInputFilePaths; typeSystemContext.ReferenceFilePaths = _referenceFilePaths; List inputModules = new List(); + List rootingModules = new List(); HashSet versionBubbleModulesHash = new HashSet(); - foreach (var inputFile in typeSystemContext.InputFilePaths) + foreach (var inputFile in inputFilePaths) { EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); inputModules.Add(module); + rootingModules.Add(module); versionBubbleModulesHash.Add(module); if (!_commandLineOptions.InputBubble) @@ -202,8 +229,15 @@ namespace ILCompiler } } + foreach (var unrootedInputFile in unrootedInputFilePaths) + { + EcmaModule module = typeSystemContext.GetModuleFromPath(unrootedInputFile.Value); + inputModules.Add(module); + versionBubbleModulesHash.Add(module); + } + string systemModuleName = _commandLineOptions.SystemModule ?? DefaultSystemModule; - typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(systemModuleName)); + typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName)); if (typeSystemContext.InputFilePaths.Count == 0) throw new CommandLineException(SR.NoInputFiles); @@ -231,6 +265,11 @@ namespace ILCompiler try { EcmaModule module = typeSystemContext.GetModuleFromPath(referenceFile); + if (versionBubbleModulesHash.Contains(module)) + { + // Ignore reference assemblies that have also been passed as inputs + continue; + } referenceableModules.Add(module); if (_commandLineOptions.InputBubble) { @@ -243,19 +282,34 @@ namespace ILCompiler List versionBubbleModules = new List(versionBubbleModulesHash); + if (!_commandLineOptions.Composite && inputModules.Count != 1) + { + throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); + } + ReadyToRunCompilationModuleGroupBase compilationGroup; List compilationRoots = new List(); if (singleMethod != null) { // Compiling just a single method - compilationGroup = new SingleMethodCompilationModuleGroup(typeSystemContext, inputModules, versionBubbleModules, _commandLineOptions.CompileBubbleGenerics, singleMethod); + compilationGroup = new SingleMethodCompilationModuleGroup( + typeSystemContext, + _commandLineOptions.Composite, + inputModules, + versionBubbleModules, + _commandLineOptions.CompileBubbleGenerics, + singleMethod); compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); } else { // Single assembly compilation. compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup( - typeSystemContext, inputModules, versionBubbleModules, _commandLineOptions.CompileBubbleGenerics); + typeSystemContext, + _commandLineOptions.Composite, + inputModules, + versionBubbleModules, + _commandLineOptions.CompileBubbleGenerics); } // Examine profile guided information as appropriate @@ -277,7 +331,7 @@ namespace ILCompiler if (singleMethod == null) { // For non-single-method compilations add compilation roots. - foreach (var module in inputModules) + foreach (var module in rootingModules) { compilationRoots.Add(new ReadyToRunRootProvider(module, profileDataManager, _commandLineOptions.Partial)); @@ -292,13 +346,8 @@ namespace ILCompiler // Compile // - string inputFilePath = ""; - foreach (var input in typeSystemContext.InputFilePaths) - { - inputFilePath = input.Value; - break; - } - ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder(typeSystemContext, compilationGroup, inputFilePath); + ReadyToRunCodegenCompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder( + typeSystemContext, compilationGroup, allInputFilePaths.Values); string compilationUnitPrefix = ""; builder.UseCompilationUnitPrefix(compilationUnitPrefix); diff --git a/src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx b/src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx index 2403b0b..d270a30 100644 --- a/src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx +++ b/src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx @@ -138,9 +138,15 @@ True when the entire input forms a version bubble (default = per-assembly bubble) + + Emit a composite R2R image comprising a number of input assemblies + Input file(s) to compile + + "Input files without automatic rooting of all methods + Path to JIT compiler library @@ -237,4 +243,7 @@ Warning: overriding -Ot with -Os + + Error: multiple input files are only supported in composite build mode: {0} + \ No newline at end of file -- 2.7.4