Initial Crossgen2 changes for composite R2R support (#31663)
authorTomáš Rylek <trylek@microsoft.com>
Wed, 19 Feb 2020 10:21:09 +0000 (11:21 +0100)
committerGitHub <noreply@github.com>
Wed, 19 Feb 2020 10:21:08 +0000 (11:21 +0100)
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

30 files changed:
src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AssemblyTableNode.cs [new file with mode: 0644]
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/OwnerCompositeExecutableNode.cs [new file with mode: 0644]
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypesTableNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IBC/IBCProfileData.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs
src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs
src/coreclr/src/tools/crossgen2/crossgen2/CommandLineOptions.cs
src/coreclr/src/tools/crossgen2/crossgen2/Program.cs
src/coreclr/src/tools/crossgen2/crossgen2/Properties/Resources.resx

index 02ae880..cda380e 100644 (file)
@@ -59,6 +59,7 @@ namespace ILCompiler.DependencyAnalysis
             //
             CorHeaderNode,
             ReadyToRunHeaderNode,
+            ReadyToRunAssemblyHeaderNode,
             ImportSectionsTableNode,
             ImportSectionNode,
             MethodEntrypointTableNode,
index 02445a1..2da9f24 100644 (file)
@@ -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
     /// </summary>
     internal class ReadyToRunObjectWriter
     {
-        // Nodefactory for which ObjectWriter is instantiated for.
+        /// <summary>
+        /// Nodefactory for which ObjectWriter is instantiated for. 
+        /// </summary>
         private readonly NodeFactory _nodeFactory;
+
+        /// <summary>
+        /// Output executable path.
+        /// </summary>
         private readonly string _objectFilePath;
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        private readonly EcmaModule _componentModule;
+
+        /// <summary>
+        /// Nodes to emit into the output executable as collected by the dependency analysis.
+        /// </summary>
         private readonly IEnumerable<DependencyNode> _nodes;
-        private readonly PEReader _inputPeReader;
+
+        /// <summary>
+        /// True when the executable generator should output a map file.
+        /// </summary>
         private readonly bool _generateMapFile;
 
 #if DEBUG
@@ -48,12 +72,12 @@ namespace ILCompiler.DependencyAnalysis
         Dictionary<string, NodeInfo> _previouslyWrittenNodeNames = new Dictionary<string, NodeInfo>();
 #endif
 
-        public ReadyToRunObjectWriter(PEReader inputPeReader, string objectFilePath, IEnumerable<DependencyNode> nodes, NodeFactory factory, bool generateMapFile)
+        public ReadyToRunObjectWriter(string objectFilePath, EcmaModule componentModule, IEnumerable<DependencyNode> 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<RuntimeFunctionsTableNode> 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<DependencyNode> nodes, NodeFactory factory, bool generateMapFile)
+        public static void EmitObject(string objectFilePath, EcmaModule componentModule, IEnumerable<DependencyNode> 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();
         }
     }
index 158188c..83fc679 100644 (file)
@@ -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.
         /// </summary>
         public abstract ReadyToRunFlags GetReadyToRunFlags();
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        public bool EnforceOwningType(EcmaModule module)
+        {
+            return IsCompositeBuildMode || module != CompilationModuleSet.Single();
+        }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        public abstract bool IsCompositeBuildMode { get; }
+
+        /// <summary>
+        /// List of input modules to use for the compilation.
+        /// </summary>
+        public abstract IEnumerable<EcmaModule> 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 (file)
index 0000000..9a0bdb2
--- /dev/null
@@ -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<AssemblyHeaderNode> _assemblyHeaders;
+
+        public AssemblyTableNode(TargetDetails target)
+            : base(target)
+        {
+            _assemblyHeaders = new List<AssemblyHeaderNode>();
+        }
+
+        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;
+    }
+}
index b7dc1a0..eef7490 100644 (file)
@@ -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
index 54ba55d..f157900 100644 (file)
@@ -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<int>.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;
+    }
 }
index f1c5696..89eb57a 100644 (file)
@@ -20,18 +20,19 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
     /// </summary>
     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;
     }
 }
index e2e5b21..6bb6ade 100644 (file)
@@ -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;
index 8774227..73ce2bf 100644 (file)
@@ -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
         /// </summary>
         private readonly Dictionary<int, AssemblyName> _moduleIdToAssemblyNameMap;
 
-
         /// <summary>
         /// Registered signature emitters.
         /// </summary>
@@ -63,36 +63,48 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         private bool _emissionCompleted;
 
         /// <summary>
-        /// Name of the input assembly.
-        /// </summary>
-        private string _inputModuleName;
-
-        /// <summary>
         /// Node factory for the compilation
         /// </summary>
         private readonly NodeFactory _nodeFactory;
 
-        public ManifestMetadataTableNode(EcmaModule inputModule, NodeFactory nodeFactory)
-            : base(inputModule.Context.Target)
+        public ManifestMetadataTableNode(NodeFactory nodeFactory)
+            : base(nodeFactory.Target)
         {
             _assemblyRefToModuleIdMap = new Dictionary<string, int>();
             _moduleIdToAssemblyNameMap = new Dictionary<int, AssemblyName>();
             _signatureEmitters = new List<ISignatureEmitter>();
             _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("<Module>"),
-               baseType: default(EntityHandle),
-               fieldList: MetadataTokens.FieldDefinitionHandle(1),
-               methodList: MetadataTokens.MethodDefinitionHandle(1));
+                default(TypeAttributes),
+                default(StringHandle),
+                metadataBuilder.GetOrAddString("<Module>"),
+                baseType: default(EntityHandle),
+                fieldList: MetadataTokens.FieldDefinitionHandle(1),
+                methodList: MetadataTokens.MethodDefinitionHandle(1));
 
             foreach (var idAndAssemblyName in _moduleIdToAssemblyNameMap.OrderBy(x => x.Key))
             {
index 6bbfc63..0df4157 100644 (file)
@@ -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 (file)
index 0000000..bc79b44
--- /dev/null
@@ -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
+{
+    /// <summary>
+    /// 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.
+    /// </summary>
+    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();
+        }
+    }
+}
index 096daf4..901cc90 100644 (file)
@@ -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 : "<Composite>");
+        }
     }
 }
index 26a204e..502da24 100644 (file)
@@ -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<TypeDefinitionHandle> defTypeInfo in r2rManager.GetDefinedTypes())
+            foreach (TypeInfo<TypeDefinitionHandle> 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<ExportedTypeHandle> expTypeInfo in r2rManager.GetExportedTypes())
+            foreach (TypeInfo<ExportedTypeHandle> 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;
     }
 }
index bfaff8c..ea37c34 100644 (file)
@@ -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;
+            }
+        }
     }
 }
index c962c66..6a3e4ef 100644 (file)
@@ -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<TypeAndMethod, IMethodNode>(CreateMethodEntrypoint);
 
-            _localMethodCache = new NodeCache<TypeAndMethod, MethodWithGCInfo>(key =>
+            _localMethodCache = new NodeCache<MethodDesc, MethodWithGCInfo>(key =>
             {
-                return new MethodWithGCInfo(key.Method.Method);
+                return new MethodWithGCInfo(key);
             });
 
             _methodSignatures = new NodeCache<MethodFixupKey, MethodFixupSignature>(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<TypeAndMethod, MethodWithGCInfo> _localMethodCache = new NodeCache<TypeAndMethod, MethodWithGCInfo>();
+        private NodeCache<MethodDesc, MethodWithGCInfo> _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<MethodWithGCInfo> 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)
index 8359e93..03be7ba 100644 (file)
@@ -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<Thread, CorInfoImpl> _corInfoImpls;
 
         /// <summary>
-        /// Name of the compilation input MSIL file.
+        /// Input MSIL file names.
         /// </summary>
-        private readonly string _inputFilePath;
+        private readonly IEnumerable<string> _inputFiles;
 
         private bool _resilient;
 
@@ -206,40 +208,86 @@ namespace ILCompiler
             ILProvider ilProvider,
             Logger logger,
             DevirtualizationManager devirtualizationManager,
-            string inputFilePath,
-            IEnumerable<ModuleDesc> modulesBeingInstrumented,
+            IEnumerable<string> 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<Thread, CorInfoImpl>();
+            _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<DependencyNodeCore<NodeFactory>> comparer = new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer());
+            DependencyAnalyzerBase<NodeFactory> componentGraph = new DependencyAnalyzer<NoLogStrategy<NodeFactory>, 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))
index 0cec5bf..1fc68ea 100644 (file)
@@ -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<string> _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<string, string>[] _ryujitOptions = Array.Empty<KeyValuePair<string, string>>();
         private ILProvider _ilProvider = new ReadyToRunILProvider();
 
-        public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group, string inputFilePath)
+        public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group, IEnumerable<string> 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<EcmaModule> 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<DependencyNodeCore<NodeFactory>> comparer = new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer());
             DependencyAnalyzerBase<NodeFactory> 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);
index 19e2e42..d313f1f 100644 (file)
@@ -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<ModuleDesc> _compilationModuleSet;
+        protected readonly HashSet<EcmaModule> _compilationModuleSet;
         private readonly HashSet<ModuleDesc> _versionBubbleModuleSet;
         private Dictionary<TypeDesc, ModuleToken> _typeRefsInCompilationModuleSet;
         private readonly bool _compileGenericDependenciesFromVersionBubbleModuleSet;
+        private readonly bool _isCompositeBuildMode;
         private readonly ConcurrentDictionary<TypeDesc, bool> _containsTypeLayoutCache = new ConcurrentDictionary<TypeDesc, bool>();
         private readonly ConcurrentDictionary<TypeDesc, bool> _versionsWithTypeCache = new ConcurrentDictionary<TypeDesc, bool>();
         private readonly ConcurrentDictionary<MethodDesc, bool> _versionsWithMethodCache = new ConcurrentDictionary<MethodDesc, bool>();
 
         public ReadyToRunCompilationModuleGroupBase(
             TypeSystemContext context,
-            IEnumerable<ModuleDesc> compilationModuleSet,
+            bool isCompositeBuildMode,
+            IEnumerable<EcmaModule> compilationModuleSet,
             IEnumerable<ModuleDesc> versionBubbleModuleSet,
             bool compileGenericDependenciesFromVersionBubbleModuleSet)
         {
-            _compilationModuleSet = new HashSet<ModuleDesc>(compilationModuleSet);
+            _compilationModuleSet = new HashSet<EcmaModule>(compilationModuleSet);
+            _isCompositeBuildMode = isCompositeBuildMode;
+
+            Debug.Assert(_isCompositeBuildMode || _compilationModuleSet.Count == 1);
 
             _versionBubbleModuleSet = new HashSet<ModuleDesc>(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<EcmaModule> CompilationModuleSet => _compilationModuleSet;
+
         private bool ComputeTypeVersionsWithCode(TypeDesc type)
         {
             if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any))
index 4991a41..f4e85d1 100644 (file)
@@ -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<ModuleDesc> compilationModuleSet,
+            TypeSystemContext context,
+            bool isCompositeBuildMode,
+            IEnumerable<EcmaModule> compilationModuleSet,
             IEnumerable<ModuleDesc> versionBubbleModuleSet,
             bool compileGenericDependenciesFromVersionBubbleModuleSet) :
                 base(context,
+                     isCompositeBuildMode,
                      compilationModuleSet,
                      versionBubbleModuleSet,
                      compileGenericDependenciesFromVersionBubbleModuleSet)
index bef02c8..aac7899 100644 (file)
@@ -78,27 +78,19 @@ namespace ILCompiler
         public ReadyToRunTableManager(CompilerTypeSystemContext typeSystemContext)
             : base(typeSystemContext) {}
 
-        public IEnumerable<TypeInfo<TypeDefinitionHandle>> GetDefinedTypes()
+        public IEnumerable<TypeInfo<TypeDefinitionHandle>> 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<TypeDefinitionHandle>(module.MetadataReader, typeDefHandle);
-                }
+                yield return new TypeInfo<TypeDefinitionHandle>(module.MetadataReader, typeDefHandle);
             }
         }
 
-            public IEnumerable<TypeInfo<ExportedTypeHandle>> GetExportedTypes()
+        public IEnumerable<TypeInfo<ExportedTypeHandle>> 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<ExportedTypeHandle>(module.MetadataReader, exportedTypeHandle);
-                }
+                yield return new TypeInfo<ExportedTypeHandle>(module.MetadataReader, exportedTypeHandle);
             }
         }
     }
index 5811caf..11c0b2e 100644 (file)
@@ -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<ModuleDesc> compilationModuleSet,
+            bool isCompositeBuildMode,
+            IEnumerable<EcmaModule> compilationModuleSet,
             IEnumerable<ModuleDesc> versionBubbleModuleSet,
             bool compileGenericDependenciesFromVersionBubbleModuleSet,
             MethodDesc method) :
                 base(context,
+                     isCompositeBuildMode,
                      compilationModuleSet,
                      versionBubbleModuleSet,
                      compileGenericDependenciesFromVersionBubbleModuleSet)
index a51c962..eaf1673 100644 (file)
@@ -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;
         }
index 8bdc4f1..65d5ccb 100644 (file)
@@ -99,6 +99,7 @@
     <Compile Include="Compiler\DependencyAnalysis\EmbeddedObjectNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\EmbeddedPointerIndirectionNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\ArgIterator.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\AssemblyTableNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\AttributePresenceFilterNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DebugDirectoryEntryNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DebugDirectoryNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InliningInfoNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InstanceEntryPointTableNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\IReadyToRunMethodCodeNode.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\OwnerCompositeExecutableNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\PrecodeMethodImport.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\LocalMethodImport.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\ManifestMetadataTableNode.cs" />
index 8b41614..944f8b2 100644 (file)
@@ -96,19 +96,7 @@ namespace ILCompiler.PEWriter
         private TargetDetails _target;
 
         /// <summary>
-        /// PE reader representing the input MSIL PE file we're copying to the output composite PE file.
-        /// </summary>
-        private PEReader _peReader;
-        
-        /// <summary>
-        /// Custom sections explicitly injected by the caller.
-        /// </summary>
-        private HashSet<string> _customSections;
-        
-        /// <summary>
-        /// 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.
         /// </summary>
         private ImmutableArray<Section> _sections;
 
@@ -164,16 +152,17 @@ namespace ILCompiler.PEWriter
         /// Constructor initializes the various control structures and combines the section list.
         /// </summary>
         /// <param name="target">Target environment specifier</param>
-        /// <param name="peReader">Input MSIL PE file reader</param>
+        /// <param name="peHeaderBuilder">PE file header builder</param>
         /// <param name="getRuntimeFunctionsTable">Callback to retrieve the runtime functions table</param>
         public R2RPEBuilder(
             TargetDetails target,
-            PEReader peReader,
+            PEHeaderBuilder peHeaderBuilder,
+            ISymbolNode r2rHeaderExportSymbol,
+            string outputFileSimpleName,
             Func<RuntimeFunctionsTableNode> getRuntimeFunctionsTable)
-            : base(PEHeaderCopier.Copy(peReader.PEHeaders, target), deterministicIdProvider: null)
+            : base(peHeaderBuilder, deterministicIdProvider: null)
         {
             _target = target;
-            _peReader = peReader;
             _getRuntimeFunctionsTable = getRuntimeFunctionsTable;
             _sectionRvaDeltas = new List<SectionRVADelta>();
 
@@ -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<string>();
-            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<Section>.Builder sectionListBuilder = ImmutableArray.CreateBuilder<Section>();
@@ -270,16 +260,18 @@ namespace ILCompiler.PEWriter
         /// Emit built sections into the R2R PE file.
         /// </summary>
         /// <param name="outputStream">Output stream for the final R2R PE file</param>
-        public void Write(Stream outputStream)
+        /// <param name="timeDateStamp">Timestamp to set in the PE header of the output R2R executable</param>
+        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
         }
 
         /// <summary>
-        /// Copy over the timestamp from IL image for determinism.
+        /// Set PE header timestamp in the output R2R image to a given value.
         /// </summary>
         /// <param name="outputStream">Output stream representing the R2R PE executable</param>
-        private void CopyTimeStampFromInputImage(Stream outputStream)
+        /// <param name="timeDateStamp">Timestamp to set in the R2R PE header</param>
+        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
     }
     
     /// <summary>
-    /// 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).
     /// </summary>
-    static class PEHeaderCopier
+    static class PEHeaderProvider
     {
         /// <summary>
         /// Copy PE headers into a PEHeaderBuilder used by PEBuilder.
@@ -560,25 +558,33 @@ namespace ILCompiler.PEWriter
         /// <param name="target">Target architecture to set in the header</param>
         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);
+        }
 
+        /// <summary>
+        /// Fill in PE header information into a PEHeaderBuilder used by PEBuilder.
+        /// </summary>
+        /// <param name="relocsStripped">Relocs are not present in the PE executable</param>
+        /// <param name="dllCharacteristics">Extra DLL characteristics to apply</param>
+        /// <param name="subsystem">Targeting subsystem</param>
+        /// <param name="target">Target architecture to set in the header</param>
+        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));
         }
     }
 }
index 4a8bb8d..904bea5 100644 (file)
@@ -245,7 +245,10 @@ namespace ILCompiler.PEWriter
         DirectoryEntry _relocationDirectoryEntry;
 
         /// <summary>
-        /// 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.
         /// </summary>
         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;
         }
 
index 58c88ed..d4ba468 100644 (file)
@@ -22,6 +22,48 @@ namespace ILCompiler.PEWriter
         NetBSD = 0x1993,
     }
 
+    /// <summary>
+    /// Constants for emission of Windows PE file mostly copied from CoreCLR pewriter.cpp.
+    /// </summary>
+    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
     {
         /// <summary>
index 132f31c..d02ed92 100644 (file)
@@ -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
index cfd2ad4..93791b1 100644 (file)
@@ -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<FileInfo[]>()
+                    {
+                        Arity = arbitraryArity
+                    }
+                },
                 new Option(new[] { "--reference", "-r" }, SR.ReferenceFiles)
-                { 
+                {
                     Argument = new Argument<string[]>() 
                     { 
                         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<bool>() 
index 5912708..6f98ee6 100644 (file)
@@ -29,6 +29,7 @@ namespace ILCompiler
         public TargetArchitecture _targetArchitecture;
         public OptimizationMode _optimizationMode;
         private Dictionary<string, string> _inputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+        private Dictionary<string, string> _unrootedInputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         private Dictionary<string, string> _referenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
         private Program(CommandLineOptions commandLineOptions)
@@ -102,6 +103,9 @@ namespace ILCompiler
             foreach (var input in _commandLineOptions.InputFilePaths ?? Enumerable.Empty<FileInfo>())
                 Helpers.AppendExpandedPaths(_inputFilePaths, input.FullName, true);
 
+            foreach (var input in _commandLineOptions.UnrootedInputFilePaths ?? Enumerable.Empty<FileInfo>())
+                Helpers.AppendExpandedPaths(_unrootedInputFilePaths, input.FullName, true);
+
             foreach (var reference in _commandLineOptions.Reference ?? Enumerable.Empty<string>())
                 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<string, string> allInputFilePaths = new Dictionary<string, string>();
                     Dictionary<string, string> inputFilePaths = new Dictionary<string, string>();
                     List<ModuleDesc> referenceableModules = new List<ModuleDesc>();
                     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<string, string> unrootedInputFilePaths = new Dictionary<string, string>();
+                    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<EcmaModule> inputModules = new List<EcmaModule>();
+                    List<EcmaModule> rootingModules = new List<EcmaModule>();
                     HashSet<ModuleDesc> versionBubbleModulesHash = new HashSet<ModuleDesc>();
 
-                    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<ModuleDesc> versionBubbleModules = new List<ModuleDesc>(versionBubbleModulesHash);
 
+                    if (!_commandLineOptions.Composite && inputModules.Count != 1)
+                    {
+                        throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules)));
+                    }
+
                     ReadyToRunCompilationModuleGroupBase compilationGroup;
                     List<ICompilationRootProvider> compilationRoots = new List<ICompilationRootProvider>();
                     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);
 
index 2403b0b..d270a30 100644 (file)
   <data name="InputBubbleOption" xml:space="preserve">
     <value>True when the entire input forms a version bubble (default = per-assembly bubble)</value>
   </data>
+  <data name="CompositeBuildMode" xml:space="preserve">
+    <value>Emit a composite R2R image comprising a number of input assemblies</value>
+  </data>
   <data name="InputFilesToCompile" xml:space="preserve">
     <value>Input file(s) to compile</value>
   </data>
+  <data name="UnrootedInputFilesToCompile" xml:space="preserve">
+    <value>"Input files without automatic rooting of all methods</value>
+  </data>
   <data name="JitPathOption" xml:space="preserve">
     <value>Path to JIT compiler library</value>
   </data>
   <data name="WarningOverridingOptimizeSpace" xml:space="preserve">
     <value>Warning: overriding -Ot with -Os</value>
   </data>
+  <data name="ErrorMultipleInputFilesCompositeModeOnly" xml:space="preserve">
+    <value>Error: multiple input files are only supported in composite build mode: {0}</value>
+  </data>
 </root>
\ No newline at end of file