Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 91 additions & 10 deletions src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1072,7 +1072,7 @@ private void ImportStoreVar(int index, bool argument)
TypeDesc varType;
StackEntry toStore = _stack.Pop();
LLVMValueRef varAddress = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out varType);
CastingStore(varAddress, toStore, varType, $"Variable{index}_");
CastingStore(varAddress, toStore, varType, false, $"Variable{index}_");
}

private void ImportStoreHelper(LLVMValueRef toStore, LLVMTypeRef valueType, LLVMValueRef basePtr, uint offset, string name = null, LLVMBuilderRef builder = default(LLVMBuilderRef))
Expand Down Expand Up @@ -1104,10 +1104,75 @@ private LLVMValueRef CastToPointerToTypeDesc(LLVMValueRef source, TypeDesc type,
return CastIfNecessary(source, LLVMTypeRef.CreatePointer(GetLLVMTypeForTypeDesc(type), 0), (name ?? "") + type.ToString());
}

private void CastingStore(LLVMValueRef address, StackEntry value, TypeDesc targetType, string targetName = null)
private void CastingStore(LLVMValueRef address, StackEntry value, TypeDesc targetType, bool withBarrier, string targetName = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: withBarrier -> withGCBarrier to avoid confusion with memory barrier.

{
var typedStoreLocation = CastToPointerToTypeDesc(address, targetType, targetName);
_builder.BuildStore(value.ValueAsType(targetType, _builder), typedStoreLocation);
if (withBarrier && targetType.IsGCPointer)
{
CallRuntime(_method.Context, "InternalCalls", "RhpAssignRef", new StackEntry[]
{
new ExpressionEntry(StackValueKind.Int32, "address", address), value
});
}
else
{
var typedStoreLocation = CastToPointerToTypeDesc(address, targetType, targetName);
var llvmValue = value.ValueAsType(targetType, _builder);
if (withBarrier && IsStruct(targetType))
{
StoreStruct(address, llvmValue, targetType, typedStoreLocation);
}
else
{
_builder.BuildStore(llvmValue, typedStoreLocation);
}
}
}

private static bool IsStruct(TypeDesc typeDesc)
{
return typeDesc.IsValueType && !typeDesc.IsPrimitive && !typeDesc.IsEnum;
}

private void StoreStruct(LLVMValueRef address, LLVMValueRef llvmValue, TypeDesc targetType, LLVMValueRef typedStoreLocation, bool childStruct = false)
{
foreach (FieldDesc f in targetType.GetFields())
{
if (f.IsStatic) continue;
if (IsStruct(f.FieldType) && llvmValue.TypeOf.IsPackedStruct)
{
LLVMValueRef targetAddress = _builder.BuildGEP(address, new[] { BuildConstInt32(f.Offset.AsInt) });
uint index = LLVMSharpInterop.ElementAtOffset(_compilation.TargetData, llvmValue.TypeOf, (ulong)f.Offset.AsInt);
LLVMValueRef fieldValue = _builder.BuildExtractValue(llvmValue, index);
//recurse into struct
StoreStruct(targetAddress, fieldValue, f.FieldType, CastToPointerToTypeDesc(targetAddress, f.FieldType), true);
}
else if (f.FieldType.IsGCPointer)
{
LLVMValueRef targetAddress = _builder.BuildGEP(address, new[] {BuildConstInt32(f.Offset.AsInt)});
LLVMValueRef fieldValue;
if (llvmValue.TypeOf.IsPackedStruct)
{
uint index = LLVMSharpInterop.ElementAtOffset(_compilation.TargetData, llvmValue.TypeOf, (ulong) f.Offset.AsInt);
fieldValue = _builder.BuildExtractValue(llvmValue, index);
Debug.Assert(fieldValue.TypeOf.Kind == LLVMTypeKind.LLVMPointerTypeKind, "expected an LLVM pointer type");
}
else
{
// single field IL structs are not LLVM structs
fieldValue = llvmValue;
}
CallRuntime(_method.Context, "InternalCalls", "RhpAssignRef",
new StackEntry[]
{
new ExpressionEntry(StackValueKind.Int32, "targetAddress", targetAddress),
new ExpressionEntry(StackValueKind.ObjRef, "sourceAddress", fieldValue)
});
}
}
if (!childStruct)
{
_builder.BuildStore(llvmValue, typedStoreLocation); // just copy all the fields again for simplicity, if all the fields where set using RhpAssignRef then a possible optimisation would be to skip this line
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has a potential for race conditions if the LLVM codegen is used for anything multithreaded. Maybe add a comment about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, think I understand - thread1 can RhpAssignRef a struct field (after thread2 has done the same), thread1 calls BuildStore and then thread2 can get switched back in and executes BuildStore. Thread 2's field which requires the GC write barrier is then lost. Will add a comment for now.

}
}

private LLVMValueRef CastIfNecessary(LLVMValueRef source, LLVMTypeRef valueType, string name = null, bool unsigned = false)
Expand Down Expand Up @@ -3578,7 +3643,6 @@ private void ImportLoadIndirect(TypeDesc type)
{
var pointer = _stack.Pop();
Debug.Assert(pointer is ExpressionEntry || pointer is ConstantEntry);
var expressionPointer = pointer as ExpressionEntry;
if (type == null)
{
type = GetWellKnownType(WellKnownType.Object);
Expand All @@ -3600,19 +3664,36 @@ private void ImportStoreIndirect(TypeDesc type)
StackEntry destinationPointer = _stack.Pop();
LLVMValueRef typedValue;
LLVMValueRef typedPointer;
bool requireWriteBarrier;

if (type != null)
{
typedValue = value.ValueAsType(type, _builder);
typedPointer = destinationPointer.ValueAsType(type.MakePointerType(), _builder);
typedValue = value.ValueAsType(type, _builder);
if (IsStruct(type))
{
StoreStruct(typedPointer, typedValue, type, typedPointer);
return;
}
requireWriteBarrier = type.IsGCPointer;
}
else
{
typedPointer = destinationPointer.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0), _builder);
typedValue = value.ValueAsInt32(_builder, false);
requireWriteBarrier = (value is ExpressionEntry) && !((ExpressionEntry)value).RawLLVMValue.IsNull && value.Type.IsGCPointer;
}
if (requireWriteBarrier)
{
CallRuntime(_method.Context, "InternalCalls", "RhpAssignRef", new StackEntry[]
{
new ExpressionEntry(StackValueKind.Int32, "typedPointer", typedPointer), value
});
}
else
{
_builder.BuildStore(typedValue, typedPointer);
}

_builder.BuildStore(typedValue, typedPointer);
}

private void ImportBinaryOperation(ILOpcode opcode)
Expand Down Expand Up @@ -4720,7 +4801,7 @@ private void ImportStoreField(int token, bool isStatic)
StackEntry valueEntry = _stack.Pop();

LLVMValueRef fieldAddress = GetFieldAddress(runtimeDeterminedField, field, isStatic);
CastingStore(fieldAddress, valueEntry, field.FieldType);
CastingStore(fieldAddress, valueEntry, field.FieldType, true);
}

// Loads symbol address. Address is represented as a i32*
Expand Down Expand Up @@ -4936,7 +5017,7 @@ private void ImportStoreElement(TypeDesc elementType)
StackEntry arrayReference = _stack.Pop();
var nullSafeElementType = elementType ?? GetWellKnownType(WellKnownType.Object);
LLVMValueRef elementAddress = GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder), nullSafeElementType);
CastingStore(elementAddress, value, nullSafeElementType);
CastingStore(elementAddress, value, nullSafeElementType, true);
}

private void ImportLoadLength()
Expand Down
15 changes: 15 additions & 0 deletions src/ILCompiler.WebAssembly/src/CodeGen/LLVMSharpInterop.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using LLVMSharp.Interop;

namespace Internal.IL
{
/// <summary>
/// Workaround while waiting for https://github.com/microsoft/LLVMSharp/pull/141
/// </summary>
internal class LLVMSharpInterop
{
internal static unsafe uint ElementAtOffset(LLVMTargetDataRef targetDataRef, LLVMTypeRef structTypeRef, ulong offset)
{
return LLVM.ElementAtOffset(targetDataRef, structTypeRef, offset);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public sealed class WebAssemblyCodegenCompilation : Compilation
{
internal WebAssemblyCodegenConfigProvider Options { get; }
internal LLVMModuleRef Module { get; }
internal LLVMTargetDataRef TargetData { get; }
public new WebAssemblyCodegenNodeFactory NodeFactory { get; }
internal LLVMDIBuilderRef DIBuilder { get; }
internal Dictionary<string, DebugMetadata> DebugMetadataMap { get; }
Expand All @@ -32,7 +33,7 @@ internal WebAssemblyCodegenCompilation(
{
NodeFactory = nodeFactory;
LLVMModuleRef m = LLVMModuleRef.CreateWithName("netscripten");
m.Target = "wasm32-unknown-unknown-wasm";
m.Target = "wasm32-unknown-emscripten";
// https://llvm.org/docs/LangRef.html#langref-datalayout
// e litte endian, mangled names
// m:e ELF mangling
Expand All @@ -41,8 +42,8 @@ internal WebAssemblyCodegenCompilation(
// n:32:64 native widths
// S128 natural alignment of stack
m.DataLayout = "e-m:e-p:32:32-i64:64-n32:64-S128";
Module = m;

Module = m;
TargetData = m.CreateExecutionEngine().TargetData;
Options = options;
DIBuilder = Module.CreateDIBuilder();
DebugMetadataMap = new Dictionary<string, DebugMetadata>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
</Compile>
<Compile Include="CodeGen\DebugMetadata.cs" />
<Compile Include="CodeGen\ILToWebAssemblyImporter_Statics.cs" />
<Compile Include="CodeGen\LLVMSharpInterop.cs" />
<Compile Include="CodeGen\WebAssemblyObjectWriter.cs" />
<Compile Include="Compiler\DependencyAnalysis\EHInfoNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\RawMainMethodRootProvider.cs" />
Expand Down
Loading