From 69d12d925616ec536eb032bd2374495b201362a2 Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Mon, 4 Jan 2016 15:16:16 -0800 Subject: [PATCH] Interface dispatch support Emit the supporting data structures for interface dispatch: - Interface dispatch cell for each call site providing the resolution function entry point as well as the interface type and interface method slot number - Interface map on EETypes specifying the list of interfaces they implement - Dispatch map that provides, for each type, the lookup rows for interface type, interface method slot, and implementing method's VTable slot - Dispatch map table that provides dispatch map pointers (the index into this table is stored on an EEType instead of a pointer to the map for space savings) Added a work-around in the runtime to provide access to the dispatch map table until full module headers are implemented --- ...pls.cs => InstantiatedType.MethodImpls.cs} | 52 ++----- .../Common/MetadataType.MethodImpls.cs | 48 ++++++ .../Compiler/DependencyAnalysis/EETypeNode.cs | 60 +++++++- .../EETypeOptionalFieldsNode.cs | 11 +- .../InterfaceDispatchCellNode.cs | 87 +++++++++++ .../InterfaceDispatchMapNode.cs | 137 ++++++++++++++++++ .../InterfaceDispatchMapTableNode.cs | 74 ++++++++++ .../DependencyAnalysis/NodeFactory.cs | 27 ++++ .../DependencyAnalysis/ObjectDataBuilder.cs | 8 +- .../ReadyToRunHelperNode.cs | 9 ++ .../Target_X64/X64ReadyToRunHelperNode.cs | 8 + .../src/ILCompiler.Compiler.csproj | 3 + .../src/ILCompiler.TypeSystem.csproj | 3 +- src/ILCompiler/repro/Program.cs | 1 - src/JitInterface/src/CorInfoImpl.cs | 35 +++-- src/Native/Bootstrap/main.cpp | 17 +-- src/Native/Runtime/CMakeLists.txt | 5 +- .../Runtime/CachedInterfaceDispatch.cpp | 27 ++-- src/Native/Runtime/inc/eetype.h | 2 + src/Native/Runtime/inc/eetype.inl | 9 ++ src/Native/Runtime/portable.cpp | 43 ++++++ src/Native/Runtime/unix/PalRedhawkInline.h | 4 +- .../src/System/Activator.cs | 9 ++ 23 files changed, 579 insertions(+), 100 deletions(-) rename src/Common/src/TypeSystem/Common/{TypeDesc.MethodImpls.cs => InstantiatedType.MethodImpls.cs} (52%) create mode 100644 src/Common/src/TypeSystem/Common/MetadataType.MethodImpls.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapTableNode.cs diff --git a/src/Common/src/TypeSystem/Common/TypeDesc.MethodImpls.cs b/src/Common/src/TypeSystem/Common/InstantiatedType.MethodImpls.cs similarity index 52% rename from src/Common/src/TypeSystem/Common/TypeDesc.MethodImpls.cs rename to src/Common/src/TypeSystem/Common/InstantiatedType.MethodImpls.cs index 6fbe1726336..1b6edfb684d 100644 --- a/src/Common/src/TypeSystem/Common/TypeDesc.MethodImpls.cs +++ b/src/Common/src/TypeSystem/Common/InstantiatedType.MethodImpls.cs @@ -5,47 +5,6 @@ namespace Internal.TypeSystem { - public struct MethodImplRecord - { - public MethodDesc Decl; - public MethodDesc Body; - } - - // MethodImpl api surface for types. - public partial class MetadataType - { - /// - /// Compute an array of all MethodImpls that pertain to overriding virtual (non-interface methods) on this type. - /// May be expensive. - /// - protected abstract MethodImplRecord[] ComputeVirtualMethodImplsForType(); - - private MethodImplRecord[] _allVirtualMethodImplsForType; - /// - /// Get an array of all MethodImpls that pertain to overriding virtual (non-interface methods) on this type. - /// Expected to cache results so this api can be used repeatedly. - /// - public MethodImplRecord[] VirtualMethodImplsForType - { - get - { - if (_allVirtualMethodImplsForType == null) - { - _allVirtualMethodImplsForType = ComputeVirtualMethodImplsForType(); - } - - return _allVirtualMethodImplsForType; - } - } - - /// - /// Get an array of MethodImpls where the Decl method matches by name with the specified name. - /// - /// - /// - public abstract MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name); - } - // Implementation of MethodImpl api surface implemented without metadata access. public partial class InstantiatedType { @@ -63,7 +22,16 @@ private MethodImplRecord[] InstantiateMethodImpls(MethodImplRecord[] uninstMetho for (int i = 0; i < uninstMethodImpls.Length; i++) { - instMethodImpls[i].Decl = _typeDef.Context.GetMethodForInstantiatedType(uninstMethodImpls[i].Decl, this); + var implTypeInstantiated = uninstMethodImpls[i].Decl.OwningType.InstantiateSignature(this.Instantiation, new Instantiation()); + if (implTypeInstantiated is InstantiatedType) + { + instMethodImpls[i].Decl = _typeDef.Context.GetMethodForInstantiatedType(uninstMethodImpls[i].Decl.GetTypicalMethodDefinition(), (InstantiatedType)implTypeInstantiated); + } + else + { + instMethodImpls[i].Decl = uninstMethodImpls[i].Decl; + } + instMethodImpls[i].Body = _typeDef.Context.GetMethodForInstantiatedType(uninstMethodImpls[i].Body, this); } diff --git a/src/Common/src/TypeSystem/Common/MetadataType.MethodImpls.cs b/src/Common/src/TypeSystem/Common/MetadataType.MethodImpls.cs new file mode 100644 index 00000000000..a1ec05c839f --- /dev/null +++ b/src/Common/src/TypeSystem/Common/MetadataType.MethodImpls.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Internal.TypeSystem +{ + public struct MethodImplRecord + { + public MethodDesc Decl; + public MethodDesc Body; + } + + // MethodImpl api surface for types. + public partial class MetadataType + { + /// + /// Compute an array of all MethodImpls that pertain to overriding virtual (non-interface methods) on this type. + /// May be expensive. + /// + protected abstract MethodImplRecord[] ComputeVirtualMethodImplsForType(); + + private MethodImplRecord[] _allVirtualMethodImplsForType; + /// + /// Get an array of all MethodImpls that pertain to overriding virtual (non-interface methods) on this type. + /// Expected to cache results so this api can be used repeatedly. + /// + public MethodImplRecord[] VirtualMethodImplsForType + { + get + { + if (_allVirtualMethodImplsForType == null) + { + _allVirtualMethodImplsForType = ComputeVirtualMethodImplsForType(); + } + + return _allVirtualMethodImplsForType; + } + } + + /// + /// Get an array of MethodImpls where the Decl method matches by name with the specified name. + /// + /// + /// + public abstract MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name); + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs index 767f00aaaa5..4ad4beea3c0 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs @@ -108,6 +108,11 @@ public override bool StaticDependenciesAreComputed } } + public void SetDispatchMapIndex(uint index) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.DispatchMap, index); + } + int ISymbolNode.Offset { get @@ -137,8 +142,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) objData.EmitZeroPointer(); return objData.ToObjectData(); } - + ComputeOptionalEETypeFields(factory); + if (null == _optionalFieldsNode) { _optionalFieldsNode = factory.EETypeOptionalFields(_optionalFieldsBuilder); @@ -155,6 +161,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) if (_constructed) { OutputVirtualSlots(factory, ref objData, _type, _type); + OutputInterfaceMap(factory, ref objData); OutputFinalizerMethod(factory, ref objData); OutputOptionalFields(factory, ref objData); OutputNullableTypeParameter(factory, ref objData); @@ -163,6 +170,18 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) return objData.ToObjectData(); } + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencyList = new DependencyNodeCore.DependencyList(); + if (_type is MetadataType && _constructed) + { + dependencyList.Add(factory.InterfaceDispatchMap(_type), "Interface dispatch map"); + } + + dependencyList.Add(factory.EETypeOptionalFields(_optionalFieldsBuilder), "EEType optional fields"); + return dependencyList; + } + public override bool HasConditionalStaticDependencies { get @@ -179,6 +198,11 @@ public override bool HasConditionalStaticDependencies return true; } + // If the type implements at least one interface, calls against that interface could result in this type's + // implementation being used. + if (_type.RuntimeInterfaces.Length > 0) + return true; + return false; } } @@ -195,6 +219,24 @@ public override IEnumerable GetConditionalStaticDep yield return new DependencyNodeCore.CombinedDependencyListEntry(factory.MethodEntrypoint(impl), factory.VirtualMethodUse(decl), "Virtual method"); } } + + // Add conditional dependencies for interface methods the type implements. For example, if the type T implements + // interface IFoo which has a method M1, add a dependency on T.M1 dependent on IFoo.M1 being called, since it's + // possible for any IFoo object to actually be an instance of T. + foreach (DefType interfaceType in _type.RuntimeInterfaces) + { + Debug.Assert(interfaceType.IsInterface); + + foreach (MethodDesc interfaceMethod in interfaceType.GetMethods()) + { + Debug.Assert(interfaceMethod.IsVirtual); + MethodDesc implMethod = VirtualFunctionResolution.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod, _type.GetClosestMetadataType()); + if (implMethod != null) + { + yield return new DependencyNodeCore.CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.ReadyToRunHelper(ReadyToRunHelperId.InterfaceDispatch, interfaceMethod), "Interface method"); + } + } + } } } @@ -335,9 +377,7 @@ private void OutputVirtualSlotAndInterfaceCount(NodeFactory factory, ref ObjectD } objData.EmitShort(checked((short)virtualSlotCount)); - - // Todo: Number of slots of EEInterfaceInfo when we add interface support - objData.EmitShort(0); + objData.EmitShort(checked((short)_type.RuntimeInterfaces.Length)); } private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType) @@ -364,6 +404,14 @@ private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objDa } } + private void OutputInterfaceMap(NodeFactory factory, ref ObjectDataBuilder objData) + { + foreach (var itf in _type.RuntimeInterfaces) + { + objData.EmitPointerReloc(factory.NecessaryTypeSymbol(itf)); + } + } + private void OutputFinalizerMethod(NodeFactory factory, ref ObjectDataBuilder objData) { MethodDesc finalizerMethod = _type.GetFinalizer(); @@ -391,12 +439,10 @@ private void OutputNullableTypeParameter(NodeFactory factory, ref ObjectDataBuil } /// - /// Populate the OptionalFieldsRuntimeBuilder if any optional fields are required. Returns true iff - /// at least one optional field was set. + /// Populate the OptionalFieldsRuntimeBuilder if any optional fields are required. /// private void ComputeOptionalEETypeFields(NodeFactory factory) { - // Todo: DispatchMap table index when we support interface dispatch maps ComputeRareFlags(); ComputeNullableValueOffset(); ComputeICastableVirtualMethodSlots(factory); diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs index 357de99c476..a2bab8b82e7 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs @@ -56,12 +56,21 @@ public override string GetName() return ((ISymbolNode)this).MangledName; } + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + return !_fieldBuilder.IsAtLeastOneFieldUsed(); + } + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder objData = new ObjectDataBuilder(factory); objData.RequirePointerAlignment(); objData.DefinedSymbols.Add(this); - objData.EmitBytes(_fieldBuilder.GetBytes()); + + if (!relocsOnly) + { + objData.EmitBytes(_fieldBuilder.GetBytes()); + } return objData.ToObjectData(); } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs new file mode 100644 index 00000000000..cbf3ad015df --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Internal.TypeSystem; +using System; +using System.Diagnostics; + +namespace ILCompiler.DependencyAnalysis +{ + class InterfaceDispatchCellNode : ObjectNode, ISymbolNode + { + MethodDesc _targetMethod; + + public InterfaceDispatchCellNode(MethodDesc targetMethod) + { + Debug.Assert(targetMethod.OwningType.IsInterface); + _targetMethod = targetMethod; + } + + public string MangledName + { + get + { + return "__InterfaceDispatchCell_" + NodeFactory.NameMangler.GetMangledMethodName(_targetMethod); + } + } + + public override string GetName() + { + return ((ISymbolNode)this).MangledName; + } + + public int Offset + { + get + { + return 0; + } + } + + public override string Section + { + get + { + return "data"; + } + } + + public override bool StaticDependenciesAreComputed + { + get + { + return true; + } + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory); + // The interface dispatch cell has an alignment requirement of 2 * [Pointer size] as part of the + // synchronization mechanism of the two values in the runtime. + objData.Alignment = _targetMethod.Context.Target.PointerSize * 2; + objData.DefinedSymbols.Add(this); + + objData.EmitPointerReloc(factory.ExternSymbol("RhpInitialDynamicInterfaceDispatch")); + + // The second cell field uses the two lower-order bits to communicate the contents. + // We add 1 to signal IDC_CachePointerIsInterfacePointer. See src\Native\Runtime\inc\rhbinder.h. + objData.EmitPointerReloc(factory.NecessaryTypeSymbol(_targetMethod.OwningType), 1); + + // End the run of dispatch cells + objData.EmitZeroPointer(); + + int interfaceMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod); + if (factory.Target.PointerSize == 8) + { + objData.EmitLong(interfaceMethodSlot); + } + else + { + throw new NotImplementedException(); + } + + return objData.ToObjectData(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs new file mode 100644 index 00000000000..09d5021aaae --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Internal.TypeSystem; +using System.Collections.Generic; +using System.Diagnostics; + +namespace ILCompiler.DependencyAnalysis +{ + internal struct DispatchMapEntry + { + public short InterfaceIndex; + public short InterfaceMethodSlot; + public short ImplementationMethodSlot; + } + + internal class InterfaceDispatchMapNode : ObjectNode, ISymbolNode + { + uint _dispatchMapTableIndex; + TypeDesc _type; + + public InterfaceDispatchMapNode(TypeDesc type) + { + _type = type; + _dispatchMapTableIndex = uint.MaxValue; + } + + public override string GetName() + { + return ((ISymbolNode)this).MangledName; + } + + string ISymbolNode.MangledName + { + get + { + if (_dispatchMapTableIndex == uint.MaxValue) + return "__InterfaceDispatchMap_"; + else + return "__InterfaceDispatchMap_" + _dispatchMapTableIndex; + } + } + + int ISymbolNode.Offset + { + get + { + return 0; + } + } + + public override bool StaticDependenciesAreComputed + { + get + { + return true; + } + } + + public override string Section + { + get + { + return "data"; + } + } + + internal void SetDispatchMapTableIndex(uint index) + { + Debug.Assert(_dispatchMapTableIndex == uint.MaxValue); + _dispatchMapTableIndex = index; + } + + protected override void OnMarked(NodeFactory context) + { + _dispatchMapTableIndex = context.DispatchMapTable.AddDispatchMap(this); + ((EETypeNode)context.ConstructedTypeSymbol(_type)).SetDispatchMapIndex(_dispatchMapTableIndex); + } + + DispatchMapEntry[] BuildDispatchMap(NodeFactory factory) + { + ArrayBuilder dispatchMapEntries = new ArrayBuilder(); + + for (int i = 0; i < _type.RuntimeInterfaces.Length; i++) + { + var interfaceType = _type.RuntimeInterfaces[i]; + Debug.Assert(interfaceType.IsInterface); + + List virtualSlots; + factory.VirtualSlots.TryGetValue(interfaceType, out virtualSlots); + + if (virtualSlots != null) + { + for (int j = 0; j < virtualSlots.Count; j++) + { + MethodDesc declMethod = virtualSlots[j]; + var implMethod = VirtualFunctionResolution.ResolveInterfaceMethodToVirtualMethodOnType(declMethod, _type.GetClosestMetadataType()); + + // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface + // dispatch will walk the inheritance chain). + if (implMethod != null) + { + var entry = new DispatchMapEntry(); + entry.InterfaceIndex = checked((short)i); + entry.InterfaceMethodSlot = checked((short)j); + entry.ImplementationMethodSlot = checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod)); + dispatchMapEntries.Add(entry); + } + } + } + } + + return dispatchMapEntries.ToArray(); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory); + objData.Alignment = 16; + objData.DefinedSymbols.Add(this); + + if (!relocsOnly) + { + var entries = BuildDispatchMap(factory); + objData.EmitInt(entries.Length); + foreach (var entry in entries) + { + objData.EmitShort(entry.InterfaceIndex); + objData.EmitShort(entry.InterfaceMethodSlot); + objData.EmitShort(entry.ImplementationMethodSlot); + } + } + + return objData.ToObjectData(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapTableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapTableNode.cs new file mode 100644 index 00000000000..11e0f80561a --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapTableNode.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; + +namespace ILCompiler.DependencyAnalysis +{ + public class InterfaceDispatchMapTableNode : ObjectNode, ISymbolNode + { + List _dispatchMaps = new List(); + + public InterfaceDispatchMapTableNode() + { + } + + public string MangledName + { + get + { + return "__InterfaceDispatchMapTable"; + } + } + + public override string GetName() + { + return ((ISymbolNode)this).MangledName; + } + + public int Offset + { + get + { + return 0; + } + } + + public override string Section + { + get + { + return "rdata"; + } + } + + public override bool StaticDependenciesAreComputed + { + get + { + return true; + } + } + + internal uint AddDispatchMap(InterfaceDispatchMapNode node) + { + _dispatchMaps.Add(node); + + return (uint)_dispatchMaps.Count - 1; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory); + objData.Alignment = factory.Target.PointerSize; + objData.DefinedSymbols.Add(this); + + foreach (var map in _dispatchMaps) + { + objData.EmitPointerReloc(map); + } + + return objData.ToObjectData(); + } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 8822b099cb0..e450dcc939c 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -148,6 +148,16 @@ private void CreateNodeCaches() { return new EETypeOptionalFieldsNode(fieldBuilder); }); + + _interfaceDispatchCells = new NodeCache((MethodDesc method) => + { + return new InterfaceDispatchCellNode(method); + }); + + _interfaceDispatchMaps = new NodeCache((TypeDesc type) => + { + return new InterfaceDispatchMapNode(type); + }); } private NodeCache _typeSymbols; @@ -190,6 +200,13 @@ public ThreadStaticsNode TypeThreadStaticsSymbol(MetadataType type) return _threadStatics.GetOrAdd(type); } + private NodeCache _interfaceDispatchCells; + + internal InterfaceDispatchCellNode InterfaceDispatchCell(MethodDesc method) + { + return _interfaceDispatchCells.GetOrAdd(method); + } + private class BoolArrayEqualityComparer : IEqualityComparer { bool IEqualityComparer.Equals(bool[] x, bool[] y) @@ -241,6 +258,13 @@ internal EETypeOptionalFieldsNode EETypeOptionalFields(EETypeOptionalFieldsBuild return _typeOptionalFields.GetOrAdd(fieldBuilder); } + private NodeCache _interfaceDispatchMaps; + + internal InterfaceDispatchMapNode InterfaceDispatchMap(TypeDesc type) + { + return _interfaceDispatchMaps.GetOrAdd(type); + } + private NodeCache _externSymbols; public ISymbolNode ExternSymbol(string name) @@ -373,6 +397,8 @@ public string GetSymbolAlternateName(ISymbolNode node) NameMangler.CompilationUnitPrefix + "__str_fixup_end", null); + public InterfaceDispatchMapTableNode DispatchMapTable = new InterfaceDispatchMapTableNode(); + public Dictionary> VirtualSlots = new Dictionary>(); public Dictionary NodeAliases = new Dictionary(); @@ -384,6 +410,7 @@ public void AttachToDependencyGraph(DependencyAnalysisFramework.DependencyAnalyz graph.AddRoot(GCStaticsRegion, "GC StaticsRegion is always generated"); graph.AddRoot(ThreadStaticsRegion, "ThreadStaticsRegion is always generated"); graph.AddRoot(StringTable, "StringTable is always generated"); + graph.AddRoot(DispatchMapTable, "DispatchMapTable is always generated"); } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index db925dd5837..4ddfcf2fd18 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -101,9 +101,9 @@ public void AddRelocAtOffset(ISymbolNode symbol, RelocType relocType, int offset _relocs.Add(symbolReloc); } - public void EmitReloc(ISymbolNode symbol, RelocType relocType) + public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0) { - AddRelocAtOffset(symbol, relocType, _data.Count); + AddRelocAtOffset(symbol, relocType, _data.Count, delta); // And add space for the reloc switch (relocType) @@ -119,11 +119,11 @@ public void EmitReloc(ISymbolNode symbol, RelocType relocType) } } - public void EmitPointerReloc(ISymbolNode symbol) + public void EmitPointerReloc(ISymbolNode symbol, int delta = 0) { if (_target.PointerSize == 8) { - EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64); + EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64, delta); } else { diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs index 28af227e3da..39cf7269cbf 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs @@ -23,6 +23,7 @@ public enum ReadyToRunHelperId GetGCStaticBase, GetThreadStaticBase, DelegateCtor, + InterfaceDispatch, } public partial class ReadyToRunHelperNode : AssemblyStubNode @@ -81,6 +82,8 @@ public override string MangledName return "__GetThreadStaticBase_" + NodeFactory.NameMangler.GetMangledTypeName((TypeDesc)_target); case ReadyToRunHelperId.DelegateCtor: return "__DelegateCtor_" + NodeFactory.NameMangler.GetMangledMethodName(((DelegateInfo)_target).Target); + case ReadyToRunHelperId.InterfaceDispatch: + return "__InterfaceDispatch_" + NodeFactory.NameMangler.GetMangledMethodName((MethodDesc)_target); default: throw new NotImplementedException(); } @@ -95,6 +98,12 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact dependencyList.Add(context.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Virtual Method Call"); return dependencyList; } + else if (_id == ReadyToRunHelperId.InterfaceDispatch) + { + DependencyList dependencyList = new DependencyList(); + dependencyList.Add(context.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Interface Method Call"); + return dependencyList; + } else { return null; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index 7149a909759..0aa9678d217 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -132,6 +132,14 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo } break; + case ReadyToRunHelperId.InterfaceDispatch: + { + encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); + AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); + encoder.EmitJmpToAddrMode(ref jmpAddrMode); + } + break; + default: throw new NotImplementedException(); } diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 90c2c6e0e5d..02b717fae59 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -38,6 +38,8 @@ + + @@ -67,6 +69,7 @@ + diff --git a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj index 887989b3d98..6df1eeab237 100644 --- a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj +++ b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj @@ -44,8 +44,10 @@ + + @@ -64,7 +66,6 @@ - diff --git a/src/ILCompiler/repro/Program.cs b/src/ILCompiler/repro/Program.cs index 328ce8871c5..f01202ea1cb 100644 --- a/src/ILCompiler/repro/Program.cs +++ b/src/ILCompiler/repro/Program.cs @@ -10,4 +10,3 @@ private static void Main(string[] args) Console.WriteLine("Hello world"); } } - diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs index 3c2efd7f537..2a3895fdb0c 100644 --- a/src/JitInterface/src/CorInfoImpl.cs +++ b/src/JitInterface/src/CorInfoImpl.cs @@ -1826,11 +1826,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO directCall = true; } } - - // TODO: Interface methods - if (targetMethod.IsVirtual && targetMethod.OwningType.IsInterface) - throw new NotImplementedException("Interface method"); - + pResult.hMethod = ObjectToHandle(targetMethod); pResult.methodFlags = getMethodAttribsInternal(targetMethod); @@ -1867,8 +1863,21 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO pResult._exactContextNeedsRuntimeLookup = 0; pResult.codePointerOrStubLookup.lookupKind.needsRuntimeLookup = false; + + if (directCall) + { + if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) + { + // Calling a string constructor doesn't call the actual constructor. + targetMethod = targetMethod.GetStringInitializer(); + } - if (!directCall) + pResult.kind = CORINFO_CALL_KIND.CORINFO_CALL; + pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; + pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(targetMethod)); + pResult.nullInstanceCheck = resolvedCallVirt; + } + else if (!targetMethod.OwningType.IsInterface) { // CORINFO_CALL_CODE_POINTER tells the JIT that this is indirect // call that should not be inlined. @@ -1885,17 +1894,13 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO } else { - if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) - { - // Calling a string constructor doesn't call the actual constructor. - targetMethod = targetMethod.GetStringInitializer(); - } - - pResult.kind = CORINFO_CALL_KIND.CORINFO_CALL; + pResult.nullInstanceCheck = true; + pResult.kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER; + pResult.codePointerOrStubLookup.lookupKind.needsRuntimeLookup = false; pResult.codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; - pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(_compilation.NodeFactory.MethodEntrypoint(targetMethod)); - pResult.nullInstanceCheck = resolvedCallVirt; + pResult.codePointerOrStubLookup.constLookup.addr = + (void*)ObjectToHandle(_compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.InterfaceDispatch, targetMethod)); } // TODO: Generics diff --git a/src/Native/Bootstrap/main.cpp b/src/Native/Bootstrap/main.cpp index a3aeb341aa9..67b53c6af1e 100644 --- a/src/Native/Bootstrap/main.cpp +++ b/src/Native/Bootstrap/main.cpp @@ -232,18 +232,6 @@ extern "C" void RhGetCurrentThreadStackTrace() throw "RhGetCurrentThreadStackTrace"; } -extern "C" void RhpGetDispatchCellInfo() -{ - throw "RhpGetDispatchCellInfo"; -} -extern "C" void RhpUpdateDispatchCellCache() -{ - throw "RhpUpdateDispatchCellCache"; -} -extern "C" void RhpSearchDispatchCellCache() -{ - throw "RhpSearchDispatchCellCache"; -} extern "C" void RhCollect() { throw "RhCollect"; @@ -285,6 +273,9 @@ extern "C" void RhReRegisterForFinalize() throw "RhReRegisterForFinalize"; } +extern "C" void * g_pDispatchMapTemporaryWorkaround; +void * g_pDispatchMapTemporaryWorkaround; + #ifndef CPPCODEGEN SimpleModuleHeader __module = { NULL, NULL /* &__gcStatics, &__gcStaticsDescs */ }; @@ -388,6 +379,7 @@ int __strings_fixup() return 0; } +extern "C" void* __InterfaceDispatchMapTable; extern "C" void* __GCStaticRegionStart; extern "C" void* __GCStaticRegionEnd; int __statics_fixup() @@ -405,6 +397,7 @@ int __statics_fixup() int main(int argc, char * argv[]) { if (__initialize_runtime() != 0) return -1; __register_module(&__module); + g_pDispatchMapTemporaryWorkaround = (void*)&__InterfaceDispatchMapTable; ReversePInvokeFrame frame; __reverse_pinvoke(&frame); if (__strings_fixup() != 0) return -1; diff --git a/src/Native/Runtime/CMakeLists.txt b/src/Native/Runtime/CMakeLists.txt index afafa46d8ee..af604f64ca8 100644 --- a/src/Native/Runtime/CMakeLists.txt +++ b/src/Native/Runtime/CMakeLists.txt @@ -1,6 +1,7 @@ set(COMMON_RUNTIME_SOURCES allocheap.cpp assert.cpp + CachedInterfaceDispatch.cpp Crst.cpp DebugEventSource.cpp dllmain.cpp @@ -77,6 +78,8 @@ if(WIN32) list(APPEND RUNTIME_SOURCES_ARCH_ASM ${ARCH_SOURCES_DIR}/GC.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/StubDispatch.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/ManagedCalloutThunk.${ASM_SUFFIX} ) else() @@ -120,7 +123,7 @@ add_definitions(-DFEATURE_PROFILING) add_definitions(-DFEATURE_REDHAWK) add_definitions(-DVERIFY_HEAP) add_definitions(-DCORERT) - +add_definitions(-DFEATURE_CACHED_INTERFACE_DISPATCH) add_definitions(-D_LIB) if(WIN32) diff --git a/src/Native/Runtime/CachedInterfaceDispatch.cpp b/src/Native/Runtime/CachedInterfaceDispatch.cpp index 5735336eb1f..4a1582044d4 100644 --- a/src/Native/Runtime/CachedInterfaceDispatch.cpp +++ b/src/Native/Runtime/CachedInterfaceDispatch.cpp @@ -11,31 +11,30 @@ #include "common.h" #ifdef FEATURE_CACHED_INTERFACE_DISPATCH -#include "commontypes.h" -#include "commonmacros.h" +#include "CommonTypes.h" +#include "CommonMacros.h" #include "daccess.h" -#include "debugmacrosext.h" -#include "palredhawkcommon.h" -#include "palredhawk.h" +#include "DebugMacrosExt.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" #include "assert.h" #include "slist.h" #include "holder.h" -#include "crst.h" -#include "redhawkwarnings.h" -#include "targetptrs.h" +#include "Crst.h" +#include "RedhawkWarnings.h" +#include "TargetPtrs.h" #include "eetype.h" -#include "range.h" -#include "memaccessmgr.h" +#include "Range.h" #include "allocheap.h" #include "rhbinder.h" -#include "objectlayout.h" +#include "ObjectLayout.h" #include "gcrhinterface.h" #include "module.h" -#include "rwlock.h" -#include "runtimeinstance.h" +#include "RWLock.h" +#include "RuntimeInstance.h" #include "eetype.inl" -#include "cachedinterfacedispatch.h" +#include "CachedInterfaceDispatch.h" extern "C" UIntTarget __fastcall ManagedCallout2(UIntTarget argument1, UIntTarget argument2, void *pTargetMethod, void *pPreviousManagedFrame); diff --git a/src/Native/Runtime/inc/eetype.h b/src/Native/Runtime/inc/eetype.h index 4d9e6b3caa1..624411b2970 100644 --- a/src/Native/Runtime/inc/eetype.h +++ b/src/Native/Runtime/inc/eetype.h @@ -14,6 +14,8 @@ class MdilModule; class EEType; class OptionalFields; + + //------------------------------------------------------------------------------------------------- // Array of these represents the interfaces implemented by a type diff --git a/src/Native/Runtime/inc/eetype.inl b/src/Native/Runtime/inc/eetype.inl index 9a1c774a07b..4d5c3ae16f0 100644 --- a/src/Native/Runtime/inc/eetype.inl +++ b/src/Native/Runtime/inc/eetype.inl @@ -152,6 +152,10 @@ inline bool EEType::HasDispatchMap() return true; } +#ifdef CORERT +extern "C" void * g_pDispatchMapTemporaryWorkaround; +#endif + inline DispatchMap * EEType::GetDispatchMap() { if (!HasInterfaces()) @@ -172,6 +176,11 @@ inline DispatchMap * EEType::GetDispatchMap() // Determine this EEType's module. RuntimeInstance * pRuntimeInstance = GetRuntimeInstance(); + +#ifdef CORERT + return (DispatchMap*)((void**)g_pDispatchMapTemporaryWorkaround)[idxDispatchMap]; +#endif + Module * pModule = pRuntimeInstance->FindModuleByReadOnlyDataAddress(this); if (pModule == NULL) pModule = pRuntimeInstance->FindModuleByDataAddress(this); diff --git a/src/Native/Runtime/portable.cpp b/src/Native/Runtime/portable.cpp index c80f89fbb52..fa5f47b3156 100644 --- a/src/Native/Runtime/portable.cpp +++ b/src/Native/Runtime/portable.cpp @@ -230,11 +230,54 @@ COOP_PINVOKE_HELPER(MDArray *, RhNewMDArray, (EEType * pArrayEEType, UInt32 rank return pObject; } +#if defined(USE_PORTABLE_HELPERS) || !defined(WIN32) COOP_PINVOKE_HELPER(void, RhpInitialDynamicInterfaceDispatch, ()) { ASSERT_UNCONDITIONALLY("NYI"); } +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch1, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch2, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch4, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch8, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch16, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch32, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch64, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(UIntTarget, ManagedCallout2, (UIntTarget argument1, UIntTarget argument2, void *pTargetMethod, void *pPreviousManagedFrame)) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return 0; +} +#endif + // finalizer.cs COOP_PINVOKE_HELPER(void, ProcessFinalizers, ()) { diff --git a/src/Native/Runtime/unix/PalRedhawkInline.h b/src/Native/Runtime/unix/PalRedhawkInline.h index a09fac87672..820e262d212 100644 --- a/src/Native/Runtime/unix/PalRedhawkInline.h +++ b/src/Native/Runtime/unix/PalRedhawkInline.h @@ -46,10 +46,10 @@ FORCEINLINE Int64 PalInterlockedCompareExchange64(_Inout_ _Interlocked_operand_ } #if defined(_AMD64_) -EXTERN_C UInt8 _InterlockedCompareExchange128(Int64 volatile *, Int64, Int64, Int64 *); FORCEINLINE UInt8 PalInterlockedCompareExchange128(_Inout_ _Interlocked_operand_ Int64 volatile *pDst, Int64 iValueHigh, Int64 iValueLow, Int64 *pComperand) { - return _InterlockedCompareExchange128(pDst, iValueHigh, iValueLow, pComperand); + ASSERT_UNCONDITIONALLY("NYI"); + return 0; } #endif // _AMD64_ diff --git a/src/System.Private.CoreLib/src/System/Activator.cs b/src/System.Private.CoreLib/src/System/Activator.cs index b5c00e0984e..6c7065b8665 100644 --- a/src/System.Private.CoreLib/src/System/Activator.cs +++ b/src/System.Private.CoreLib/src/System/Activator.cs @@ -67,7 +67,16 @@ public static T CreateInstance() } [Intrinsic] +#if CORERT + // CORERT-TODO: Add CreateInstanceIntrinsic intrinsic support. + // https://github.com/dotnet/corert/issues/368 + private static T CreateInstanceIntrinsic() + { + throw new NotSupportedException("CreateInstance"); + } +#else private extern static T CreateInstanceIntrinsic(); +#endif [ThreadStatic] internal static bool s_createInstanceMissingDefaultConstructor;