diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2017-06-12 21:47:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-12 21:47:56 +0300 |
commit | 717b4dbf806252d1847b5fdfed86859af15e2894 (patch) | |
tree | a8f3604d4680302818862e954d8c396918067e78 | |
parent | 566051d731c0191eed1c674fecd5fbbe9823738d (diff) |
Allow an external optimizer to specify VTable layout (#3854)
Adds an extensibility point to the `CompilationBuilder` that allows
specifying how vtables should be built.
We currently have two strategies: eagerly built VTables (where we just
put all the virtual methods that the type specifies in the metadata into
the VTable), and lazily built vtables (where we track virtual method
usage during the compilation and only generate vtable slots for methods
that were actually used).
The advantage of the former is that we know the slot assignment very
early on and can inline it into the codegen. The advantage of the latter
is natural reduction of the amount of code we compile.
I'm adding an option to specify optional optimization data that can be
used instead of the lazily built strategy and making it possible for the
IL scanner to generate this data. This way we can have an option where
we both have the natural reduction in the amount of generated code, and
also the inlining of slot numbers in codegen (that part depends on #3773
to light up.
16 files changed, 134 insertions, 73 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs index 838606a9d..6a8df8d8f 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs @@ -24,6 +24,7 @@ namespace ILCompiler protected OptimizationMode _optimizationMode = OptimizationMode.None; protected bool _generateDebugInfo = false; protected MetadataManager _metadataManager; + protected VTableSliceProvider _vtableSliceProvider = new LazyVTableSliceProvider(); public CompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler nameMangler) { @@ -63,6 +64,12 @@ namespace ILCompiler return this; } + public CompilationBuilder UseVTableSliceProvider(VTableSliceProvider provider) + { + _vtableSliceProvider = provider; + return this; + } + public CompilationBuilder UseDebugInfo(bool generateDebugInfo) { _generateDebugInfo = generateDebugInfo; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs index 3d1378efe..ad73f0a6f 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs @@ -48,7 +48,7 @@ namespace ILCompiler.DependencyAnalysis if (_type.RuntimeInterfaces.Length > 0) dependencyList.Add(factory.InterfaceDispatchMap(_type), "Canonical interface dispatch map"); - dependencyList.Add(factory.VTable(_type), "VTable"); + dependencyList.Add(factory.VTable(closestDefType), "VTable"); if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal)) dependencyList.Add(factory.NativeLayout.TemplateTypeLayout(_type), "Universal generic types always have template layout"); diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index e7889f2aa..1106bea0b 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -66,7 +66,7 @@ namespace ILCompiler.DependencyAnalysis } } - dependencyList.Add(factory.VTable(_type), "VTable"); + dependencyList.Add(factory.VTable(closestDefType), "VTable"); if (closestDefType.HasInstantiation) { diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs index 979868dd2..e88e44806 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs @@ -231,10 +231,10 @@ namespace ILCompiler.DependencyAnalysis private void AddVirtualMethodUseDependencies(DependencyList dependencyList, NodeFactory factory) { - if (_type.RuntimeInterfaces.Length > 0 && !factory.VTable(_type).HasFixedSlots) - { - DefType closestDefType = _type.GetClosestDefType(); + DefType closestDefType = _type.GetClosestDefType(); + if (_type.RuntimeInterfaces.Length > 0 && !factory.VTable(closestDefType).HasFixedSlots) + { foreach (var implementedInterface in _type.RuntimeInterfaces) { // If the type implements ICastable, the methods are implicitly necessary diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs index a920c6732..67c91ccab 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs @@ -16,7 +16,7 @@ namespace ILCompiler.DependencyAnalysis public sealed class ILScanNodeFactory : NodeFactory { public ILScanNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) - : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy()) + : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy(), new LazyVTableSliceProvider()) { } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 1f1b76c3c..c264ad2de 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -23,14 +23,16 @@ namespace ILCompiler.DependencyAnalysis private TargetDetails _target; private CompilerTypeSystemContext _context; private CompilationModuleGroup _compilationModuleGroup; + private VTableSliceProvider _vtableSliceProvider; private bool _markingComplete; public NodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, - MetadataManager metadataManager, NameMangler nameMangler, LazyGenericsPolicy lazyGenericsPolicy) + MetadataManager metadataManager, NameMangler nameMangler, LazyGenericsPolicy lazyGenericsPolicy, VTableSliceProvider vtableSliceProvider) { _target = context.Target; _context = context; _compilationModuleGroup = compilationModuleGroup; + _vtableSliceProvider = vtableSliceProvider; NameMangler = nameMangler; InteropStubManager = new InteropStubManager(compilationModuleGroup, context, new InteropStateManager(compilationModuleGroup.GeneratedAssembly)); CreateNodeCaches(); @@ -407,7 +409,7 @@ namespace ILCompiler.DependencyAnalysis if (CompilationModuleGroup.ShouldProduceFullVTable(type)) return new EagerlyBuiltVTableSliceNode(type); else - return new LazilyBuiltVTableSliceNode(type); + return _vtableSliceProvider.GetSlice(type); }); _methodGenericDictionaries = new NodeCache<MethodDesc, ISymbolNode>(method => diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs index 6e22b73da..243f509a0 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs @@ -12,8 +12,9 @@ namespace ILCompiler.DependencyAnalysis { public sealed class RyuJitNodeFactory : NodeFactory { - public RyuJitNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) - : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy()) + public RyuJitNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, + NameMangler nameMangler, VTableSliceProvider vtableSliceProvider) + : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy(), vtableSliceProvider) { } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs index 5b099551c..2d1e3861f 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs @@ -71,7 +71,7 @@ namespace ILCompiler } public UtcNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, IEnumerable<ModuleDesc> inputModules, string metadataFile, string outputFile, UTCNameMangler nameMangler, bool buildMRT) - : base(context, compilationModuleGroup, PickMetadataManager(context, compilationModuleGroup, inputModules, metadataFile), nameMangler, new AttributeDrivenLazyGenericsPolicy()) + : base(context, compilationModuleGroup, PickMetadataManager(context, compilationModuleGroup, inputModules, metadataFile), nameMangler, new AttributeDrivenLazyGenericsPolicy(), null) { CreateHostedNodeCaches(); CompilationUnitPrefix = nameMangler.CompilationUnitPrefix; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs index f01fbf406..59777bae9 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs @@ -21,6 +21,7 @@ namespace ILCompiler.DependencyAnalysis public VTableSliceNode(TypeDesc type) { + Debug.Assert(!type.IsArray, "Wanted to call GetClosestDefType?"); _type = type; } @@ -29,6 +30,8 @@ namespace ILCompiler.DependencyAnalysis get; } + public TypeDesc Type => _type; + /// <summary> /// Gets a value indicating whether the slots are assigned at the beginning of the compilation. /// </summary> @@ -41,6 +44,16 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; + public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) + { + if (_type.HasBaseType) + { + return new[] { new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable") }; + } + + return null; + } + public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null; public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null; @@ -48,9 +61,9 @@ namespace ILCompiler.DependencyAnalysis public override bool HasDynamicDependencies => false; public override bool HasConditionalStaticDependencies => false; - protected IEnumerable<MethodDesc> GetAllVirtualMethods() + protected static IEnumerable<MethodDesc> GetAllVirtualMethods(TypeDesc type) { - foreach (MethodDesc method in _type.GetAllMethods()) + foreach (MethodDesc method in type.GetAllMethods()) { if (method.IsVirtual) yield return method; @@ -59,23 +72,55 @@ namespace ILCompiler.DependencyAnalysis } /// <summary> + /// Represents a VTable slice with fixed slots whose assignment was determined at the time the slice was allocated. + /// </summary> + internal class PrecomputedVTableSliceNode : VTableSliceNode + { + private readonly IReadOnlyList<MethodDesc> _slots; + + public PrecomputedVTableSliceNode(TypeDesc type, IReadOnlyList<MethodDesc> slots) + : base(type) + { + _slots = slots; + } + + public override IReadOnlyList<MethodDesc> Slots + { + get + { + return _slots; + } + } + + public override bool HasFixedSlots + { + get + { + return true; + } + } + } + + /// <summary> /// Represents a VTable slice for a complete type - a type with all virtual method slots generated, /// irrespective of whether they are used. /// </summary> - internal sealed class EagerlyBuiltVTableSliceNode : VTableSliceNode + internal sealed class EagerlyBuiltVTableSliceNode : PrecomputedVTableSliceNode { - private MethodDesc[] _slots; - public EagerlyBuiltVTableSliceNode(TypeDesc type) - : base(type) + : base(type, ComputeSlots(type)) + { + } + + private static IReadOnlyList<MethodDesc> ComputeSlots(TypeDesc type) { var slots = new ArrayBuilder<MethodDesc>(); bool isObjectType = type.IsObject; - DefType defType = _type.GetClosestDefType(); + DefType defType = type.GetClosestDefType(); - IEnumerable<MethodDesc> allSlots = _type.IsInterface ? - GetAllVirtualMethods() : defType.EnumAllVirtualSlots(); + IEnumerable<MethodDesc> allSlots = type.IsInterface ? + GetAllVirtualMethods(type) : defType.EnumAllVirtualSlots(); foreach (var method in allSlots) { @@ -94,36 +139,7 @@ namespace ILCompiler.DependencyAnalysis slots.Add(method); } - _slots = slots.ToArray(); - } - - public override IReadOnlyList<MethodDesc> Slots - { - get - { - return _slots; - } - } - - public override bool HasFixedSlots - { - get - { - return true; - } - } - - public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) - { - if (_type.HasBaseType) - { - return new DependencyListEntry[] - { - new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable") - }; - } - - return null; + return slots.ToArray(); } } @@ -187,16 +203,6 @@ namespace ILCompiler.DependencyAnalysis _usedMethods.Add(virtualMethod); } - public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) - { - if (_type.HasBaseType) - { - return new[] { new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable") }; - } - - return null; - } - public override bool HasConditionalStaticDependencies { get @@ -212,7 +218,7 @@ namespace ILCompiler.DependencyAnalysis DefType defType = _type.GetClosestDefType(); IEnumerable<MethodDesc> allSlots = _type.IsInterface ? - GetAllVirtualMethods() : defType.EnumAllVirtualSlots(); + GetAllVirtualMethods(_type) : defType.EnumAllVirtualSlots(); foreach (var method in allSlots) { diff --git a/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs b/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs index d93aba60c..67c0a8532 100644 --- a/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs +++ b/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs @@ -74,8 +74,9 @@ namespace ILCompiler } } - public ILScanResults Scan() + ILScanResults IILScanner.Scan() { + _nodeFactory.NameMangler.CompilationUnitPrefix = ""; return new ILScanResults(_dependencyGraph.MarkedNodeList); } } @@ -94,17 +95,31 @@ namespace ILCompiler _markedNodes = markedNodes; } - public IEnumerable<MethodDesc> CompiledMethods + public VTableSliceProvider GetVTableLayoutInfo() { - get + return new ScannedVTableProvider(_markedNodes); + } + + private class ScannedVTableProvider : VTableSliceProvider + { + private Dictionary<TypeDesc, IReadOnlyList<MethodDesc>> _vtableSlices = new Dictionary<TypeDesc, IReadOnlyList<MethodDesc>>(); + + public ScannedVTableProvider(ImmutableArray<DependencyNodeCore<NodeFactory>> markedNodes) { - foreach (var node in _markedNodes) + foreach (var node in markedNodes) { - var methodNode = node as ScannedMethodNode; - if (methodNode != null) - yield return methodNode.Method; + var vtableSliceNode = node as VTableSliceNode; + if (vtableSliceNode != null) + { + _vtableSlices.Add(vtableSliceNode.Type, vtableSliceNode.Slots); + } } } + + internal override VTableSliceNode GetSlice(TypeDesc type) + { + return new PrecomputedVTableSliceNode(type, _vtableSlices[type]); + } } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs index 3de4197dc..27240e366 100644 --- a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs @@ -85,7 +85,7 @@ namespace ILCompiler jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_FEATURE_SIMD); } - var factory = new RyuJitNodeFactory(_context, _compilationGroup, _metadataManager, _nameMangler); + var factory = new RyuJitNodeFactory(_context, _compilationGroup, _metadataManager, _nameMangler, _vtableSliceProvider); var jitConfig = new JitConfigProvider(jitFlagBuilder.ToArray(), _ryujitOptions); DependencyAnalyzerBase<NodeFactory> graph = CreateDependencyGraph(factory); diff --git a/src/ILCompiler.Compiler/src/Compiler/VTableSliceProvider.cs b/src/ILCompiler.Compiler/src/Compiler/VTableSliceProvider.cs new file mode 100644 index 000000000..18455f2b4 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/VTableSliceProvider.cs @@ -0,0 +1,28 @@ +// 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 Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + /// <summary> + /// Provides VTable information for a specific type. + /// </summary> + public abstract class VTableSliceProvider + { + internal abstract VTableSliceNode GetSlice(TypeDesc type); + } + + /// <summary> + /// Provides VTable information that collects data during the compilation to build a VTable for a type. + /// </summary> + public sealed class LazyVTableSliceProvider : VTableSliceProvider + { + internal override VTableSliceNode GetSlice(TypeDesc type) + { + return new LazilyBuiltVTableSliceNode(type); + } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 475866398..8c38efba6 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -304,6 +304,7 @@ <Compile Include="Compiler\SimdHelper.cs" /> <Compile Include="Compiler\VectorOfTFieldLayoutAlgorithm.cs" /> <Compile Include="Compiler\VirtualMethodCallHelper.cs" /> + <Compile Include="Compiler\VTableSliceProvider.cs" /> <Compile Include="Compiler\WindowsNodeMangler.cs" /> <Compile Include="IL\ILImporter.Scanner.cs" /> <Compile Include="IL\Stubs\PInvokeILProvider.cs" /> diff --git a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs index 7ea68e212..3f56e757c 100644 --- a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs +++ b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs @@ -29,7 +29,7 @@ namespace ILCompiler public override ICompilation ToCompilation() { - CppCodegenNodeFactory factory = new CppCodegenNodeFactory(_context, _compilationGroup, _metadataManager, _nameMangler); + CppCodegenNodeFactory factory = new CppCodegenNodeFactory(_context, _compilationGroup, _metadataManager, _nameMangler, _vtableSliceProvider); DependencyAnalyzerBase<NodeFactory> graph = CreateDependencyGraph(factory); return new CppCodegenCompilation(graph, factory, _compilationRoots, _logger, _config); diff --git a/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs b/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs index 3eaf2ae2a..2a0e66acb 100644 --- a/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs +++ b/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs @@ -10,8 +10,9 @@ namespace ILCompiler.DependencyAnalysis { public sealed class CppCodegenNodeFactory : NodeFactory { - public CppCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) - : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy()) + public CppCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, + NameMangler nameMangler, VTableSliceProvider vtableSliceProvider) + : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy(), vtableSliceProvider) { } diff --git a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs index 53e9e892d..5e5069a63 100644 --- a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs +++ b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs @@ -1109,7 +1109,7 @@ namespace ILCompiler.CppCodeGen { OutputTypeFields(typeDefinitions, nodeType); - IReadOnlyList<MethodDesc> virtualSlots = _compilation.NodeFactory.VTable(nodeType).Slots; + IReadOnlyList<MethodDesc> virtualSlots = _compilation.NodeFactory.VTable(nodeType.GetClosestDefType()).Slots; int baseSlots = 0; var baseType = nodeType.BaseType; |