Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Commit

Permalink
Add support for eager static constructors
Browse files Browse the repository at this point in the history
This looks like a lot of code just to support a rarely used feature,
but a lot of this is plumbing that will let me delete some existing code
in a subsequent commit. Some of this will also be needed to support
the various preinitialized data attributes/general type preinitialization.

* Redirecting uses of HasStaticConstructor to go through the TypeInitialization class (exposing higher level APIs "HasLazyCctor" and "HasEagerCctor")
* Emitting a new table with an array of eager class constructors
* Augmenting GC/nonGC/ThreadStatic and Method nodes to depend on an entry in the eager cctor table if the owning type has a cctor
* Startup code addition to iterate over the list of eager cctors and call them one by one

Reusable code:
* ArrayOfEmbeddedPointers: we have a buch of places where we just want an array of pointers. This will let us e.g. delete StringIndirectionNode.

Missing features: ordering the eager cctors. I want to think a bit about
how we parse custom attribute blobs before I implement that.

Fixes #471.
  • Loading branch information
MichalStrehovsky committed Feb 19, 2016
1 parent 3a209ba commit f97d248
Show file tree
Hide file tree
Showing 19 changed files with 392 additions and 26 deletions.
10 changes: 9 additions & 1 deletion src/ILCompiler.Compiler/src/Compiler/Compilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public partial class Compilation
{
private readonly CompilerTypeSystemContext _typeSystemContext;
private readonly CompilationOptions _options;
private readonly TypeInitialization _typeInitManager;

private NodeFactory _nodeFactory;
private DependencyAnalyzerBase<NodeFactory> _dependencyGraph;
Expand All @@ -61,6 +62,8 @@ public Compilation(CompilationOptions options)
_typeSystemContext.SetSystemModule(_typeSystemContext.GetModuleForSimpleName(options.SystemModuleName));

_nameMangler = new NameMangler(this);

_typeInitManager = new TypeInitialization();
}

public CompilerTypeSystemContext TypeSystemContext
Expand Down Expand Up @@ -132,7 +135,7 @@ public void CompileSingleFile()
{
NodeFactory.NameMangler = NameMangler;

_nodeFactory = new NodeFactory(_typeSystemContext, _options.IsCppCodeGen);
_nodeFactory = new NodeFactory(_typeSystemContext, _typeInitManager, _options.IsCppCodeGen);

// Choose which dependency graph implementation to use based on the amount of logging requested.
if (_options.DgmlLog == null)
Expand Down Expand Up @@ -329,5 +332,10 @@ public object GetFieldRvaData(FieldDesc field)
return _nodeFactory.ReadOnlyDataBlob(NameMangler.GetMangledFieldName(field),
((EcmaField)field).GetFieldRvaData(), _typeSystemContext.Target.PointerSize);
}

public bool HasLazyStaticConstructor(TypeDesc type)
{
return _typeInitManager.HasLazyStaticConstructor(type);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,28 @@

namespace ILCompiler.DependencyAnalysis
{
public class ArrayOfEmbeddedDataNode : ObjectNode
/// <summary>
/// Represents an array of <typeparamref name="TEmbedded"/> nodes. The contents of this node will be emitted
/// by placing a starting symbol, followed by contents of <typeparamref name="TEmbedded"/> nodes (optionally
/// sorted using provided comparer), followed by ending symbol.
/// </summary>
public class ArrayOfEmbeddedDataNode<TEmbedded> : ObjectNode
where TEmbedded : EmbeddedObjectNode
{
private HashSet<EmbeddedObjectNode> _nestedNodes = new HashSet<EmbeddedObjectNode>();
private List<EmbeddedObjectNode> _nestedNodesList = new List<EmbeddedObjectNode>();
private HashSet<TEmbedded> _nestedNodes = new HashSet<TEmbedded>();
private List<TEmbedded> _nestedNodesList = new List<TEmbedded>();
private ObjectAndOffsetSymbolNode _startSymbol;
private ObjectAndOffsetSymbolNode _endSymbol;
private IComparer<EmbeddedObjectNode> _sorter;
private IComparer<TEmbedded> _sorter;

public ArrayOfEmbeddedDataNode(string startSymbolMangledName, string endSymbolMangledName, IComparer<EmbeddedObjectNode> nodeSorter)
public ArrayOfEmbeddedDataNode(string startSymbolMangledName, string endSymbolMangledName, IComparer<TEmbedded> nodeSorter)
{
_startSymbol = new ObjectAndOffsetSymbolNode(this, 0, startSymbolMangledName);
_endSymbol = new ObjectAndOffsetSymbolNode(this, 0, endSymbolMangledName);
_sorter = nodeSorter;
}

public void AddEmbeddedObject(EmbeddedObjectNode symbol)
public void AddEmbeddedObject(TEmbedded symbol)
{
if (_nestedNodes.Add(symbol))
{
Expand Down Expand Up @@ -64,7 +70,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
_nestedNodesList.Sort(_sorter);

builder.DefinedSymbols.Add(_startSymbol);
foreach (EmbeddedObjectNode node in _nestedNodesList)
foreach (TEmbedded node in _nestedNodesList)
{
if (!relocsOnly)
node.Offset = builder.CountBytes;
Expand All @@ -82,4 +88,14 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
return objData;
}
}

// TODO: delete this once we review each use of this and put it on the generic plan with the
// right element type
public class ArrayOfEmbeddedDataNode : ArrayOfEmbeddedDataNode<EmbeddedObjectNode>
{
public ArrayOfEmbeddedDataNode(string startSymbolMangledName, string endSymbolMangledName, IComparer<EmbeddedObjectNode> nodeSorter)
: base(startSymbolMangledName, endSymbolMangledName, nodeSorter)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// 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;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// Represents an array of pointers to symbols. <typeparamref name="TTarget"/> is the type
/// of node each pointer within the vector points to.
/// </summary>
public sealed class ArrayOfEmbeddedPointersNode<TTarget> : ArrayOfEmbeddedDataNode<EmbeddedPointerIndirectionNode<TTarget>>
where TTarget : ISymbolNode
{
private int _nextId;
private string _startSymbolMangledName;

public ArrayOfEmbeddedPointersNode(string startSymbolMangledName, string endSymbolMangledName, IComparer<TTarget> nodeSorter)
: base(
startSymbolMangledName,
endSymbolMangledName,
nodeSorter != null ? new PointerIndirectionNodeComparer(nodeSorter) : null)
{
_startSymbolMangledName = startSymbolMangledName;
}

public EmbeddedObjectNode NewNode(TTarget target)
{
return new SimpleEmbeddedPointerIndirectionNode(this, target);
}

public EmbeddedObjectNode NewNodeWithSymbol(TTarget target)
{
int id = System.Threading.Interlocked.Increment(ref _nextId);
return new EmbeddedPointerIndirectionWithSymbolNode(this, target, id);
}

private class PointerIndirectionNodeComparer : IComparer<EmbeddedPointerIndirectionNode<TTarget>>
{
private IComparer<TTarget> _innerComparer;

public PointerIndirectionNodeComparer(IComparer<TTarget> innerComparer)
{
_innerComparer = innerComparer;
}

public int Compare(EmbeddedPointerIndirectionNode<TTarget> x, EmbeddedPointerIndirectionNode<TTarget> y)
{
return _innerComparer.Compare(x.Target, y.Target);
}
}

private class SimpleEmbeddedPointerIndirectionNode : EmbeddedPointerIndirectionNode<TTarget>
{
protected ArrayOfEmbeddedPointersNode<TTarget> _parentNode;

public SimpleEmbeddedPointerIndirectionNode(ArrayOfEmbeddedPointersNode<TTarget> futureParent, TTarget target)
: base(target)
{
_parentNode = futureParent;
}

public override string GetName()
{
return "Embedded pointer to " + Target.MangledName;
}

protected override void OnMarked(NodeFactory context)
{
// We don't want the child in the parent collection unless it's necessary.
// Only when this node gets marked, the parent node becomes the actual parent.
_parentNode.AddEmbeddedObject(this);
}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context)
{
return new[]
{
new DependencyListEntry(Target, "reloc"),
new DependencyListEntry(_parentNode, "Pointer region")
};
}
}

private class EmbeddedPointerIndirectionWithSymbolNode : SimpleEmbeddedPointerIndirectionNode, ISymbolNode
{
private int _id;

public EmbeddedPointerIndirectionWithSymbolNode(ArrayOfEmbeddedPointersNode<TTarget> futureParent, TTarget target, int id)
: base(futureParent, target)
{
_id = id;
}

public string MangledName
{
get
{
return String.Concat(_parentNode._startSymbolMangledName, "_", _id.ToStringInvariant());
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace ILCompiler.DependencyAnalysis
{
internal class CppMethodCodeNode : DependencyNodeCore<NodeFactory>, ISymbolNode
internal class CppMethodCodeNode : DependencyNodeCore<NodeFactory>, IMethodNode
{
private MethodDesc _method;
private string _methodCode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,13 +450,13 @@ private void OutputNullableTypeParameter(NodeFactory factory, ref ObjectDataBuil
/// </summary>
private void ComputeOptionalEETypeFields(NodeFactory factory)
{
ComputeRareFlags();
ComputeRareFlags(factory);
ComputeNullableValueOffset();
ComputeICastableVirtualMethodSlots(factory);
ComputeValueTypeFieldPadding();
}

void ComputeRareFlags()
void ComputeRareFlags(NodeFactory factory)
{
uint flags = 0;

Expand All @@ -465,7 +465,7 @@ void ComputeRareFlags()
flags |= (uint)EETypeRareFlags.IsNullableFlag;
}

if (_type.HasStaticConstructor)
if (factory.TypeInitializationManager.HasLazyStaticConstructor(_type))
{
flags |= (uint)EETypeRareFlags.HasCctorFlag;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// An <see cref="EmbeddedObjectNode"/> whose sole value is a pointer to a different <see cref="ISymbolNode"/>.
/// <typeparamref name="TTarget"/> represents the node type this pointer points to.
/// </summary>
public abstract class EmbeddedPointerIndirectionNode<TTarget> : EmbeddedObjectNode
where TTarget : ISymbolNode
{
private TTarget _targetNode;

/// <summary>
/// Target symbol this node points to.
/// </summary>
public TTarget Target
{
get
{
return _targetNode;
}
}

internal EmbeddedPointerIndirectionNode(TTarget target)
{
_targetNode = target;
}

public override bool StaticDependenciesAreComputed
{
get
{
return true;
}
}

public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly)
{
dataBuilder.RequirePointerAlignment();
dataBuilder.EmitPointerReloc(Target);
}

// At minimum, Target needs to be reported as a static dependency by inheritors.
public abstract override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,18 @@ public ISymbolNode GetGCStaticEETypeNode(NodeFactory context)

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context)
{
return new DependencyListEntry[] { new DependencyListEntry(context.GCStaticsRegion, "GCStatics Region"),
new DependencyListEntry(GetGCStaticEETypeNode(context), "GCStatic EEType")};
DependencyListEntry[] result;
if (context.TypeInitializationManager.HasEagerStaticConstructor(_type))
{
result = new DependencyListEntry[3];
result[2] = new DependencyListEntry(context.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor");
}
else
result = new DependencyListEntry[2];

result[0] = new DependencyListEntry(context.GCStaticsRegion, "GCStatics Region");
result[1] = new DependencyListEntry(GetGCStaticEETypeNode(context), "GCStatic EEType");
return result;
}

int ISymbolNode.Offset
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// A dependency analysis node that represents a method.
/// </summary>
public interface IMethodNode : ISymbolNode
{
MethodDesc Method { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace ILCompiler.DependencyAnalysis
{
internal class MethodCodeNode : ObjectNode, INodeWithFrameInfo, INodeWithDebugInfo, ISymbolNode
internal class MethodCodeNode : ObjectNode, IMethodNode, INodeWithFrameInfo, INodeWithDebugInfo
{
private MethodDesc _method;
private ObjectData _methodCode;
Expand Down Expand Up @@ -72,6 +72,19 @@ int ISymbolNode.Offset
}
}

protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory context)
{
TypeDesc owningType = _method.OwningType;
if (context.TypeInitializationManager.HasEagerStaticConstructor(owningType))
{
var result = new DependencyList();
result.Add(context.EagerCctorIndirection(owningType.GetStaticConstructor()), "Eager .cctor");
return result;
}

return null;
}

public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
{
return _methodCode;
Expand Down
Loading

0 comments on commit f97d248

Please sign in to comment.