diff --git a/src/coreclr/src/tools/crossgen2/Common/Internal/NativeFormat/NativeFormatWriter.cs b/src/coreclr/src/tools/crossgen2/Common/Internal/NativeFormat/NativeFormatWriter.cs index c170e3707f8ea..09f5555e0f916 100644 --- a/src/coreclr/src/tools/crossgen2/Common/Internal/NativeFormat/NativeFormatWriter.cs +++ b/src/coreclr/src/tools/crossgen2/Common/Internal/NativeFormat/NativeFormatWriter.cs @@ -6,6 +6,7 @@ using System.IO; using System.Diagnostics; using System.Collections.Generic; +using System.Linq; using System.Text; // Managed mirror of NativeFormatWriter.h/.cpp @@ -2041,13 +2042,9 @@ void ComputeLayout() // we can use the ordering to terminate the lookup prematurely. uint mask = ((_nBuckets - 1) << 8) | 0xFF; - // sort it by hashcode - _Entries.Sort( - (a, b) => - { - return (int)(a.Hashcode & mask) - (int)(b.Hashcode & mask); - } - ); + // Sort by hashcode. This sort must be stable since we need determinism even if two entries have + // the same hashcode. This is deterministic if entries are added in a deterministic order. + _Entries = _Entries.OrderBy(entry => entry.Hashcode & mask).ToList(); // Start with maximum size entries _entryIndexSize = 2; diff --git a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/EcmaMethodIL.cs b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/EcmaMethodIL.cs index f15b86762fd72..33e94fec9a541 100644 --- a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/EcmaMethodIL.cs +++ b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/EcmaMethodIL.cs @@ -6,7 +6,7 @@ using System.Collections.Immutable; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; - +using System.Threading; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -64,8 +64,8 @@ public override byte[] GetILBytes() if (_ilBytes != null) return _ilBytes; - byte[] ilBytes = _methodBody.GetILBytes(); - return (_ilBytes = ilBytes); + Interlocked.CompareExchange(ref _ilBytes, _methodBody.GetILBytes(), null); + return _ilBytes; } public override bool IsInitLocals diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs index 933414bb0453a..18ee3e11fb8b3 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs @@ -4,12 +4,12 @@ using System; using System.Collections.Generic; +using System.Linq; 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; @@ -36,9 +36,11 @@ public class ManifestMetadataTableNode : HeaderTableNode private readonly Dictionary _assemblyRefToModuleIdMap; /// - /// Assembly references to store in the manifest metadata. + /// Map from module index to the AssemblyName for the module. This only contains modules + /// that were actually loaded and is populated by ModuleToIndex. /// - private readonly List _manifestAssemblies; + private readonly Dictionary _moduleIdToAssemblyNameMap; + /// /// Registered signature emitters. @@ -69,7 +71,7 @@ public ManifestMetadataTableNode(EcmaModule inputModule, NodeFactory nodeFactory : base(inputModule.Context.Target) { _assemblyRefToModuleIdMap = new Dictionary(); - _manifestAssemblies = new List(); + _moduleIdToAssemblyNameMap = new Dictionary(); _signatureEmitters = new List(); _nodeFactory = nodeFactory; @@ -95,9 +97,22 @@ public void RegisterEmitter(ISignatureEmitter emitter) public int ModuleToIndex(EcmaModule module) { + if (!_nodeFactory.MarkingComplete) + { + // If we call this function before sorting is complete, we might have a determinism bug caused by + // compiling two functions in an arbitrary order and hence getting different module IDs. + throw new InvalidOperationException("Cannot get ModuleToIndex mapping until marking is complete."); + } + AssemblyName assemblyName = module.Assembly.GetName(); + int assemblyRefIndex; + if (!_assemblyRefToModuleIdMap.TryGetValue(assemblyName.Name, out assemblyRefIndex)) + { + assemblyRefIndex = _nextModuleId++; + _assemblyRefToModuleIdMap.Add(assemblyName.Name, assemblyRefIndex); + } - if (!_manifestAssemblies.Contains(assemblyName)) + if (!_moduleIdToAssemblyNameMap.ContainsKey(assemblyRefIndex)) { if (_emissionCompleted) { @@ -108,14 +123,9 @@ public int ModuleToIndex(EcmaModule module) // the verification logic would be broken at runtime. Debug.Assert(_nodeFactory.CompilationModuleGroup.VersionsWithModule(module)); - _manifestAssemblies.Add(assemblyName); - if (!_assemblyRefToModuleIdMap.ContainsKey(assemblyName.Name)) - _assemblyRefToModuleIdMap.Add(assemblyName.Name, _nextModuleId); - - _nextModuleId++; + _moduleIdToAssemblyNameMap.Add(assemblyRefIndex, assemblyName); } - - return _assemblyRefToModuleIdMap[assemblyName.Name]; + return assemblyRefIndex; } public override int ClassCode => 791828335; @@ -166,7 +176,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) fieldList: MetadataTokens.FieldDefinitionHandle(1), methodList: MetadataTokens.MethodDefinitionHandle(1)); - foreach (AssemblyName assemblyName in _manifestAssemblies) + IEnumerable manifestAssemblies = _moduleIdToAssemblyNameMap.OrderBy(x => x.Key).Select(x => x.Value); + foreach (AssemblyName assemblyName in manifestAssemblies) { AssemblyFlags assemblyFlags = 0; byte[] publicKeyOrToken; diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs index c830b56afa0d6..24d1bc305ee46 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs @@ -27,11 +27,15 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ReadyToRunCodegenNodeFactory r2rFactory = (ReadyToRunCodegenNodeFactory)factory; ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(); - dataBuilder.AddSymbol(this); - EcmaModule targetModule = _signatureContext.GetTargetModule(_arrayType); - SignatureContext innerContext = dataBuilder.EmitFixup(r2rFactory, ReadyToRunFixupKind.READYTORUN_FIXUP_NewArray, targetModule, _signatureContext); - dataBuilder.EmitTypeSignature(_arrayType, innerContext); + if (!relocsOnly) + { + dataBuilder.AddSymbol(this); + + EcmaModule targetModule = _signatureContext.GetTargetModule(_arrayType); + SignatureContext innerContext = dataBuilder.EmitFixup(r2rFactory, ReadyToRunFixupKind.READYTORUN_FIXUP_NewArray, targetModule, _signatureContext); + dataBuilder.EmitTypeSignature(_arrayType, innerContext); + } return dataBuilder.ToObjectData(); } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs index ea1e3c542d58e..c3ca2674b26fb 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs @@ -25,10 +25,14 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ReadyToRunCodegenNodeFactory r2rFactory = (ReadyToRunCodegenNodeFactory)factory; ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(); - dataBuilder.AddSymbol(this); - dataBuilder.EmitFixup(r2rFactory, ReadyToRunFixupKind.READYTORUN_FIXUP_StringHandle, _token.Module, _signatureContext); - dataBuilder.EmitUInt(_token.TokenRid); + if (!relocsOnly) + { + dataBuilder.AddSymbol(this); + + dataBuilder.EmitFixup(r2rFactory, ReadyToRunFixupKind.READYTORUN_FIXUP_StringHandle, _token.Module, _signatureContext); + dataBuilder.EmitUInt(_token.TokenRid); + } return dataBuilder.ToObjectData(); } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 059dceaba1b92..575dcb6e24ad6 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -10,7 +10,6 @@ using System.Threading; using Internal.IL; -using Internal.IL.Stubs; using Internal.JitInterface; using Internal.TypeSystem; @@ -26,7 +25,7 @@ public abstract class Compilation : ICompilation protected readonly NodeFactory _nodeFactory; protected readonly Logger _logger; private readonly DevirtualizationManager _devirtualizationManager; - private ILCache _methodILCache; + protected ILCache _methodILCache; private readonly HashSet _modulesBeingInstrumented; @@ -79,10 +78,6 @@ public bool CanInline(MethodDesc caller, MethodDesc callee) public virtual MethodIL GetMethodIL(MethodDesc method) { - // Flush the cache when it grows too big - if (_methodILCache.Count > 1000) - _methodILCache = new ILCache(_methodILCache.ILProvider, NodeFactory.CompilationModuleGroup); - return _methodILCache.GetOrCreateValue(method).MethodIL; } @@ -106,7 +101,7 @@ public bool IsModuleInstrumented(ModuleDesc module) return _modulesBeingInstrumented.Contains(module); } - private sealed class ILCache : LockFreeReaderHashtable + public sealed class ILCache : LockFreeReaderHashtable { public ILProvider ILProvider { get; } private readonly CompilationModuleGroup _compilationModuleGroup; @@ -147,7 +142,7 @@ protected override MethodILData CreateValueFromKey(MethodDesc key) return new MethodILData() { Method = key, MethodIL = methodIL }; } - internal class MethodILData + public class MethodILData { public MethodDesc Method; public MethodIL MethodIL; @@ -302,6 +297,11 @@ protected override void ComputeDependencyNodeDependencies(List 1000) + { + _methodILCache = new ILCache(_methodILCache.ILProvider, NodeFactory.CompilationModuleGroup); + } } public ISymbolNode GetFieldRvaData(FieldDesc field) => NodeFactory.CopiedFieldRva(field);