enabled thread static fields in WebAssembly (#4999)

* enabled thread static fields, changed vtable dispatch to not be dependent on EETypeNode, added typed node factory
This commit is contained in:
Jeff Greene 2017-11-30 00:19:32 -08:00 коммит произвёл Morgan Brown
Родитель b68c08e3ce
Коммит d80ed0826b
7 изменённых файлов: 113 добавлений и 35 удалений

Просмотреть файл

@ -847,12 +847,9 @@ namespace Internal.IL
private LLVMValueRef GetOrCreateMethodSlot(MethodDesc method)
{
var globalRefName = "__getslot__" + _compilation.NameMangler.GetMangledMethodName(method);
LLVMValueRef slot = LLVM.GetNamedGlobal(Module, globalRefName);
if(slot.Pointer == IntPtr.Zero)
{
slot = LLVM.AddGlobal(Module, LLVM.Int32Type(), globalRefName);
}
var vtableSlotSymbol = _compilation.NodeFactory.VTableSlot(method);
_dependencies.Add(vtableSlotSymbol);
LLVMValueRef slot = LoadAddressOfSymbolNode(vtableSlotSymbol);
return LLVM.BuildLoad(_builder, slot, string.Empty);
}

Просмотреть файл

@ -203,11 +203,7 @@ namespace ILCompiler.DependencyAnalysis
public static LLVMValueRef EmitGlobal(LLVMModuleRef module, FieldDesc field, NameMangler nameMangler)
{
if (field.IsThreadStatic)
{
throw new NotImplementedException("thread static field");
}
else if (field.IsStatic)
if (field.IsStatic)
{
if (s_staticFieldMapping.TryGetValue(field, out LLVMValueRef existingValue))
return existingValue;
@ -217,6 +213,10 @@ namespace ILCompiler.DependencyAnalysis
var llvmValue = LLVM.AddGlobal(module, valueType, nameMangler.GetMangledFieldName(field).ToString());
LLVM.SetLinkage(llvmValue, LLVMLinkage.LLVMInternalLinkage);
LLVM.SetInitializer(llvmValue, GetConstZeroArray(field.FieldType.GetElementSize().AsInt));
if (field.IsThreadStatic)
{
LLVM.SetThreadLocal(llvmValue, LLVMMisc.True);
}
s_staticFieldMapping.Add(field, llvmValue);
return llvmValue;
}
@ -699,28 +699,6 @@ namespace ILCompiler.DependencyAnalysis
if (node.ShouldSkipEmittingObjectNode(factory))
continue;
if (node is EETypeNode)
{
DefType t = ((EETypeNode)node).Type.GetClosestDefType();
int iSlot = GetVTableSlotsCount(factory, t.BaseType);
var pointerSize = factory.Target.PointerSize;
foreach (MethodDesc m in factory.VTable(t).Slots)
{
// set _getslot variable to sizeof(EEType) + iSlot * pointer size
string realSymbolName = factory.NameMangler.GetMangledMethodName(m).ToString();
var globalRefName = "__getslot__" + realSymbolName;
LLVMValueRef slot = LLVM.GetNamedGlobal(compilation.Module, globalRefName);
if (slot.Pointer == IntPtr.Zero)
{
slot = LLVM.AddGlobal(compilation.Module, LLVM.Int32Type(), globalRefName);
}
LLVM.SetInitializer(slot, LLVM.ConstInt(LLVM.Int32Type(), (ulong)((EETypeNode.GetVTableOffset(pointerSize) / pointerSize) + iSlot), LLVMMisc.False));
LLVM.SetGlobalConstant(slot, LLVMMisc.True);
iSlot++;
}
}
objectWriter.StartObjectNode(node);
ObjectData nodeContents = node.GetData(factory);

Просмотреть файл

@ -10,6 +10,8 @@ namespace ILCompiler.DependencyAnalysis
{
public sealed class WebAssemblyCodegenNodeFactory : NodeFactory
{
private NodeCache<MethodDesc, WebAssemblyVTableSlotNode> _vTableSlotNodes;
public WebAssemblyCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager,
InteropStubManager interopStubManager, NameMangler nameMangler, VTableSliceProvider vtableSliceProvider, DictionaryLayoutProvider dictionaryLayoutProvider)
: base(context,
@ -22,6 +24,10 @@ namespace ILCompiler.DependencyAnalysis
dictionaryLayoutProvider,
new ImportedNodeProviderThrowing())
{
_vTableSlotNodes = new NodeCache<MethodDesc, WebAssemblyVTableSlotNode>(methodKey =>
{
return new WebAssemblyVTableSlotNode(methodKey);
});
}
public override bool IsCppCodegenTemporaryWorkaround => true;
@ -38,6 +44,11 @@ namespace ILCompiler.DependencyAnalysis
}
}
public WebAssemblyVTableSlotNode VTableSlot(MethodDesc method)
{
return _vTableSlotNodes.GetOrAdd(method);
}
protected override IMethodNode CreateUnboxingStubNode(MethodDesc method)
{
// TODO: this is wrong: this returns an unstubbed node

Просмотреть файл

@ -0,0 +1,77 @@
// 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.Diagnostics;
using Internal.Runtime;
using Internal.Text;
using Internal.TypeSystem;
namespace ILCompiler.DependencyAnalysis
{
public class WebAssemblyVTableSlotNode : ObjectNode, ISymbolDefinitionNode
{
private readonly MethodDesc _targetMethod;
public WebAssemblyVTableSlotNode(MethodDesc targetMethod)
{
Debug.Assert(targetMethod.IsVirtual);
Debug.Assert(!targetMethod.IsSharedByGenericInstantiations);
_targetMethod = targetMethod;
}
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
sb.Append(GetMangledName(nameMangler, _targetMethod));
}
public int Offset => 0;
public override bool IsShareable => false;
public static string GetMangledName(NameMangler nameMangler, MethodDesc method)
{
return "__getslot__" + nameMangler.GetMangledMethodName(method);
}
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectNodeSection Section => ObjectNodeSection.DataSection;
public override bool StaticDependenciesAreComputed => true;
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
{
DependencyList result = new DependencyList();
if (!factory.VTable(_targetMethod.OwningType).HasFixedSlots)
{
result.Add(factory.VirtualMethodUse(_targetMethod), "VTable method use");
}
return result;
}
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
Debug.Assert((EETypeNode.GetVTableOffset(factory.Target.PointerSize) % factory.Target.PointerSize) == 0, "vtable offset must be aligned");
ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly);
objData.AddSymbol(this);
if (!relocsOnly)
{
var tableOffset = EETypeNode.GetVTableOffset(factory.Target.PointerSize) / factory.Target.PointerSize;
objData.EmitInt(tableOffset + VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod));
}
return objData.ToObjectData();
}
protected override int ClassCode => 0;
protected override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer)
{
return comparer.Compare(_targetMethod, ((WebAssemblyVTableSlotNode)other)._targetMethod);
}
}
}

Просмотреть файл

@ -16,15 +16,16 @@ namespace ILCompiler
{
internal WebAssemblyCodegenConfigProvider Options { get; }
internal LLVMModuleRef Module { get; }
public new WebAssemblyCodegenNodeFactory NodeFactory { get; }
internal WebAssemblyCodegenCompilation(
DependencyAnalyzerBase<NodeFactory> dependencyGraph,
NodeFactory nodeFactory,
WebAssemblyCodegenNodeFactory nodeFactory,
IEnumerable<ICompilationRootProvider> roots,
Logger logger,
WebAssemblyCodegenConfigProvider options)
: base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), null, logger)
{
NodeFactory = nodeFactory;
LLVM.LoadLibrary_libLLVM("./libLLVM-x64.dll");
Module = LLVM.ModuleCreateWithName("netscripten");
LLVM.SetTarget(Module, "asmjs-unknown-emscripten");

Просмотреть файл

@ -44,6 +44,7 @@
<Compile Include="Compiler\DependencyAnalysis\RawMainMethodRootProvider.cs" />
<Compile Include="Compiler\DependencyAnalysis\WebAssemblyCodegenNodeFactory.cs" />
<Compile Include="Compiler\DependencyAnalysis\WebAssemblyMethodCodeNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\WebAssemblyVTableSlotNode.cs" />
<Compile Include="Compiler\WebAssemblyNodeMangler.cs" />
<Compile Include="Compiler\WebAssemblyCodegenCompilation.cs" />
<Compile Include="Compiler\WebAssemblyCodegenCompilationBuilder.cs" />

Просмотреть файл

@ -8,6 +8,8 @@ using System.Runtime.InteropServices;
internal static class Program
{
private static int staticInt;
[ThreadStatic]
private static int threadStaticInt;
private static unsafe void Main(string[] args)
{
Add(1, 2);
@ -39,6 +41,17 @@ internal static class Program
PrintLine("static int field test: Ok.");
}
if(threadStaticInt == 0)
{
PrintLine("thread static int initial value field test: Ok.");
}
threadStaticInt = 9;
if(threadStaticInt == 9)
{
PrintLine("thread static int field test: Ok.");
}
var boxedInt = (object)tempInt;
if(((int)boxedInt) == 9)
{