enabled thread static fields in WebAssembly - #4999
Conversation
That sounds like a bug somewhere. To put it into a sample: the code below is not supposed to have an EEType for internal class MyProgram
{
protected virtual void DoNothing() { }
private static void Main(string[] args)
{
MyProgram p = null;
if (args == null)
p.DoNothing();
}
} |
|
How can I call a method via virtual slot without an eetype? Is this case supposed to be devirtualized? |
You don't need to directly reference the EEType at the callsite. The EEType is implicitly looked up by dereferencing the The EEType might be required because of how virtual calls got hooked up for WASM, but this can be implemented without the EEType. E.g. a scheme that would likely work is:
Once you have that, to generate a virtual call, you would:
|
| { | ||
| slot = LLVM.AddGlobal(Module, LLVM.Int32Type(), globalRefName); | ||
| } | ||
| var vtableSlotSymbol = ((WebAssemblyCodegenNodeFactory)_compilation.NodeFactory).VTableSlot(method); |
There was a problem hiding this comment.
It might be a general improvement to add public new WebAssemblyCodegenNodeFactory NodeFactory property to WebAssemblyCodegenCompilation so that this cast is not necessary. WebAssemblyCodegenCompilationBuilder can just pass it as a strongly typed argument to the constructor and Compilation can save it.
| if (!relocsOnly) | ||
| { | ||
| var tableOffset = EETypeNode.GetVTableOffset(factory.Target.PointerSize) / factory.Target.PointerSize; | ||
| objData.EmitInt(tableOffset + VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod)); |
There was a problem hiding this comment.
Is this multiplied by pointer size at runtime? It might be better to just save it premultiplied by pointer size so that this is a simple addition at runtime.
There was a problem hiding this comment.
storing it as a multiple of pointers allows the LLVM/WASM instructions to be shorter because we treat the EEType as a blob of function pointers and this lets us directly index without having to cast anything.
| { | ||
| public sealed class WebAssemblyCodegenNodeFactory : NodeFactory | ||
| { | ||
| private NodeCache<MethodKey, WebAssemblyVTableSlotNode> _vTableSlotNodes; |
There was a problem hiding this comment.
This can just use MethodDesc as the key. MethodKey is used when the method can be an unboxing stub. It can't in this case.
|
|
||
| public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) | ||
| { | ||
|
|
There was a problem hiding this comment.
Nit: unnecessary blank line
|
|
||
| if (!relocsOnly) | ||
| { | ||
| var tableOffset = EETypeNode.GetVTableOffset(factory.Target.PointerSize) / factory.Target.PointerSize; |
There was a problem hiding this comment.
While I don't think we would ever want vtable slots to be unaligned, maybe assert that the offset is divisible by pointer size?
| internal WebAssemblyCodegenConfigProvider Options { get; } | ||
| internal LLVMModuleRef Module { get; } | ||
|
|
||
| public new WebAssemblyCodegenNodeFactory NodeFactory { get; private set; } |
There was a problem hiding this comment.
Nit: remove the private set; part to make it a read only property.
| LLVM.SetInitializer(llvmValue, GetConstZeroArray(field.FieldType.GetElementSize().AsInt)); | ||
| if (field.IsThreadStatic) | ||
| { | ||
| LLVM.SetThreadLocal(llvmValue, LLVMMisc.True); |
There was a problem hiding this comment.
It's cool that LLVM can do this for us 😄. What happens with initialization for this? Can you please also add a test using a thread local? I know we can't test it multithreaded, but it would be good to just ensure reading/writing works right.
There was a problem hiding this comment.
@hippiehunter what I meant about initialization is that .NET thread static fields are supposed to be zero/null initialized. Does LLVM do that for us or do you also need to do some sort of initialization?
There was a problem hiding this comment.
I've added a test to validate the initial value of zero and that works. My understanding is that this should all be managed by the call to SetInitializer directly above.
morganbr
left a comment
There was a problem hiding this comment.
Looks good, thanks, @hippiehunter!
@morganbr
I've enabled thread static fields, it's generating quite a bit more code as a result and it ended up pulling in some weirdness around virtual calls to System.Type. The EEType wasnt getting output, so I've put in a dependency for the owning type when calling a virtual method. If you have any ideas for a different place to trigger the reference for the EEType that works too.