Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMorgan Brown <morganbr@users.noreply.github.com>2018-03-06 15:15:09 +0300
committerGitHub <noreply@github.com>2018-03-06 15:15:09 +0300
commit19116562388ed45b336321456079fc7c398556fc (patch)
treebb027294e397853d1db5852090881de0b1034088 /src/ILCompiler.WebAssembly
parent40b2afa6ba669c28aeffdd5a8d544220b2312232 (diff)
Static constructors for WebAssembly (#5425)
* Implement static constructor triggering. Also includes several bug fixes found in the process: * Don't make the ClassConstructorRunner depend on module intialization * Moves non-GC statics and thread statics from globals to the type's data regions. GC statics can't be moved until we can call InitializeModules on startup. * Devirtualizing interface calls to structs in order to be able to compile the class constructor runner. * Add a prolog block before Block0 to allow branching to Block0, which happens in retail builds * Correct use of unreachable in traps and at the end of finally blocks to fix more retail build problems * Stop reusing spill slots when a spilled value is spilled again. This avoids cases where the spills feeding into a block don't use the same slot * Enable thunks for NativeCallable methods and call RhpReversePInvoke for them * Fix alignment for cases where a small type is followed by a larger one * Workaround for Emscripten atomics bug
Diffstat (limited to 'src/ILCompiler.WebAssembly')
-rw-r--r--src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs257
-rw-r--r--src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs8
2 files changed, 203 insertions, 62 deletions
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
index 8b0936d1e..b4859b849 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
@@ -110,6 +110,8 @@ namespace Internal.IL
{
FindBasicBlocks();
+ GenerateProlog();
+
try
{
ImportBasicBlocks();
@@ -130,39 +132,53 @@ namespace Internal.IL
LLVM.PositionBuilderAtEnd(_builder, trapBlock);
EmitTrapCall();
- LLVM.BuildRetVoid(_builder);
throw;
}
finally
{
// Generate thunk for runtime exports
- if (_method.IsRuntimeExport)
+ if (_method.IsRuntimeExport || _method.IsNativeCallable)
{
- EmitNativeToManagedThunk(_compilation, _method, ((EcmaMethod)_method).GetRuntimeExportName(), _llvmFunction);
+ EcmaMethod ecmaMethod = ((EcmaMethod)_method);
+ string exportName = ecmaMethod.IsRuntimeExport ? ecmaMethod.GetRuntimeExportName() : ecmaMethod.GetNativeCallableExportName();
+ EmitNativeToManagedThunk(_compilation, _method, exportName, _llvmFunction);
}
}
}
private void GenerateProlog()
{
- if (!_methodIL.IsInitLocals)
- {
- return;
- }
+ LLVMBasicBlockRef prologBlock = LLVM.AppendBasicBlock(_llvmFunction, "Prolog");
+ LLVM.PositionBuilderAtEnd(_builder, prologBlock);
- int totalLocalSize = 0;
- foreach(LocalVariableDefinition local in _locals)
+ if (_methodIL.IsInitLocals)
{
- totalLocalSize = PadNextOffset(local.Type, totalLocalSize);
+ int totalLocalSize = 0;
+ foreach (LocalVariableDefinition local in _locals)
+ {
+ totalLocalSize = PadNextOffset(local.Type, totalLocalSize);
+ }
+
+ var sp = LLVM.GetFirstParam(_llvmFunction);
+ int paramOffset = GetTotalParameterOffset();
+ for (int i = 0; i < totalLocalSize; i++)
+ {
+ var stackOffset = LLVM.BuildGEP(_builder, sp, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)(paramOffset + i), LLVMMisc.False) }, String.Empty);
+ LLVM.BuildStore(_builder, LLVM.ConstInt(LLVM.Int8Type(), 0, LLVMMisc.False), stackOffset);
+ }
}
- var sp = LLVM.GetFirstParam(_llvmFunction);
- int paramOffset = GetTotalParameterOffset();
- for (int i = 0; i < totalLocalSize; i++)
+ MetadataType metadataType = (MetadataType)_thisType;
+ if (!metadataType.IsBeforeFieldInit)
{
- var stackOffset = LLVM.BuildGEP(_builder, sp, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)(paramOffset + i), LLVMMisc.False) }, String.Empty);
- LLVM.BuildStore(_builder, LLVM.ConstInt(LLVM.Int8Type(), 0, LLVMMisc.False), stackOffset);
+ if (!_method.IsStaticConstructor && _method.Signature.IsStatic || _method.IsConstructor || (_thisType.IsValueType && !_method.Signature.IsStatic))
+ {
+ TriggerCctor(metadataType);
+ }
}
+
+ LLVMBasicBlockRef block0 = GetLLVMBasicBlockForBlock(_basicBlocks[0]);
+ LLVM.BuildBr(_builder, block0);
}
private LLVMValueRef CreateLLVMFunction(string mangledName)
@@ -278,19 +294,9 @@ namespace Internal.IL
}
}
- bool isFirstBlock = false;
- if(_curBasicBlock.Equals(default(LLVMBasicBlockRef)))
- {
- isFirstBlock = true;
- }
_curBasicBlock = GetLLVMBasicBlockForBlock(basicBlock);
LLVM.PositionBuilderAtEnd(_builder, _curBasicBlock);
-
- if(isFirstBlock)
- {
- GenerateProlog();
- }
}
private void EndImportingBasicBlock(BasicBlock basicBlock)
@@ -305,10 +311,6 @@ namespace Internal.IL
LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(_basicBlocks[_currentOffset]));
}
- else
- {
- LLVM.BuildRet(_builder, default(LLVMValueRef));
- }
}
}
@@ -749,6 +751,7 @@ namespace Internal.IL
{
offset = PadNextOffset(_signature[i], offset);
}
+ offset = PadOffset(argType, offset);
}
private void GetLocalSizeAndOffsetAtIndex(int index, out int size, out int offset)
@@ -761,6 +764,7 @@ namespace Internal.IL
{
offset = PadNextOffset(_locals[i].Type, offset);
}
+ offset = PadOffset(local.Type, offset);
}
private void GetSpillSizeAndOffsetAtIndex(int index, out int size, out int offset)
@@ -773,6 +777,7 @@ namespace Internal.IL
{
offset = PadNextOffset(_spilledExpressions[i].Type, offset);
}
+ offset = PadOffset(spill.Type, offset);
}
public int PadNextOffset(TypeDesc type, int atOffset)
@@ -810,7 +815,7 @@ namespace Internal.IL
{
TypeDesc type;
LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out type);
- _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldloca", typedLoadLocation, type.MakePointerType()));
+ _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldloca", typedLoadLocation, type.MakeByRefType()));
}
private void ImportDup()
@@ -959,9 +964,46 @@ namespace Internal.IL
if (!_compilation.HasFixedSlotVTable(callee.OwningType))
AddVirtualMethodReference(callee);
- //TODO: needs runtime support for DispatchByInterface
+ bool isValueTypeCall = false;
+ TypeDesc thisType = thisPointer.Type;
+ TypeFlags category = thisType.Category;
+ MethodDesc targetMethod = null;
+ TypeDesc parameterType = null;
+
+ if (category == TypeFlags.ByRef)
+ {
+ parameterType = ((ByRefType)thisType).ParameterType;
+ if (parameterType.IsValueType)
+ {
+ isValueTypeCall = true;
+ }
+ }
if (callee.OwningType.IsInterface)
- throw new NotImplementedException("Interface call");
+ {
+ // For value types, devirtualize the call
+ if (isValueTypeCall)
+ {
+ targetMethod = parameterType.ResolveInterfaceMethodTarget(callee);
+ }
+ else
+ {
+ //TODO: needs runtime support for DispatchByInterface
+ throw new NotImplementedException("Interface call");
+ }
+ }
+ else
+ {
+ if (isValueTypeCall)
+ {
+ targetMethod = parameterType.FindVirtualFunctionTargetMethodOnObjectType(callee);
+ }
+ }
+
+ if (targetMethod != null)
+ {
+ AddMethodReference(targetMethod);
+ return GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(targetMethod).ToString());
+ }
return GetCallableVirtualMethod(thisPointer.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), callee);
}
@@ -1204,11 +1246,14 @@ namespace Internal.IL
argType = signature[index - instanceAdjustment];
}
+ // The previous argument might have left this type unaligned, so pad if necessary
+ argOffset = PadOffset(argType, argOffset);
+
LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType);
ImportStoreHelper(toStore.ValueAsType(valueType, _builder), valueType, castShadowStack, (uint)argOffset);
- argOffset = PadNextOffset(argType, argOffset);
+ argOffset += argType.GetElementSize().AsInt;
}
LLVMValueRef fn;
@@ -1382,25 +1427,61 @@ namespace Internal.IL
LLVMTypeRef thunkSig = LLVM.FunctionType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), llvmParams, false);
LLVMValueRef thunkFunc = LLVM.AddFunction(compilation.Module, nativeName, thunkSig);
- LLVMBasicBlockRef block = LLVM.AppendBasicBlock(thunkFunc, "Block0");
+ LLVMBasicBlockRef shadowStackSetupBlock = LLVM.AppendBasicBlock(thunkFunc, "ShadowStackSetupBlock");
+ LLVMBasicBlockRef allocateShadowStackBlock = LLVM.AppendBasicBlock(thunkFunc, "allocateShadowStackBlock");
+ LLVMBasicBlockRef managedCallBlock = LLVM.AppendBasicBlock(thunkFunc, "ManagedCallBlock");
+
LLVMBuilderRef builder = LLVM.CreateBuilder();
- LLVM.PositionBuilderAtEnd(builder, block);
+ LLVM.PositionBuilderAtEnd(builder, shadowStackSetupBlock);
- LLVMValueRef shadowStack = LLVM.BuildLoad(builder, ShadowStackTop, "");
+ // Allocate shadow stack if it's null
+ LLVMValueRef shadowStackPtr = LLVM.BuildAlloca(builder, LLVM.PointerType(LLVM.Int8Type(), 0), "ShadowStackPtr");
+ LLVMValueRef savedShadowStack = LLVM.BuildLoad(builder, ShadowStackTop, "SavedShadowStack");
+ LLVM.BuildStore(builder, savedShadowStack, shadowStackPtr);
+ LLVMValueRef shadowStackNull = LLVM.BuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, savedShadowStack, LLVM.ConstPointerNull(LLVM.PointerType(LLVM.Int8Type(), 0)), "ShadowStackNull");
+ LLVM.BuildCondBr(builder, shadowStackNull, allocateShadowStackBlock, managedCallBlock);
+ LLVM.PositionBuilderAtEnd(builder, allocateShadowStackBlock);
+
+ LLVMValueRef newShadowStack = LLVM.BuildArrayMalloc(builder, LLVM.Int8Type(), BuildConstInt32(1000000), "NewShadowStack");
+ LLVM.BuildStore(builder, newShadowStack, shadowStackPtr);
+ LLVM.BuildBr(builder, managedCallBlock);
+
+ LLVM.PositionBuilderAtEnd(builder, managedCallBlock);
+ LLVMTypeRef reversePInvokeFrameType = LLVM.StructType(new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false);
+ LLVMValueRef reversePInvokeFrame = default(LLVMValueRef);
+ LLVMTypeRef reversePInvokeFunctionType = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(reversePInvokeFrameType, 0) }, false);
+ if (method.IsNativeCallable)
+ {
+ reversePInvokeFrame = LLVM.BuildAlloca(builder, reversePInvokeFrameType, "ReversePInvokeFrame");
+ LLVMValueRef RhpReversePInvoke2 = GetOrCreateLLVMFunction("RhpReversePInvoke2", reversePInvokeFunctionType);
+ LLVM.BuildCall(builder, RhpReversePInvoke2, new LLVMValueRef[] { reversePInvokeFrame }, "");
+ }
+
+ LLVMValueRef shadowStack = LLVM.BuildLoad(builder, shadowStackPtr, "ShadowStack");
int curOffset = 0;
+ curOffset = PadNextOffset(method.Signature.ReturnType, curOffset);
+ LLVMValueRef calleeFrame = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { BuildConstInt32(curOffset) }, "calleeFrame");
+
for (int i = 0; i < llvmParams.Length; i++)
{
+ curOffset = PadOffset(method.Signature[i], curOffset);
LLVMValueRef argAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "arg" + i);
LLVM.BuildStore(builder, LLVM.GetParam(thunkFunc, (uint)i), CastIfNecessary(builder, argAddr, LLVM.PointerType(llvmParams[i], 0)));
curOffset = PadNextOffset(method.Signature[i], curOffset);
}
- LLVMValueRef retAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "retAddr");
- LLVM.BuildCall(builder, managedFunction, new LLVMValueRef[] { shadowStack, retAddr }, "");
+ LLVM.BuildCall(builder, managedFunction, new LLVMValueRef[] { calleeFrame, shadowStack }, "");
+
+ if (method.IsNativeCallable)
+ {
+ LLVMValueRef RhpReversePInvokeReturn2 = GetOrCreateLLVMFunction("RhpReversePInvokeReturn2", reversePInvokeFunctionType);
+ LLVM.BuildCall(builder, RhpReversePInvokeReturn2, new LLVMValueRef[] { reversePInvokeFrame }, "");
+ }
+
if (method.Signature.ReturnType != compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Void))
{
- LLVM.BuildRet(builder, LLVM.BuildLoad(builder, CastIfNecessary(builder, retAddr, LLVM.PointerType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), 0)), ""));
+ LLVM.BuildRet(builder, LLVM.BuildLoad(builder, CastIfNecessary(builder, shadowStack, LLVM.PointerType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), 0)), ""));
}
else
{
@@ -1470,9 +1551,6 @@ namespace Internal.IL
if (opcode == ILOpcode.br)
{
ImportFallthrough(target);
- //TODO: why does this illegal branch happen in System.Reflection.MemberFilter.ctor
- if (target.StartOffset == 0)
- throw new InvalidProgramException();
LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(target));
}
else
@@ -1593,9 +1671,6 @@ namespace Internal.IL
}
}
}
- //TODO: why did this happen only during an optimized build of [System.Private.CoreLib]System.Threading.Lock.ReleaseContended
- if (target.StartOffset == 0)
- throw new NotImplementedException("cant branch to entry basic block");
ImportFallthrough(target);
ImportFallthrough(fallthrough);
@@ -1630,7 +1705,7 @@ namespace Internal.IL
var expressionPointer = pointer as ExpressionEntry;
if(type == null)
{
- type = GetWellKnownType(WellKnownType.Object).MakeByRefType();
+ type = GetWellKnownType(WellKnownType.Object);
}
LLVMValueRef pointerElementType = pointer.ValueAsType(type.MakePointerType(), _builder);
@@ -1827,6 +1902,7 @@ namespace Internal.IL
if(enumCleanTargetType != null && targetType.IsPrimitive)
{
if(enumCleanTargetType.IsWellKnownType(WellKnownType.Byte) ||
+ enumCleanTargetType.IsWellKnownType(WellKnownType.Char) ||
enumCleanTargetType.IsWellKnownType(WellKnownType.UInt16) ||
enumCleanTargetType.IsWellKnownType(WellKnownType.UInt32) ||
enumCleanTargetType.IsWellKnownType(WellKnownType.UInt64) ||
@@ -2186,7 +2262,49 @@ namespace Internal.IL
if (!isStatic)
_stack.Pop();
- return WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler);
+ ISymbolNode node;
+ MetadataType owningType = (MetadataType)field.OwningType;
+ LLVMValueRef staticBase;
+ int fieldOffset = field.Offset.AsInt;
+
+ // TODO: We need the right thread static per thread
+ if (field.IsThreadStatic)
+ {
+ node = _compilation.NodeFactory.TypeThreadStaticsSymbol(owningType);
+ staticBase = LoadAddressOfSymbolNode(node);
+ }
+
+ else if (field.HasGCStaticBase)
+ {
+ node = _compilation.NodeFactory.TypeGCStaticsSymbol(owningType);
+
+ // We can't use GCStatics in the data section until we can successfully call
+ // InitializeModules on startup, so stick with globals for now
+ //LLVMValueRef basePtrPtr = LoadAddressOfSymbolNode(node);
+ //staticBase = LLVM.BuildLoad(_builder, LLVM.BuildLoad(_builder, LLVM.BuildPointerCast(_builder, basePtrPtr, LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(LLVM.Int8Type(), 0), 0), 0), "castBasePtrPtr"), "basePtr"), "base");
+ staticBase = WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler);
+ fieldOffset = 0;
+ }
+ else
+ {
+ node = _compilation.NodeFactory.TypeNonGCStaticsSymbol(owningType);
+ staticBase = LoadAddressOfSymbolNode(node);
+ }
+
+ _dependencies.Add(node);
+
+ // Run static constructor if necessary
+ // If the type is non-BeforeFieldInit, this is handled before calling any methods on it
+ if (owningType.IsBeforeFieldInit || (!owningType.IsBeforeFieldInit && owningType != _thisType))
+ {
+ TriggerCctor(owningType);
+ }
+
+ LLVMValueRef castStaticBase = LLVM.BuildPointerCast(_builder, staticBase, LLVM.PointerType(LLVM.Int8Type(), 0), owningType.Name + "_statics");
+ LLVMValueRef fieldAddr = LLVM.BuildGEP(_builder, castStaticBase, new LLVMValueRef[] { BuildConstInt32(fieldOffset) }, field.Name + "_addr");
+
+
+ return fieldAddr;
}
else
{
@@ -2194,6 +2312,27 @@ namespace Internal.IL
}
}
+ /// <summary>
+ /// Triggers a static constructor check and call for types that have them
+ /// </summary>
+ private void TriggerCctor(MetadataType type)
+ {
+ if (_compilation.TypeSystemContext.HasLazyStaticConstructor(type))
+ {
+ ISymbolNode classConstructionContextSymbol = _compilation.NodeFactory.TypeNonGCStaticsSymbol(type);
+ _dependencies.Add(classConstructionContextSymbol);
+ LLVMValueRef firstNonGcStatic = LoadAddressOfSymbolNode(classConstructionContextSymbol);
+
+ // TODO: Codegen could check whether it has already run rather than calling into EnsureClassConstructorRun
+ // but we'd have to figure out how to manage the additional basic blocks
+ LLVMValueRef classConstructionContextPtr = LLVM.BuildGEP(_builder, firstNonGcStatic, new LLVMValueRef[] { BuildConstInt32(-2) }, "classConstructionContext");
+ StackEntry classConstructionContext = new AddressExpressionEntry(StackValueKind.NativeInt, "classConstructionContext", classConstructionContextPtr, GetWellKnownType(WellKnownType.IntPtr));
+ MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System.Runtime.CompilerServices", "ClassConstructorRunner");
+ MethodDesc helperMethod = helperType.GetKnownMethod("EnsureClassConstructorRun", null);
+ HandleCall(helperMethod, helperMethod.Signature, new StackEntry[] { classConstructionContext });
+ }
+ }
+
private void ImportLoadField(int token, bool isStatic)
{
FieldDesc field = (FieldDesc)_methodIL.GetObject(token);
@@ -2205,7 +2344,7 @@ namespace Internal.IL
{
FieldDesc field = (FieldDesc)_methodIL.GetObject(token);
LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic);
- _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldflda", fieldAddress, field.FieldType.MakePointerType()));
+ _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldflda", fieldAddress, field.FieldType.MakeByRefType()));
}
private void ImportStoreField(int token, bool isStatic)
@@ -2372,6 +2511,9 @@ namespace Internal.IL
private void ImportEndFinally()
{
+ // These are currently unreachable since we can't get into finally blocks.
+ // We'll need to change this once we have other finally block handling.
+ LLVM.BuildUnreachable(_builder);
}
private void ImportFallthrough(BasicBlock next)
@@ -2418,9 +2560,6 @@ namespace Internal.IL
if (entry == null)
throw new InvalidProgramException();
- if (currentEntry is SpilledExpressionEntry)
- continue; //this is already a sharable value
-
StoreTemp(entry.LocalIndex, currentEntry.ValueAsType(entry.Type, _builder));
}
}
@@ -2453,16 +2592,11 @@ namespace Internal.IL
private StackEntry NewSpillSlot(StackEntry entry)
{
- if (entry is SpilledExpressionEntry)
- return entry;
- else
- {
- var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal
- var entryIndex = _spilledExpressions.Count;
- var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "spilled" + entryIndex, entryType, entryIndex, this);
- _spilledExpressions.Add(newEntry);
- return newEntry;
- }
+ var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal
+ var entryIndex = _spilledExpressions.Count;
+ var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "spilled" + entryIndex, entryType, entryIndex, this);
+ _spilledExpressions.Add(newEntry);
+ return newEntry;
}
private StackEntry TakeAddressOf(StackEntry entry)
@@ -2535,6 +2669,7 @@ namespace Internal.IL
TrapFunction = LLVM.AddFunction(Module, "llvm.trap", LLVM.FunctionType(LLVM.VoidType(), Array.Empty<LLVMTypeRef>(), false));
}
LLVM.BuildCall(_builder, TrapFunction, Array.Empty<LLVMValueRef>(), string.Empty);
+ LLVM.BuildUnreachable(_builder);
}
private void EmitDoNothingCall()
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
index 8808a2e02..29a6a26cd 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
@@ -243,7 +243,13 @@ namespace ILCompiler.DependencyAnalysis
LLVMTypeRef reversePInvokeFrameType = LLVM.StructType(new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false);
LLVMValueRef reversePinvokeFrame = LLVM.BuildAlloca(builder, reversePInvokeFrameType, "ReversePInvokeFrame");
- LLVMValueRef RhpReversePInvoke2 = LLVM.AddFunction(Module, "RhpReversePInvoke2", LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(reversePInvokeFrameType, 0) }, false));
+ LLVMValueRef RhpReversePInvoke2 = LLVM.GetNamedFunction(Module, "RhpReversePInvoke2");
+
+ if (RhpReversePInvoke2.Pointer == IntPtr.Zero)
+ {
+ RhpReversePInvoke2 = LLVM.AddFunction(Module, "RhpReversePInvoke2", LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(reversePInvokeFrameType, 0) }, false));
+ }
+
LLVM.BuildCall(builder, RhpReversePInvoke2, new LLVMValueRef[] { reversePinvokeFrame }, "");
var shadowStack = LLVM.BuildMalloc(builder, LLVM.ArrayType(LLVM.Int8Type(), 1000000), String.Empty);