-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Crossgen2 determinism fixes #164
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<string, int> _assemblyRefToModuleIdMap; | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
private readonly List<AssemblyName> _manifestAssemblies; | ||
private readonly Dictionary<int, AssemblyName> _moduleIdToAssemblyNameMap; | ||
|
||
|
||
/// <summary> | ||
/// Registered signature emitters. | ||
|
@@ -69,7 +71,7 @@ public ManifestMetadataTableNode(EcmaModule inputModule, NodeFactory nodeFactory | |
: base(inputModule.Context.Target) | ||
{ | ||
_assemblyRefToModuleIdMap = new Dictionary<string, int>(); | ||
_manifestAssemblies = new List<AssemblyName>(); | ||
_moduleIdToAssemblyNameMap = new Dictionary<int, AssemblyName>(); | ||
_signatureEmitters = new List<ISignatureEmitter>(); | ||
_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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong: this will cause missing entries in the _manifestAssemblies (the bug that I fixed recently). The right way to fix this is to get rid of _manifestAssemblies, and emit a sorted version of _assemblyRefToModuleIdMap in GetData(), sorted by module ID There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And make _assemblyRefToModuleIdMap a |
||
{ | ||
assemblyRefIndex = _nextModuleId++; | ||
_assemblyRefToModuleIdMap.Add(assemblyName.Name, assemblyRefIndex); | ||
} | ||
|
||
if (!_moduleIdToAssemblyNameMap.ContainsKey(assemblyRefIndex)) | ||
{ | ||
if (_emissionCompleted) | ||
{ | ||
|
@@ -108,9 +123,7 @@ public int ModuleToIndex(EcmaModule module) | |
// the verification logic would be broken at runtime. | ||
Debug.Assert(_nodeFactory.CompilationModuleGroup.VersionsWithModule(module)); | ||
|
||
assemblyRefIndex = _nextModuleId++; | ||
_manifestAssemblies.Add(assemblyName); | ||
_assemblyRefToModuleIdMap.Add(assemblyName.Name, assemblyRefIndex); | ||
_moduleIdToAssemblyNameMap.Add(assemblyRefIndex, assemblyName); | ||
} | ||
return assemblyRefIndex; | ||
} | ||
|
@@ -163,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<AssemblyName> manifestAssemblies = _moduleIdToAssemblyNameMap.OrderBy(x => x.Key).Select(x => x.Value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: It might look nicer to write it without .Select(), like: foreach (var index in _moduleIdToAssemblyNameMap.Keys.OrderBy(...))
{
use _moduleIdToAssemblyNameMap[index]
} |
||
foreach (AssemblyName assemblyName in manifestAssemblies) | ||
{ | ||
AssemblyFlags assemblyFlags = 0; | ||
byte[] publicKeyOrToken; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs to be in a loop to handle race conditions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MSDN has a sample here: https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=netframework-4.8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct as is. No loop needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right. Sorry, my bad. I got confused by the so many other compare-exchanges we have in the runtime code base :). Thanks for catching that Jan!