From 6c50d6e5c7a9cc0ccebf6e98180cf83490780021 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Mon, 6 May 2019 22:06:15 +0200 Subject: [PATCH 1/4] Initial CPAOT support for large version bubble As I don't see any way to revive the original PR I accidentally merged in and then reverted, I have created a new one. In the initial commit I have pushed the previous change. As the second commit I have added the CompilationModuleGroup change suggested by Simon - using separate sets for input assemblies vs. for the version bubble. This change modifies the existing --inputbubble CPAOT option to work the same as Crossgen /largeversionbubble i.e. the main module and all reference assemblies are considered to belong to the same version bubble. The gist of the change deals with encoding module overrides in signatures. I have implemented a new R2R header table ManifestMetadataTableNode and added logic for composing and emitting the extra AssemblyRef list. One challenging bit was that we need to emit all the signatures before emitting the ManifestMetadataTableNode because only the emission of the signatures shakes out all the required AssemblyRef's. I have added a simple loop to ImportSectionsTableNode / ImportSectionNode that traverses and emits all registered import cell signatures. For instance entrypoints, I didn't find any manner of encoding a module override for their signatures in the native hashtable so for now I added filtering that we're only storing those instance entrypoints that have the current input module as the reference module. One can theoretically imagine that a module may contain arbitrary generic instantiations spanning other modules when larger bubbles are on. While working on the change I also realized that the current way of basing signature contexts on input modules in CorInfoImpl.ReadyToRun was incorrect - when a method gets inlined, we still need the original signature context because that's what defines the ambient module used in the CoreCLR signature parser. I have deleted the field and instead I added a new method GetSignatureContext which currently returns ReadyToRunCoregenNodeFactory.InputModuleContext. Once we start implementing "single-file" (i.e. compiling multiple MSIL modules into a single PE), we'll change this to something more fine-grained, most likely based on some lookup on MethodBeingCompiled. I found out that part of the framework libraries were crashing in release build with large version bubble on due to the presence of duplicate symbols. I found out that CPAOT was still inconsistent w.r.t. means for formatting typesystem entities in AppendMangledName methods. I went over all these methods and unified them to use the name mangled for formatting types, methods and fields. As part of this cleanup I could subsequently remove the WriteTo methods on RuntimeDeterminedTypeHelper that are no longer needed. Rerunning the Pri#1 tests with my new support for issues.targets filtering out some annoying timing out GC tests I found out that several tests failed during CPAOT compilation in the manifest metadata table node. It turns out that the AssemblyRef metadata table is not always unique, in particular I hit duplication of the "mscorlib" reference in the table. This change makes CPAOT resilient towards this type of issues. Thanks Tomas --- .../ReadyToRun/DelegateCtorSignature.cs | 17 +- .../ReadyToRun/FieldFixupSignature.cs | 7 +- .../ReadyToRun/FixupConstants.cs | 7 + .../ReadyToRun/GenericLookupSignature.cs | 127 +++++++----- .../ReadyToRun/ImportSectionNode.cs | 24 +++ .../ReadyToRun/ImportSectionsTableNode.cs | 26 ++- .../ReadyToRun/InstanceEntryPointTableNode.cs | 9 +- .../ReadyToRun/ManifestMetadataTableNode.cs | 188 ++++++++++++++++++ .../ReadyToRun/MethodEntryPointTableNode.cs | 2 +- .../ReadyToRun/MethodFixupSignature.cs | 5 +- .../ReadyToRun/ModuleToken.cs | 2 +- .../ReadyToRun/ModuleTokenResolver.cs | 23 ++- .../ReadyToRun/NewArrayFixupSignature.cs | 3 +- .../ReadyToRun/NewObjectFixupSignature.cs | 8 +- .../ReadyToRun/SignatureBuilder.cs | 49 ++++- .../ReadyToRun/SignatureContext.cs | 59 +++++- .../ReadyToRun/StringImport.cs | 4 +- .../ReadyToRun/StringImportSignature.cs | 8 +- .../ReadyToRun/TypeFixupSignature.cs | 10 +- .../ReadyToRunCodegenNodeFactory.cs | 9 +- .../ReadyToRunSymbolNodeFactory.cs | 4 +- .../ReadyToRunCodegenCompilationBuilder.cs | 2 +- .../Compiler/RuntimeDeterminedTypeHelper.cs | 104 ---------- .../src/ILCompiler.ReadyToRun.csproj | 1 + .../JitInterface/CorInfoImpl.ReadyToRun.cs | 51 ++--- src/ILCompiler/src/Program.cs | 12 +- src/JitInterface/src/CorInfoImpl.cs | 4 +- 27 files changed, 535 insertions(+), 230 deletions(-) create mode 100644 src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/DelegateCtorSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/DelegateCtorSignature.cs index fd9b06650c6..efbad225087 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/DelegateCtorSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/DelegateCtorSignature.cs @@ -38,21 +38,24 @@ public DelegateCtorSignature( public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { + ReadyToRunCodegenNodeFactory r2rFactory = (ReadyToRunCodegenNodeFactory)factory; ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder(); builder.AddSymbol(this); if (!relocsOnly) { - builder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_DelegateCtor); + SignatureContext innerContext = builder.EmitFixup(r2rFactory, ReadyToRunFixupKind.READYTORUN_FIXUP_DelegateCtor, _methodToken.Module, _signatureContext); + builder.EmitMethodSignature( _targetMethod.Method, constrainedType: null, methodToken: _methodToken, enforceDefEncoding: false, - _signatureContext, + innerContext, isUnboxingStub: false, isInstantiatingStub: false); - builder.EmitTypeSignature(_delegateType, _signatureContext); + + builder.EmitTypeSignature(_delegateType, innerContext); } return builder.ToObjectData(); @@ -71,7 +74,13 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append($@"DelegateCtor({_delegateType} -> {_targetMethod.Method})"); + sb.Append($@"DelegateCtor("); + sb.Append(nameMangler.GetMangledTypeName(_delegateType)); + sb.Append(" -> "); + sb.Append(nameMangler.GetMangledMethodName(_targetMethod.Method)); + sb.Append("; "); + sb.Append(_methodToken.ToString()); + sb.Append(")"); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs index 21ad6039d44..3be2b4bd964 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs @@ -7,6 +7,7 @@ using Internal.JitInterface; using Internal.Text; using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis.ReadyToRun { @@ -36,8 +37,10 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { dataBuilder.AddSymbol(this); - dataBuilder.EmitByte((byte)_fixupKind); - dataBuilder.EmitFieldSignature(_fieldDesc, _signatureContext); + EcmaModule targetModule = _signatureContext.GetModuleTokenForField(_fieldDesc).Module; + SignatureContext innerContext = dataBuilder.EmitFixup(r2rFactory, _fixupKind, targetModule, _signatureContext); + + dataBuilder.EmitFieldSignature(_fieldDesc, innerContext); } return dataBuilder.ToObjectData(); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FixupConstants.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FixupConstants.cs index 8f9c2f94b78..8116e1ac195 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FixupConstants.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FixupConstants.cs @@ -71,6 +71,7 @@ public enum DictionaryEntryKind DeclaringTypeHandleSlot = 7, } + [Flags] public enum ReadyToRunFixupKind { READYTORUN_FIXUP_Invalid = 0x00, @@ -120,6 +121,12 @@ public enum ReadyToRunFixupKind READYTORUN_FIXUP_DelegateCtor = 0x2C, /* optimized delegate ctor */ READYTORUN_FIXUP_DeclaringTypeHandle = 0x2D, + + READYTORUN_FIXUP_ModuleOverride = 0x80, + // followed by sig-encoded UInt with assemblyref index into either the assemblyref + // table of the MSIL metadata of the master context module for the signature or + // into the extra assemblyref table in the manifest metadata R2R header table + // (used in cases inlining brings in references to assemblies not seen in the MSIL). } // diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs index 4f31ff29664..38882695480 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs @@ -8,12 +8,13 @@ using Internal.JitInterface; using Internal.Text; using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class GenericLookupSignature : Signature { - private CORINFO_RUNTIME_LOOKUP_KIND _runtimeLookupKind; + private readonly CORINFO_RUNTIME_LOOKUP_KIND _runtimeLookupKind; private readonly ReadyToRunFixupKind _fixupKind; @@ -28,9 +29,9 @@ public class GenericLookupSignature : Signature private readonly SignatureContext _signatureContext; public GenericLookupSignature( - CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, - ReadyToRunFixupKind fixupKind, - TypeDesc typeArgument, + CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, + ReadyToRunFixupKind fixupKind, + TypeDesc typeArgument, MethodWithToken methodArgument, FieldDesc fieldArgument, GenericContext methodContext, @@ -50,56 +51,86 @@ public GenericLookupSignature( public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { + if (relocsOnly) + { + return new ObjectData(Array.Empty(), null, 1, null); + } + ReadyToRunCodegenNodeFactory r2rFactory = (ReadyToRunCodegenNodeFactory)factory; - ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(); - if (!relocsOnly) + // Determine the need for module override + EcmaModule targetModule; + if (_methodArgument != null) { - dataBuilder.AddSymbol(this); + targetModule = _methodArgument.Token.Module; + } + else if (_typeArgument != null) + { + targetModule = _signatureContext.GetTargetModule(_typeArgument); + } + else if (_fieldArgument != null) + { + targetModule = _signatureContext.GetTargetModule(_fieldArgument); + } + else + { + throw new NotImplementedException(); + } - switch (_runtimeLookupKind) - { - case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM: - dataBuilder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_TypeDictionaryLookup); - break; + ReadyToRunFixupKind fixupToEmit; + TypeDesc contextTypeToEmit = null; - case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM: - dataBuilder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_MethodDictionaryLookup); - break; + switch (_runtimeLookupKind) + { + case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM: + fixupToEmit = ReadyToRunFixupKind.READYTORUN_FIXUP_TypeDictionaryLookup; + break; - case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ: - dataBuilder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_ThisObjDictionaryLookup); - dataBuilder.EmitTypeSignature(_methodContext.ContextType, _signatureContext); - break; + case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM: + fixupToEmit = ReadyToRunFixupKind.READYTORUN_FIXUP_MethodDictionaryLookup; + break; - default: - throw new NotImplementedException(); - } + case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ: + fixupToEmit = ReadyToRunFixupKind.READYTORUN_FIXUP_ThisObjDictionaryLookup; + contextTypeToEmit = _methodContext.ContextType; + break; - dataBuilder.EmitByte((byte)_fixupKind); - if (_methodArgument != null) - { - dataBuilder.EmitMethodSignature( - method: _methodArgument.Method, - constrainedType: _typeArgument, - methodToken: _methodArgument.Token, - enforceDefEncoding: false, - context: _signatureContext, - isUnboxingStub: false, - isInstantiatingStub: true); - } - else if (_typeArgument != null) - { - dataBuilder.EmitTypeSignature(_typeArgument, _signatureContext); - } - else if (_fieldArgument != null) - { - dataBuilder.EmitFieldSignature(_fieldArgument, _signatureContext); - } - else - { + default: throw new NotImplementedException(); - } + } + + ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(); + dataBuilder.AddSymbol(this); + + SignatureContext innerContext = dataBuilder.EmitFixup(r2rFactory, fixupToEmit, targetModule, _signatureContext); + if (contextTypeToEmit != null) + { + dataBuilder.EmitTypeSignature(contextTypeToEmit, innerContext); + } + + dataBuilder.EmitByte((byte)_fixupKind); + if (_methodArgument != null) + { + dataBuilder.EmitMethodSignature( + method: _methodArgument.Method, + constrainedType: _typeArgument, + methodToken: _methodArgument.Token, + enforceDefEncoding: false, + context: innerContext, + isUnboxingStub: false, + isInstantiatingStub: true); + } + else if (_typeArgument != null) + { + dataBuilder.EmitTypeSignature(_typeArgument, innerContext); + } + else if (_fieldArgument != null) + { + dataBuilder.EmitFieldSignature(_fieldArgument, innerContext); + } + else + { + throw new NotImplementedException(); } return dataBuilder.ToObjectData(); @@ -115,7 +146,7 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde sb.Append(": "); if (_methodArgument != null) { - RuntimeDeterminedTypeHelper.WriteTo(_methodArgument.Method, sb); + sb.Append(nameMangler.GetMangledMethodName(_methodArgument.Method)); if (!_methodArgument.Token.IsNull) { sb.Append(" ["); @@ -127,11 +158,11 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde } if (_typeArgument != null) { - RuntimeDeterminedTypeHelper.WriteTo(_typeArgument, sb); + sb.Append(nameMangler.GetMangledTypeName(_typeArgument)); } if (_fieldArgument != null) { - RuntimeDeterminedTypeHelper.WriteTo(_fieldArgument, sb); + sb.Append(nameMangler.GetMangledFieldName(_fieldArgument)); } sb.Append(" ("); _methodContext.AppendMangledName(nameMangler, sb); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs index 6143c35d422..eeed8216518 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; namespace ILCompiler.DependencyAnalysis.ReadyToRun @@ -11,6 +12,8 @@ public class ImportSectionNode : EmbeddedObjectNode private readonly ArrayOfEmbeddedDataNode _imports; // TODO: annoying - today there's no way to put signature RVA's into R/O data section private readonly ArrayOfEmbeddedPointersNode _signatures; + // TODO: annoying - cannot enumerate the ArrayOfEmbeddedPointersNode so we must keep a copy. + private readonly List _signatureList; private readonly GCRefMapNode _gcRefMap; private readonly CorCompileImportType _type; @@ -20,6 +23,8 @@ public class ImportSectionNode : EmbeddedObjectNode private readonly bool _emitPrecode; private readonly bool _emitGCRefMap; + private bool _materializedSignature; + public ImportSectionNode(string name, CorCompileImportType importType, CorCompileImportFlags flags, byte entrySize, bool emitPrecode, bool emitGCRefMap) { _name = name; @@ -31,13 +36,32 @@ public ImportSectionNode(string name, CorCompileImportType importType, CorCompil _imports = new ArrayOfEmbeddedDataNode(_name + "_ImportBegin", _name + "_ImportEnd", null); _signatures = new ArrayOfEmbeddedPointersNode(_name + "_SigBegin", _name + "_SigEnd", null); + _signatureList = new List(); _gcRefMap = (_emitGCRefMap ? new GCRefMapNode(this) : null); } + public void MaterializeSignature(ReadyToRunCodegenNodeFactory r2rFactory) + { + if (!_materializedSignature) + { + foreach (Signature signature in _signatureList) + { + signature.GetData(r2rFactory, relocsOnly: false); + } + _materializedSignature = true; + } + } + public void AddImport(NodeFactory factory, Import import) { + if (_materializedSignature) + { + throw new Exception("Cannot call AddImport after MaterializeSignature"); + } + _imports.AddEmbeddedObject(import); _signatures.AddEmbeddedObject(import.ImportSignature); + _signatureList.Add(import.ImportSignature.Target); if (_emitGCRefMap) { _gcRefMap.AddImport(import); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionsTableNode.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionsTableNode.cs index fbd5d42e2b4..98a37ca62e1 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionsTableNode.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionsTableNode.cs @@ -6,13 +6,31 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { - public class ImportSectionsTableNode : ArrayOfEmbeddedDataNode - { - public ImportSectionsTableNode(TargetDetails target) + public class ImportSectionsTableNode : ArrayOfEmbeddedDataNode, ISignatureEmitter + { + private readonly ReadyToRunCodegenNodeFactory _r2rFactory; + + private bool _materializedSignature; + + public ImportSectionsTableNode(ReadyToRunCodegenNodeFactory r2rFactory) : base("ImportSectionsTableStart", "ImportSectionsTableEnd", null) { + _r2rFactory = r2rFactory; + _r2rFactory.ManifestMetadataTable.RegisterEmitter(this); + } + + public void MaterializeSignature() + { + if (!_materializedSignature) + { + foreach (ImportSectionNode importSection in NodesList) + { + importSection.MaterializeSignature(_r2rFactory); + } + _materializedSignature = true; + } } - + protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly) { builder.RequireInitialPointerAlignment(); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs index af0eebdced9..b879a996f1f 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs @@ -52,11 +52,18 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { int methodIndex = r2rFactory.RuntimeFunctionsTable.GetIndex(method); + ModuleToken moduleToken = method.SignatureContext.GetModuleTokenForMethod(method.Method.GetTypicalMethodDefinition()); + if (moduleToken.Module != r2rFactory.InputModuleContext.GlobalContext) + { + // TODO: encoding of instance methods relative to other modules within the version bubble + continue; + } + ArraySignatureBuilder signatureBuilder = new ArraySignatureBuilder(); signatureBuilder.EmitMethodSignature( method.Method, constrainedType: null, - default(ModuleToken), + moduleToken, enforceDefEncoding: true, method.SignatureContext, isUnboxingStub: false, diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs new file mode 100644 index 00000000000..c4c8c8d078a --- /dev/null +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs @@ -0,0 +1,188 @@ +// 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.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + /// + /// Signature emitters need to register themselves with the manifest metadata table; + /// this is needed so that the manifest metadata can force all signatures to materialize, + /// and, in doing so, all extra reference modules to be emitted to the manifest metadata. + /// + public interface ISignatureEmitter + { + void MaterializeSignature(); + } + + public class ManifestMetadataTableNode : HeaderTableNode + { + /// + /// Map from simple assembly names to their module indices. The map gets prepopulated + /// with AssemblyRef's from the input module and subsequently expanded by adding entries + /// recorded in the manifest metadata. + /// + private readonly Dictionary _assemblyRefToModuleIdMap; + + /// + /// Assembly references to store in the manifest metadata. + /// + private readonly List _manifestAssemblies; + + /// + /// Registered signature emitters. + /// + private readonly List _signatureEmitters; + + /// + /// ID corresponding to the next manifest metadata assemblyref entry. + /// + private int _nextModuleId; + + /// + /// Set to true after GetData has been called. After that, ModuleToIndex may be called no more. + /// + private bool _emissionCompleted; + + /// + /// Name of the input assembly. + /// + private string _inputModuleName; + + public ManifestMetadataTableNode(EcmaModule inputModule) + : base(inputModule.Context.Target) + { + _assemblyRefToModuleIdMap = new Dictionary(); + _manifestAssemblies = new List(); + _signatureEmitters = new List(); + + _inputModuleName = inputModule.Assembly.GetName().Name; + + int assemblyRefCount = inputModule.MetadataReader.GetTableRowCount(TableIndex.AssemblyRef); + for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++) + { + AssemblyReferenceHandle assemblyRefHandle = MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex); + AssemblyReference assemblyRef = inputModule.MetadataReader.GetAssemblyReference(assemblyRefHandle); + string assemblyName = inputModule.MetadataReader.GetString(assemblyRef.Name); + _assemblyRefToModuleIdMap[assemblyName] = assemblyRefIndex; + } + + // AssemblyRefCount + 1 corresponds to ROWID 0 in the manifest metadata + _nextModuleId = assemblyRefCount + 2; + } + + public void RegisterEmitter(ISignatureEmitter emitter) + { + _signatureEmitters.Add(emitter); + } + + public int ModuleToIndex(EcmaModule module) + { + AssemblyName assemblyName = module.Assembly.GetName(); + int assemblyRefIndex; + if (!_assemblyRefToModuleIdMap.TryGetValue(assemblyName.Name, out assemblyRefIndex)) + { + if (_emissionCompleted) + { + throw new Exception("mustn't add new assemblies after signatures have been materialized"); + } + + assemblyRefIndex = _nextModuleId++; + _manifestAssemblies.Add(assemblyName); + _assemblyRefToModuleIdMap.Add(assemblyName.Name, assemblyRefIndex); + } + return assemblyRefIndex; + } + + public override int ClassCode => 791828335; + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("ManifestMetadataTableNode"); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + if (relocsOnly) + { + return new ObjectData(Array.Empty(), null, 1, null); + } + + if (!_emissionCompleted) + { + foreach (ISignatureEmitter emitter in _signatureEmitters) + { + emitter.MaterializeSignature(); + } + + _emissionCompleted = true; + } + + MetadataBuilder metadataBuilder = new MetadataBuilder(); + + metadataBuilder.AddAssembly( + metadataBuilder.GetOrAddString(_inputModuleName), + new Version(0, 0, 0, 0), + culture: default(StringHandle), + publicKey: default(BlobHandle), + flags: default(AssemblyFlags), + hashAlgorithm: AssemblyHashAlgorithm.None); + + metadataBuilder.AddModule( + 0, + metadataBuilder.GetOrAddString(_inputModuleName), + default(GuidHandle), default(GuidHandle), default(GuidHandle)); + + // Module type + metadataBuilder.AddTypeDefinition( + default(TypeAttributes), + default(StringHandle), + metadataBuilder.GetOrAddString(""), + baseType: default(EntityHandle), + fieldList: MetadataTokens.FieldDefinitionHandle(1), + methodList: MetadataTokens.MethodDefinitionHandle(1)); + + foreach (AssemblyName assemblyName in _manifestAssemblies) + { + AssemblyFlags assemblyFlags = 0; + if ((assemblyName.Flags & AssemblyNameFlags.PublicKey) != 0) + { + assemblyFlags |= AssemblyFlags.PublicKey; + } + if ((assemblyName.Flags & AssemblyNameFlags.Retargetable) != 0) + { + assemblyFlags |= AssemblyFlags.Retargetable; + } + + AssemblyReferenceHandle newHandle = metadataBuilder.AddAssemblyReference( + name: metadataBuilder.GetOrAddString(assemblyName.Name), + version: assemblyName.Version, + culture: metadataBuilder.GetOrAddString(assemblyName.CultureName), + publicKeyOrToken: metadataBuilder.GetOrAddBlob(assemblyName.GetPublicKeyToken()), + flags: assemblyFlags, + hashValue: default(BlobHandle) /* TODO */); + } + + MetadataRootBuilder metadataRootBuilder = new MetadataRootBuilder(metadataBuilder); + BlobBuilder metadataBlobBuilder = new BlobBuilder(); + metadataRootBuilder.Serialize(metadataBlobBuilder, methodBodyStreamRva: 0, mappedFieldDataStreamRva: 0); + + return new ObjectData( + data: metadataBlobBuilder.ToArray(), + relocs: Array.Empty(), + alignment: 1, + definedSymbols: new ISymbolDefinitionNode[] { this }); + } + } +} diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs index c18fa70972f..549fdd02aa2 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs @@ -57,7 +57,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (MethodWithGCInfo method in r2rFactory.EnumerateCompiledMethods()) { - if (method.Method is EcmaMethod ecmaMethod) + if (method.Method is EcmaMethod ecmaMethod && r2rFactory.InputModuleContext.GlobalContext == ecmaMethod.Module) { // Strip away the token type bits, keep just the low 24 bits RID uint rid = SignatureBuilder.RidFromToken((mdToken)MetadataTokens.GetToken(ecmaMethod.Handle)); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs index 71d7268e8fa..9310f3338c0 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs @@ -58,10 +58,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) ReadyToRunCodegenNodeFactory r2rFactory = (ReadyToRunCodegenNodeFactory)factory; ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(); dataBuilder.AddSymbol(this); - - dataBuilder.EmitByte((byte)_fixupKind); + SignatureContext innerContext = dataBuilder.EmitFixup(r2rFactory, _fixupKind, _methodToken.Module, _signatureContext); dataBuilder.EmitMethodSignature(_methodDesc, _constrainedType, _methodToken, enforceDefEncoding: false, - _signatureContext, _isUnboxingStub, _isInstantiatingStub); + innerContext, _isUnboxingStub, _isInstantiatingStub); return dataBuilder.ToObjectData(); } diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleToken.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleToken.cs index c7d751dea33..f30a063a67e 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleToken.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleToken.cs @@ -78,7 +78,7 @@ public int CompareTo(ModuleToken other) public SignatureContext SignatureContext(ModuleTokenResolver resolver) { - return new SignatureContext(resolver); + return new SignatureContext(Module, resolver); } public MetadataReader MetadataReader => Module.PEReader.GetMetadataReader(); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs index a482816fc4a..3c6a5f9e18c 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs @@ -33,12 +33,19 @@ public class ModuleTokenResolver private readonly CompilerTypeSystemContext _typeSystemContext; + private Func _moduleIndexLookup; + public ModuleTokenResolver(CompilationModuleGroup compilationModuleGroup, CompilerTypeSystemContext typeSystemContext) { _compilationModuleGroup = compilationModuleGroup; _typeSystemContext = typeSystemContext; } + public void SetModuleIndexLookup(Func moduleIndexLookup) + { + _moduleIndexLookup = moduleIndexLookup; + } + public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = true) { if (_compilationModuleGroup.ContainsType(type)) @@ -65,6 +72,8 @@ public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = t public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool throwIfNotFound = true) { + method = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (_compilationModuleGroup.ContainsMethodBody(method, unboxingStub: false) && method is EcmaMethod ecmaMethod) { @@ -89,8 +98,15 @@ public ModuleToken GetModuleTokenForField(FieldDesc field, bool throwIfNotFound return new ModuleToken(ecmaField.Module, ecmaField.Handle); } + TypeDesc owningCanonType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); + FieldDesc canonField = field; + if (owningCanonType != field.OwningType) + { + canonField = _typeSystemContext.GetFieldForInstantiatedType(field.GetTypicalFieldDefinition(), (InstantiatedType)owningCanonType); + } + ModuleToken token; - if (_fieldToRefTokens.TryGetValue(field, out token)) + if (_fieldToRefTokens.TryGetValue(canonField, out token)) { return token; } @@ -190,6 +206,11 @@ public void AddModuleTokenForType(TypeDesc type, ModuleToken token) } } + public int GetModuleIndex(EcmaModule module) + { + return _moduleIndexLookup(module); + } + /// /// As of 8/20/2018, recursive propagation of type information through /// the composite signature tree is not needed for anything. We're adding diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs index e38b63a9a86..4345ad15b90 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs @@ -37,7 +37,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append($@"NewArraySignature: {_arrayType.ToString()}"); + sb.Append($@"NewArraySignature: "); + sb.Append(nameMangler.GetMangledTypeName(_arrayType)); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/NewObjectFixupSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/NewObjectFixupSignature.cs index 6de578ee80e..d4a9d15d6b3 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/NewObjectFixupSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/NewObjectFixupSignature.cs @@ -33,8 +33,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { dataBuilder.AddSymbol(this); - dataBuilder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_NewObject); - dataBuilder.EmitTypeSignature(_typeDesc, _signatureContext); + EcmaModule targetModule = _signatureContext.GetTargetModule(_typeDesc); + SignatureContext innerContext = dataBuilder.EmitFixup(r2rFactory, ReadyToRunFixupKind.READYTORUN_FIXUP_NewObject, targetModule, _signatureContext); + dataBuilder.EmitTypeSignature(_typeDesc, innerContext); } return dataBuilder.ToObjectData(); @@ -43,7 +44,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append($@"NewObjectSignature: {_typeDesc.ToString()}"); + sb.Append($@"NewObjectSignature: "); + sb.Append(nameMangler.GetMangledTypeName(_typeDesc)); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs index 919089c56aa..52ec980b73a 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs @@ -298,23 +298,39 @@ public void EmitTypeSignature(TypeDesc typeDesc, SignatureContext context) } else { + ModuleToken token = context.GetModuleTokenForType((EcmaType)typeDesc); + EmitModuleOverride(token.Module, context); EmitElementType(CorElementType.ELEMENT_TYPE_CLASS); - EmitTypeToken((EcmaType)typeDesc, context); + EmitToken(token.Token); } return; case TypeFlags.ValueType: case TypeFlags.Nullable: case TypeFlags.Enum: - EmitElementType(CorElementType.ELEMENT_TYPE_VALUETYPE); - EmitTypeToken((EcmaType)typeDesc, context); - return; + { + ModuleToken token = context.GetModuleTokenForType((EcmaType)typeDesc); + EmitModuleOverride(token.Module, context); + EmitElementType(CorElementType.ELEMENT_TYPE_VALUETYPE); + EmitToken(token.Token); + return; + } default: throw new NotImplementedException(); } } + private void EmitModuleOverride(EcmaModule module, SignatureContext context) + { + if (module != context.LocalContext) + { + EmitElementType(CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG); + uint moduleIndex = (uint)context.Resolver.GetModuleIndex(module); + EmitUInt(moduleIndex); + } + } + private void EmitTypeToken(EcmaType type, SignatureContext context) { ModuleToken token = context.GetModuleTokenForType(type); @@ -395,7 +411,7 @@ public void EmitMethodSignature( { if (methodToken.IsNull) { - methodToken = context.GetModuleTokenForMethod(method.GetTypicalMethodDefinition()); + methodToken = context.GetModuleTokenForMethod(method); } switch (methodToken.TokenType) { @@ -462,21 +478,17 @@ private void EmitMethodSpecificationSignature(MethodDesc method, ModuleToken met MethodSpecification methodSpecification = methodToken.MetadataReader.GetMethodSpecification((MethodSpecificationHandle)methodToken.Handle); methodToken = new ModuleToken(methodToken.Module, methodSpecification.Method); } - else - { - throw new NotImplementedException(); - } } } if (methodToken.IsNull && !enforceDefEncoding) { - methodToken = context.GetModuleTokenForMethod(method.GetMethodDefinition(), throwIfNotFound: false); + methodToken = context.GetModuleTokenForMethod(method, throwIfNotFound: false); } if (methodToken.IsNull) { flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType; - methodToken = context.GetModuleTokenForMethod(method.GetTypicalMethodDefinition()); + methodToken = context.GetModuleTokenForMethod(method); } if (method.OwningType.HasInstantiation) @@ -582,6 +594,21 @@ public ObjectNode.ObjectData ToObjectData() { return _builder.ToObjectData(); } + + public SignatureContext EmitFixup(ReadyToRunCodegenNodeFactory factory, ReadyToRunFixupKind fixupKind, EcmaModule targetModule, SignatureContext outerContext) + { + if (targetModule == outerContext.LocalContext) + { + EmitByte((byte)fixupKind); + return outerContext; + } + else + { + EmitByte((byte)(fixupKind | ReadyToRunFixupKind.READYTORUN_FIXUP_ModuleOverride)); + EmitUInt((uint)factory.ManifestMetadataTable.ModuleToIndex(targetModule)); + return outerContext.InnerContext(targetModule); + } + } } internal class ArraySignatureBuilder : SignatureBuilder diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs index c185ed17ee8..6980e9b3598 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs @@ -15,26 +15,73 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class SignatureContext { - private readonly ModuleTokenResolver _resolver; + /// + /// Context module used for the signature. Whenever a part of the signature + /// needs to encode an entity external to the context module, it muse use + /// an ELEMENT_TYPE_MODULE_ZAPSIG module override. + /// + public readonly EcmaModule GlobalContext; - public SignatureContext(ModuleTokenResolver resolver) + /// + /// Local context changes during recursive descent while encoding the signature. + /// + public readonly EcmaModule LocalContext; + + /// + /// Resolver used to back-translate types and fields to tokens. + /// + public readonly ModuleTokenResolver Resolver; + + public SignatureContext(EcmaModule context, ModuleTokenResolver resolver) + { + GlobalContext = context; + LocalContext = context; + Resolver = resolver; + } + + private SignatureContext(EcmaModule globalContext, EcmaModule localContext, ModuleTokenResolver resolver) + { + GlobalContext = globalContext; + LocalContext = localContext; + Resolver = resolver; + } + + public SignatureContext InnerContext(EcmaModule innerContext) + { + return new SignatureContext(GlobalContext, innerContext, Resolver); + } + + public EcmaModule GetTargetModule(TypeDesc type) + { + if (type.IsPrimitive) + { + return LocalContext; + } + if (type.GetTypeDefinition() is EcmaType ecmaType) + { + return GetModuleTokenForType(ecmaType).Module; + } + return LocalContext; + } + + public EcmaModule GetTargetModule(FieldDesc field) { - _resolver = resolver; + return GetModuleTokenForField(field).Module; } public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = true) { - return _resolver.GetModuleTokenForType(type, throwIfNotFound); + return Resolver.GetModuleTokenForType(type, throwIfNotFound); } public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool throwIfNotFound = true) { - return _resolver.GetModuleTokenForMethod(method, throwIfNotFound); + return Resolver.GetModuleTokenForMethod(method, throwIfNotFound); } public ModuleToken GetModuleTokenForField(FieldDesc field, bool throwIfNotFound = true) { - return _resolver.GetModuleTokenForField(field, throwIfNotFound); + return Resolver.GetModuleTokenForField(field, throwIfNotFound); } } } diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/StringImport.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/StringImport.cs index f89f65cab78..4374b489333 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/StringImport.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/StringImport.cs @@ -14,8 +14,8 @@ public class StringImport : Import private int _definitionOffset; - public StringImport(ImportSectionNode table, ModuleToken token) - : base(table, new StringImportSignature(token)) + public StringImport(ImportSectionNode table, ModuleToken token, SignatureContext signatureContext) + : base(table, new StringImportSignature(token, signatureContext)) { _token = token; } diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs index 15ac93c05d1..f15cdabd072 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs @@ -11,9 +11,12 @@ public class StringImportSignature : Signature { private readonly ModuleToken _token; - public StringImportSignature(ModuleToken token) + private readonly SignatureContext _signatureContext; + + public StringImportSignature(ModuleToken token, SignatureContext signatureContext) { _token = token; + _signatureContext = signatureContext; } public override int ClassCode => 324832559; @@ -24,8 +27,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(); dataBuilder.AddSymbol(this); - // TODO: module override for external module strings - dataBuilder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_StringHandle); + dataBuilder.EmitFixup(r2rFactory, ReadyToRunFixupKind.READYTORUN_FIXUP_StringHandle, _token.Module, _signatureContext); dataBuilder.EmitUInt(_token.TokenRid); return dataBuilder.ToObjectData(); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs index 01772b5d1ae..dca54e1dde6 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs @@ -4,9 +4,9 @@ using System; -using Internal.JitInterface; using Internal.Text; using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis.ReadyToRun { @@ -36,8 +36,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { dataBuilder.AddSymbol(this); - dataBuilder.EmitByte((byte)_fixupKind); - dataBuilder.EmitTypeSignature(_typeDesc, _signatureContext); + EcmaModule targetModule = _signatureContext.GetTargetModule(_typeDesc); + SignatureContext innerContext = dataBuilder.EmitFixup(r2rFactory, _fixupKind, targetModule, _signatureContext); + dataBuilder.EmitTypeSignature(_typeDesc, innerContext); } return dataBuilder.ToObjectData(); @@ -46,7 +47,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append($@"TypeFixupSignature({_fixupKind.ToString()}): {_typeDesc.ToString()}"); + sb.Append($@"TypeFixupSignature({_fixupKind.ToString()}): "); + sb.Append(nameMangler.GetMangledTypeName(_typeDesc)); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index fff772a53be..9722c4deb6d 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -61,6 +61,8 @@ public ReadyToRunCodegenNodeFactory( public InstanceEntryPointTableNode InstanceEntryPointTable; + public ManifestMetadataTableNode ManifestMetadataTable; + public TypesTableNode TypesTable; public ImportSectionsTableNode ImportSectionsTable; @@ -337,13 +339,18 @@ public override void AttachToDependencyGraph(DependencyAnalyzerBase MethodEntryPointTable = new MethodEntryPointTableNode(Target); Header.Add(Internal.Runtime.ReadyToRunSectionType.MethodDefEntryPoints, MethodEntryPointTable, MethodEntryPointTable); + ManifestMetadataTable = new ManifestMetadataTableNode(InputModuleContext.GlobalContext); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable); + + Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex); + InstanceEntryPointTable = new InstanceEntryPointTableNode(Target); Header.Add(Internal.Runtime.ReadyToRunSectionType.InstanceMethodEntryPoints, InstanceEntryPointTable, InstanceEntryPointTable); TypesTable = new TypesTableNode(Target); Header.Add(Internal.Runtime.ReadyToRunSectionType.AvailableTypes, TypesTable, TypesTable); - ImportSectionsTable = new ImportSectionsTableNode(Target); + ImportSectionsTable = new ImportSectionsTableNode(this); Header.Add(Internal.Runtime.ReadyToRunSectionType.ImportSections, ImportSectionsTable, ImportSectionsTable.StartSymbol); DebugInfoTable = new DebugInfoTableNode(Target); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index 0f7427c7d3d..87b9f1485f2 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -27,11 +27,11 @@ public ReadyToRunSymbolNodeFactory(ReadyToRunCodegenNodeFactory codegenNodeFacto private readonly Dictionary _importStrings = new Dictionary(); - public ISymbolNode StringLiteral(ModuleToken token) + public ISymbolNode StringLiteral(ModuleToken token, SignatureContext signatureContext) { if (!_importStrings.TryGetValue(token, out ISymbolNode stringNode)) { - stringNode = new StringImport(_codegenNodeFactory.StringImports, token); + stringNode = new StringImport(_codegenNodeFactory.StringImports, token, signatureContext); _importStrings.Add(token, stringNode); } return stringNode; diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunCodegenCompilationBuilder.cs index 1ad1a0de9c6..4433b8d0353 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -79,7 +79,7 @@ public override ICompilation ToCompilation() var interopStubManager = new EmptyInteropStubManager(_compilationGroup, _context, new InteropStateManager(_context.GeneratedAssembly)); ModuleTokenResolver moduleTokenResolver = new ModuleTokenResolver(_compilationGroup, _context); - SignatureContext signatureContext = new SignatureContext(moduleTokenResolver); + SignatureContext signatureContext = new SignatureContext(_inputModule, moduleTokenResolver); ReadyToRunCodegenNodeFactory factory = new ReadyToRunCodegenNodeFactory( _context, diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/RuntimeDeterminedTypeHelper.cs b/src/ILCompiler.ReadyToRun/src/Compiler/RuntimeDeterminedTypeHelper.cs index dc1ad99b9be..a52d7dc818b 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/RuntimeDeterminedTypeHelper.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/RuntimeDeterminedTypeHelper.cs @@ -151,109 +151,5 @@ public static int GetHashCode(FieldDesc field) { return unchecked(GetHashCode(field.OwningType) + 97 * GetHashCode(field.FieldType) + 31 * field.Name.GetHashCode()); } - - public static void WriteTo(Instantiation instantiation, Utf8StringBuilder sb) - { - sb.Append("<"); - for (int typeArgIndex = 0; typeArgIndex < instantiation.Length; typeArgIndex++) - { - if (typeArgIndex != 0) - { - sb.Append(", "); - } - WriteTo(instantiation[typeArgIndex], sb); - } - sb.Append(">"); - } - - public static void WriteTo(TypeDesc type, Utf8StringBuilder sb) - { - if (type is RuntimeDeterminedType runtimeDeterminedType) - { - switch (runtimeDeterminedType.RuntimeDeterminedDetailsType.Kind) - { - case GenericParameterKind.Type: - sb.Append("T"); - break; - - case GenericParameterKind.Method: - sb.Append("M"); - break; - - default: - throw new NotImplementedException(); - } - sb.Append(runtimeDeterminedType.RuntimeDeterminedDetailsType.Index.ToString()); - } - else if (type is InstantiatedType instantiatedType) - { - sb.Append(instantiatedType.GetTypeDefinition().ToString()); - WriteTo(instantiatedType.Instantiation, sb); - } - else if (type is ArrayType arrayType) - { - WriteTo(arrayType.ElementType, sb); - sb.Append("["); - switch (arrayType.Rank) - { - case 0: - break; - case 1: - sb.Append("*"); - break; - default: - sb.Append(new String(',', arrayType.Rank - 1)); - break; - } - sb.Append("]"); - } - else if (type is ByRefType byRefType) - { - WriteTo(byRefType.ParameterType, sb); - sb.Append("&"); - } - else if (type is PointerType pointerType) - { - WriteTo(pointerType.ParameterType, sb); - sb.Append("*"); - } - else - { - Debug.Assert(type is DefType); - sb.Append(type.ToString()); - } - } - - public static void WriteTo(MethodDesc method, Utf8StringBuilder sb) - { - WriteTo(method.Signature.ReturnType, sb); - sb.Append(" "); - WriteTo(method.OwningType, sb); - sb.Append("."); - sb.Append(method.Name); - if (method.HasInstantiation) - { - WriteTo(method.Instantiation, sb); - } - sb.Append("("); - for (int argIndex = 0; argIndex < method.Signature.Length; argIndex++) - { - if (argIndex != 0) - { - sb.Append(", "); - } - WriteTo(method.Signature[argIndex], sb); - } - sb.Append(")"); - } - - public static void WriteTo(FieldDesc field, Utf8StringBuilder sb) - { - WriteTo(field.FieldType, sb); - sb.Append(" "); - WriteTo(field.OwningType, sb); - sb.Append("."); - sb.Append(field.Name); - } } } diff --git a/src/ILCompiler.ReadyToRun/src/ILCompiler.ReadyToRun.csproj b/src/ILCompiler.ReadyToRun/src/ILCompiler.ReadyToRun.csproj index 302bc2f30f1..c0cd01a7c5f 100644 --- a/src/ILCompiler.ReadyToRun/src/ILCompiler.ReadyToRun.csproj +++ b/src/ILCompiler.ReadyToRun/src/ILCompiler.ReadyToRun.csproj @@ -54,6 +54,7 @@ + diff --git a/src/ILCompiler.ReadyToRun/src/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/ILCompiler.ReadyToRun/src/JitInterface/CorInfoImpl.ReadyToRun.cs index 4e6870b43cc..a405af622f6 100644 --- a/src/ILCompiler.ReadyToRun/src/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/ILCompiler.ReadyToRun/src/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -106,14 +106,11 @@ unsafe partial class CorInfoImpl /// private readonly EcmaModule _tokenContext; - private readonly SignatureContext _signatureContext; - public CorInfoImpl(ReadyToRunCodegenCompilation compilation, EcmaModule tokenContext, JitConfigProvider jitConfig) : this(jitConfig) { _compilation = compilation; _tokenContext = tokenContext; - _signatureContext = new SignatureContext(_compilation.NodeFactory.Resolver); } public void CompileMethod(IReadyToRunMethodCodeNode methodCodeNodeNeedingCode) @@ -180,11 +177,17 @@ private void ComputeLookup(ref CORINFO_RESOLVED_TOKEN pResolvedToken, object ent targetEntity = new MethodWithToken(methodDesc, new ModuleToken(_tokenContext, pResolvedToken.token)); } lookup.lookupKind.needsRuntimeLookup = false; - ISymbolNode constLookup = _compilation.SymbolNodeFactory.ComputeConstantLookup(helperId, targetEntity, _signatureContext); + ISymbolNode constLookup = _compilation.SymbolNodeFactory.ComputeConstantLookup(helperId, targetEntity, GetSignatureContext()); lookup.constLookup = CreateConstLookupToSymbol(constLookup); } } + private SignatureContext GetSignatureContext() + { + // TODO: this will need changing when compiling multiple input MSIL modules into a single PE executable + return _compilation.NodeFactory.InputModuleContext; + } + private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref CORINFO_LOOKUP_KIND pGenericLookupKind, CorInfoHelpFunc id, ref CORINFO_CONST_LOOKUP pLookup) { switch (id) @@ -196,7 +199,7 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) return false; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.NewHelper, type, _signatureContext)); + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.NewHelper, type, GetSignatureContext())); } break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NEWARR_1: @@ -206,7 +209,7 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) return false; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.NewArr1, type, _signatureContext)); + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.NewArr1, type, GetSignatureContext())); } break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_ISINSTANCEOF: @@ -219,7 +222,7 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref if (type.IsNullable) type = type.Instantiation[0]; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.IsInstanceOf, type, _signatureContext)); + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.IsInstanceOf, type, GetSignatureContext())); } break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_CHKCAST: @@ -232,7 +235,7 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref if (type.IsNullable) type = type.Instantiation[0]; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.CastClass, type, _signatureContext)); + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.CastClass, type, GetSignatureContext())); } break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_STATIC_BASE: @@ -241,7 +244,7 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) return false; - pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.CctorTrigger, type, _signatureContext)); + pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper(ReadyToRunHelperId.CctorTrigger, type, GetSignatureContext())); } break; case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_HANDLE: @@ -267,7 +270,7 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref helperArg, constrainedType, methodContext, - _signatureContext); + GetSignatureContext()); pLookup = CreateConstLookupToSymbol(helper); } break; @@ -291,7 +294,7 @@ private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetM pLookup.lookupKind.needsRuntimeLookup = false; pLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.DelegateCtor( - delegateTypeDesc, targetMethod, new ModuleToken(_tokenContext, (mdToken)pTargetMethod.token), _signatureContext)); + delegateTypeDesc, targetMethod, new ModuleToken(_tokenContext, (mdToken)pTargetMethod.token), GetSignatureContext())); } private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) @@ -629,7 +632,7 @@ private bool canTailCall(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUC private InfoAccessType constructStringLiteral(CORINFO_MODULE_STRUCT_* module, mdToken metaTok, ref void* ppValue) { - ISymbolNode stringObject = _compilation.SymbolNodeFactory.StringLiteral(new ModuleToken(_tokenContext, metaTok)); + ISymbolNode stringObject = _compilation.SymbolNodeFactory.StringLiteral(new ModuleToken(_tokenContext, metaTok), GetSignatureContext()); ppValue = (void*)ObjectToHandle(stringObject); return InfoAccessType.IAT_PPVALUE; } @@ -1167,7 +1170,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( _compilation.SymbolNodeFactory.InterfaceDispatchCell(targetMethod, new ModuleToken(callerModule, (mdToken)pResolvedToken.token), - _signatureContext, + GetSignatureContext(), isUnboxingStub: false, _compilation.NameMangler.GetMangledMethodName(MethodBeingCompiled).ToString())); } @@ -1194,7 +1197,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO new ModuleToken(callerModule, pResolvedToken.token), isUnboxingStub: false, isInstantiatingStub: useInstantiatingStub, - _signatureContext)); + GetSignatureContext())); } break; @@ -1210,7 +1213,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( _compilation.NodeFactory.DynamicHelperCell( new MethodWithToken(targetMethod, new ModuleToken(callerModule, pResolvedToken.token)), - _signatureContext)); + GetSignatureContext())); Debug.Assert(!pResult->sig.hasTypeArg()); } @@ -1238,14 +1241,14 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO pResult->instParamLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper( ReadyToRunHelperId.MethodDictionary, new MethodWithToken(targetMethod, new ModuleToken(callerModule, pResolvedToken.token)), - signatureContext: _signatureContext)); + signatureContext: GetSignatureContext())); } else { pResult->instParamLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.ReadyToRunHelper( ReadyToRunHelperId.TypeDictionary, exactType, - signatureContext: _signatureContext)); + signatureContext: GetSignatureContext())); } } } @@ -1271,7 +1274,7 @@ private void ComputeRuntimeLookupForSharedGenericToken( pResult.indirections = CORINFO.USEHELPER; MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext); - TypeDesc contextType = contextMethod.OwningType; + TypeDesc contextType = typeFromContext(pResolvedToken.tokenContext); // Do not bother computing the runtime lookup if we are inlining. The JIT is going // to abort the inlining attempt anyway. @@ -1474,21 +1477,21 @@ private void embedGenericHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool symbolNode = _compilation.SymbolNodeFactory.ReadyToRunHelper( ReadyToRunHelperId.TypeHandle, HandleToObject(pResolvedToken.hClass), - _signatureContext); + GetSignatureContext()); break; case CorInfoGenericHandleType.CORINFO_HANDLETYPE_METHOD: symbolNode = _compilation.SymbolNodeFactory.ReadyToRunHelper( ReadyToRunHelperId.MethodHandle, new MethodWithToken(HandleToObject(pResolvedToken.hMethod), new ModuleToken(_tokenContext, pResolvedToken.token)), - _signatureContext); + GetSignatureContext()); break; case CorInfoGenericHandleType.CORINFO_HANDLETYPE_FIELD: symbolNode = _compilation.SymbolNodeFactory.ReadyToRunHelper( ReadyToRunHelperId.FieldHandle, HandleToObject(pResolvedToken.hField), - _signatureContext); + GetSignatureContext()); break; default: @@ -1609,7 +1612,7 @@ private void EncodeFieldBaseOffset(FieldDesc field, CORINFO_FIELD_INFO* pResult, // ENCODE_FIELD_OFFSET pResult->offset = 0; pResult->fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_INSTANCE_WITH_BASE; - pResult->fieldLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.FieldOffset(field, _signatureContext)); + pResult->fieldLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.FieldOffset(field, GetSignatureContext())); } } else if (pMT.IsValueType) @@ -1629,7 +1632,7 @@ private void EncodeFieldBaseOffset(FieldDesc field, CORINFO_FIELD_INFO* pResult, // ENCODE_FIELD_OFFSET pResult->offset = 0; pResult->fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_INSTANCE_WITH_BASE; - pResult->fieldLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.FieldOffset(field, _signatureContext)); + pResult->fieldLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.FieldOffset(field, GetSignatureContext())); } else { @@ -1638,7 +1641,7 @@ private void EncodeFieldBaseOffset(FieldDesc field, CORINFO_FIELD_INFO* pResult, // ENCODE_FIELD_BASE_OFFSET pResult->offset -= (uint)pMT.BaseType.InstanceByteCount.AsInt; pResult->fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_INSTANCE_WITH_BASE; - pResult->fieldLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.FieldBaseOffset(field.OwningType, _signatureContext)); + pResult->fieldLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.FieldBaseOffset(field.OwningType, GetSignatureContext())); } } } diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index 44c82e87a94..fce67a685c6 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -402,15 +402,25 @@ private int Run(string[] args) foreach (var inputFile in typeSystemContext.InputFilePaths) { EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); - compilationRoots.Add(new ReadyToRunRootProvider(module)); inputModules.Add(module); + if (!_isInputVersionBubble) { break; } } + if (_isInputVersionBubble) + { + // In large version bubble mode add reference paths to the compilation group + foreach (KeyValuePair referenceFile in _referenceFilePaths) + { + EcmaModule module = typeSystemContext.GetModuleFromPath(referenceFile.Value); + inputModules.Add(module); + } + } + compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(typeSystemContext, inputModules); } else if (_multiFile) diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs index d5f311400ff..4f3e3e4962f 100644 --- a/src/JitInterface/src/CorInfoImpl.cs +++ b/src/JitInterface/src/CorInfoImpl.cs @@ -2150,7 +2150,7 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET // Static fields outside of the version bubble need to be accessed using the ENCODE_FIELD_ADDRESS // helper in accordance with ZapInfo::getFieldInfo in CoreCLR. - pResult->fieldLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.FieldAddress(field, _signatureContext)); + pResult->fieldLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.FieldAddress(field, GetSignatureContext())); pResult->helper = CorInfoHelpFunc.CORINFO_HELP_READYTORUN_STATIC_BASE; @@ -2164,7 +2164,7 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET { pResult->fieldLookup = CreateConstLookupToSymbol( #if READYTORUN - _compilation.SymbolNodeFactory.ReadyToRunHelper(helperId, field.OwningType, _signatureContext) + _compilation.SymbolNodeFactory.ReadyToRunHelper(helperId, field.OwningType, GetSignatureContext()) #else _compilation.NodeFactory.ReadyToRunHelper(helperId, field.OwningType) #endif From 42109b6051051b3789b1213f83a918ed02e82709 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Tue, 14 May 2019 22:56:19 -0700 Subject: [PATCH 2/4] Cleanly split input and version bubble in R2R compilation group Based on PR discussion I have modified the R2R single assembly compilation module group to keep input assemblies separate from the version bubble module set. With this change, CPAOT dependency analysis no longer attempts to compose the code graph across all assemblies, not only those being actually compiled, while keeping the more relaxed inlining conditions in case of larger version bubble. Based on this additional delta I could finally remove the artificial filtering of methods in MethodEntryPointTableNode. To facilitate this clean separation, I have added two new methods to CompilationModuleGroup for the version bubble checks - VersionsWithType and VersionsWithMethodBody. I have subsequently audited all CompilationModuleGroup queries in the CPAOT compiler and I changed the checks semantically referring to the version bubble to use the new methods. Thanks Tomas --- .../src/Compiler/CompilationModuleGroup.cs | 16 +++++++ .../ReadyToRun/FieldFixupSignature.cs | 2 +- .../ReadyToRun/MethodEntryPointTableNode.cs | 2 +- .../ReadyToRun/ModuleTokenResolver.cs | 10 ++--- .../ReadyToRun/SignatureContext.cs | 2 +- ...RunSingleAssemblyCompilationModuleGroup.cs | 43 +++++++++++++------ .../JitInterface/CorInfoImpl.ReadyToRun.cs | 12 +++--- src/ILCompiler/src/Program.cs | 19 ++++++-- src/JitInterface/src/CorInfoImpl.cs | 2 +- 9 files changed, 75 insertions(+), 33 deletions(-) diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilationModuleGroup.cs b/src/ILCompiler.Compiler/src/Compiler/CompilationModuleGroup.cs index 316a31de731..4c8101df202 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilationModuleGroup.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilationModuleGroup.cs @@ -100,5 +100,21 @@ public abstract class CompilationModuleGroup /// The called method to be inlined into the caller /// public virtual bool CanInline(MethodDesc callerMethod, MethodDesc calleeMethod) => true; + + /// + /// Returns true when a given type belongs to the same version bubble as the compilation module group. + /// By default return the same outcome as ContainsType. + /// + /// Type to check + /// True if the given type versions with the current compilation module group + public virtual bool VersionsWithType(TypeDesc typeDesc) => ContainsType(typeDesc); + + /// + /// Returns true when a given method belongs to the same version bubble as the compilation module group. + /// By default return the same outcome as ContainsMethodBody. + /// + /// Method to check + /// True if the given method versions with the current compilation module group + public virtual bool VersionsWithMethodBody(MethodDesc methodDesc) => ContainsMethodBody(methodDesc, unboxingStub: false); } } diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs index 3be2b4bd964..ba0dda78b9f 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs @@ -37,7 +37,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { dataBuilder.AddSymbol(this); - EcmaModule targetModule = _signatureContext.GetModuleTokenForField(_fieldDesc).Module; + EcmaModule targetModule = _signatureContext.GetTargetModule(_fieldDesc); SignatureContext innerContext = dataBuilder.EmitFixup(r2rFactory, _fixupKind, targetModule, _signatureContext); dataBuilder.EmitFieldSignature(_fieldDesc, innerContext); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs index 549fdd02aa2..c18fa70972f 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/MethodEntryPointTableNode.cs @@ -57,7 +57,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (MethodWithGCInfo method in r2rFactory.EnumerateCompiledMethods()) { - if (method.Method is EcmaMethod ecmaMethod && r2rFactory.InputModuleContext.GlobalContext == ecmaMethod.Module) + if (method.Method is EcmaMethod ecmaMethod) { // Strip away the token type bits, keep just the low 24 bits RID uint rid = SignatureBuilder.RidFromToken((mdToken)MetadataTokens.GetToken(ecmaMethod.Handle)); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs index 3c6a5f9e18c..22e040ff1dc 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs @@ -48,7 +48,7 @@ public void SetModuleIndexLookup(Func moduleIndexLookup) public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = true) { - if (_compilationModuleGroup.ContainsType(type)) + if (_compilationModuleGroup.VersionsWithType(type)) { return new ModuleToken(type.EcmaModule, (mdToken)MetadataTokens.GetToken(type.Handle)); } @@ -74,7 +74,7 @@ public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool throwIfNotFou { method = method.GetCanonMethodTarget(CanonicalFormKind.Specific); - if (_compilationModuleGroup.ContainsMethodBody(method, unboxingStub: false) && + if (_compilationModuleGroup.VersionsWithMethodBody(method) && method is EcmaMethod ecmaMethod) { return new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle); @@ -93,7 +93,7 @@ public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool throwIfNotFou public ModuleToken GetModuleTokenForField(FieldDesc field, bool throwIfNotFound = true) { - if (_compilationModuleGroup.ContainsType(field.OwningType) && field is EcmaField ecmaField) + if (_compilationModuleGroup.VersionsWithType(field.OwningType) && field is EcmaField ecmaField) { return new ModuleToken(ecmaField.Module, ecmaField.Handle); } @@ -148,7 +148,7 @@ private void AddModuleTokenForFieldReference(TypeDesc owningType, ModuleToken to public void AddModuleTokenForField(FieldDesc field, ModuleToken token) { - if (_compilationModuleGroup.ContainsType(field.OwningType) && field.OwningType is EcmaType) + if (_compilationModuleGroup.VersionsWithType(field.OwningType) && field.OwningType is EcmaType) { // We don't need to store handles within the current compilation group // as we can read them directly from the ECMA objects. @@ -185,7 +185,7 @@ public void AddModuleTokenForType(TypeDesc type, ModuleToken token) specialTypeFound = true; } - if (_compilationModuleGroup.ContainsType(type)) + if (_compilationModuleGroup.VersionsWithType(type)) { // We don't need to store handles within the current compilation group // as we can read them directly from the ECMA objects. diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs index 6980e9b3598..6b19de7eb4a 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs @@ -66,7 +66,7 @@ public EcmaModule GetTargetModule(TypeDesc type) public EcmaModule GetTargetModule(FieldDesc field) { - return GetModuleTokenForField(field).Module; + return GetTargetModule(field.OwningType); } public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = true) diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs b/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs index 52b61b10ae1..e70764102b5 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs @@ -2,10 +2,10 @@ // 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 ILCompiler.DependencyAnalysis; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -14,26 +14,25 @@ namespace ILCompiler public class ReadyToRunSingleAssemblyCompilationModuleGroup : CompilationModuleGroup { private HashSet _compilationModuleSet; + private HashSet _versionBubbleModuleSet; - public ReadyToRunSingleAssemblyCompilationModuleGroup(TypeSystemContext context, IEnumerable compilationModuleSet) + public ReadyToRunSingleAssemblyCompilationModuleGroup( + TypeSystemContext context, + IEnumerable compilationModuleSet, + IEnumerable versionBubbleModuleSet) { _compilationModuleSet = new HashSet(compilationModuleSet); // The fake assembly that holds compiler generated types is part of the compilation. _compilationModuleSet.Add(context.GeneratedAssembly); + + _versionBubbleModuleSet = new HashSet(versionBubbleModuleSet); + _versionBubbleModuleSet.UnionWith(_compilationModuleSet); } public sealed override bool ContainsType(TypeDesc type) { - if (type is EcmaType ecmaType) - { - return IsModuleInCompilationGroup(ecmaType.EcmaModule); - } - if (type is InstantiatedType instantiatedType) - { - return ContainsType(instantiatedType.GetTypeDefinition()); - } - return true; + return type.GetTypeDefinition() is EcmaType ecmaType && IsModuleInCompilationGroup(ecmaType.EcmaModule); } public sealed override bool ContainsTypeDictionary(TypeDesc type) @@ -206,11 +205,27 @@ private bool ContainsTypeLayoutUncached(TypeDesc type, HashSet recursi return true; } + public override bool VersionsWithType(TypeDesc typeDesc) + { + return typeDesc.GetTypeDefinition() is EcmaType ecmaType && + _versionBubbleModuleSet.Contains(ecmaType.EcmaModule); + } + + public override bool VersionsWithMethodBody(MethodDesc method) + { + return VersionsWithType(method.OwningType); + } + public override bool CanInline(MethodDesc callerMethod, MethodDesc calleeMethod) { - // Allow inlining if the target method is within the same version bubble - return ContainsMethodBody(calleeMethod, unboxingStub: false) || - calleeMethod.HasCustomAttribute("System.Runtime.Versioning", "NonVersionableAttribute"); + // Allow inlining if the caller is within the current version bubble + // (because otherwise we may not be able to encode its tokens) + // and if the callee is either in the same version bubble or is marked as non-versionable. + bool canInline = VersionsWithMethodBody(callerMethod) && + (VersionsWithMethodBody(calleeMethod) || + calleeMethod.HasCustomAttribute("System.Runtime.Versioning", "NonVersionableAttribute")); + + return canInline; } } } diff --git a/src/ILCompiler.ReadyToRun/src/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/ILCompiler.ReadyToRun/src/JitInterface/CorInfoImpl.ReadyToRun.cs index a405af622f6..96ae6ae9f18 100644 --- a/src/ILCompiler.ReadyToRun/src/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/ILCompiler.ReadyToRun/src/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -812,7 +812,7 @@ private void ceeInfoGetCallInfo( TypeDesc type = HandleToObject(pResolvedToken.hClass); callerMethod = HandleToObject(callerHandle); - if (!_compilation.NodeFactory.CompilationModuleGroup.ContainsMethodBody(callerMethod, unboxingStub: false)) + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(callerMethod)) { // We must abort inline attempts calling from outside of the version bubble being compiled // because we have no way to remap the token relative to the external module to the current version bubble. @@ -964,8 +964,8 @@ private void ceeInfoGetCallInfo( // we have to apply more restrictive rules // These rules are related to the "inlining rules" as far as the // boundaries of a version bubble are concerned. - if (!_compilation.NodeFactory.CompilationModuleGroup.ContainsMethodBody(callerMethod, unboxingStub: false) || - !_compilation.NodeFactory.CompilationModuleGroup.ContainsMethodBody(targetMethod, unboxingStub: false)) + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(callerMethod) || + !_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(targetMethod)) { // For version resiliency we won't de-virtualize all final/sealed method calls. Because during a // servicing event it is legal to unseal a method or type. @@ -1120,7 +1120,7 @@ private void ceeInfoGetCallInfo( private bool MethodInSystemVersionBubble(MethodDesc method) { - return _compilation.NodeFactory.CompilationModuleGroup.ContainsMethodBody(method, unboxingStub: false) && + return _compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(method) && method.OwningType.GetTypeDefinition().GetClosestDefType() is MetadataType metadataType && metadataType.Module == _compilation.TypeSystemContext.SystemModule; } @@ -1517,7 +1517,7 @@ private bool IsLayoutFixedInCurrentVersionBubble(TypeDesc type) return true; } - if (!_compilation.NodeFactory.CompilationModuleGroup.ContainsType(type)) + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(type)) { if (!type.IsValueType) { @@ -1582,7 +1582,7 @@ private bool HasLayoutMetadata(TypeDesc type) /// private void PreventRecursiveFieldInlinesOutsideVersionBubble(FieldDesc field, MethodDesc callerMethod) { - if (!_compilation.NodeFactory.CompilationModuleGroup.ContainsMethodBody(callerMethod, unboxingStub: false)) + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(callerMethod)) { // Prevent recursive inline attempts where an inlined method outside of the version bubble is // referencing fields outside the version bubble. diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index fce67a685c6..f9cd77813c5 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -411,17 +411,28 @@ private int Run(string[] args) } } + + List versionBubbleModules = new List(); if (_isInputVersionBubble) { // In large version bubble mode add reference paths to the compilation group - foreach (KeyValuePair referenceFile in _referenceFilePaths) + foreach (string referenceFile in _referenceFilePaths.Values) { - EcmaModule module = typeSystemContext.GetModuleFromPath(referenceFile.Value); - inputModules.Add(module); + try + { + EcmaModule module = typeSystemContext.GetModuleFromPath(referenceFile); + versionBubbleModules.Add(module); + + } + catch (Exception ex) + { + Console.WriteLine("Warning: cannot open reference assembly '{0}': {1}", referenceFile, ex.Message); + } } } - compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(typeSystemContext, inputModules); + compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup( + typeSystemContext, inputModules, versionBubbleModules); } else if (_multiFile) { diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs index 4f3e3e4962f..cc70ce12321 100644 --- a/src/JitInterface/src/CorInfoImpl.cs +++ b/src/JitInterface/src/CorInfoImpl.cs @@ -984,7 +984,7 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) #if READYTORUN TypeDesc owningType = methodIL.OwningMethod.GetTypicalMethodDefinition().OwningType; EcmaModule tokenContextToRecord = null; - if (_compilation.NodeFactory.CompilationModuleGroup.ContainsType(owningType) && + if (_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(owningType) && owningType is EcmaType owningEcmaType) { tokenContextToRecord = owningEcmaType.EcmaModule; From 849d06905fa1a4f3778f120e5db8ff5910c746ee Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Sat, 18 May 2019 10:21:38 -0700 Subject: [PATCH 3/4] Fix signature reference module for object and string When determining reference module for encoding type signatures, we're special-casing primitive types as they don't need any reference module and use special constants in the signature encoding scheme. I previously didn't realize that object and string need the same treatment. I have also added an explanatory comment to Program.cs as to why we're using a try block when opening reference modules. Thanks Tomas --- .../Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs | 2 +- src/ILCompiler/src/Program.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs index 6b19de7eb4a..667b4ef76f9 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs @@ -53,7 +53,7 @@ public SignatureContext InnerContext(EcmaModule innerContext) public EcmaModule GetTargetModule(TypeDesc type) { - if (type.IsPrimitive) + if (type.IsPrimitive || type.IsString || type.IsObject) { return LocalContext; } diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index f9cd77813c5..61164539ac7 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -420,9 +420,10 @@ private int Run(string[] args) { try { + // Currently SimpleTest.targets has no easy way to filter out non-managed assemblies + // from the reference list. EcmaModule module = typeSystemContext.GetModuleFromPath(referenceFile); versionBubbleModules.Add(module); - } catch (Exception ex) { From c228c809ae81435c45c850a9a2de8992ad89dae2 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Sun, 19 May 2019 21:00:03 -0700 Subject: [PATCH 4/4] Only catch BadImageFormatException when opening reference assemblies --- src/ILCompiler/src/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index 61164539ac7..b69e7872821 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -425,7 +425,7 @@ private int Run(string[] args) EcmaModule module = typeSystemContext.GetModuleFromPath(referenceFile); versionBubbleModules.Add(module); } - catch (Exception ex) + catch (BadImageFormatException ex) { Console.WriteLine("Warning: cannot open reference assembly '{0}': {1}", referenceFile, ex.Message); }