diff --git a/src/Common/src/Internal/Runtime/RuntimeConstants.cs b/src/Common/src/Internal/Runtime/RuntimeConstants.cs
index c219ea2a5ac..a01ed788e52 100644
--- a/src/Common/src/Internal/Runtime/RuntimeConstants.cs
+++ b/src/Common/src/Internal/Runtime/RuntimeConstants.cs
@@ -4,15 +4,6 @@
namespace Internal.Runtime
{
- internal static class FatFunctionPointerConstants
- {
- ///
- /// Offset by which fat function pointers are shifted to distinguish them
- /// from real function pointers.
- ///
- public const int Offset = 2;
- }
-
internal static class IndirectionConstants
{
///
diff --git a/src/Common/src/TypeSystem/Aot/TargetDetails.Aot.cs b/src/Common/src/TypeSystem/Aot/TargetDetails.Aot.cs
new file mode 100644
index 00000000000..51b80b376a9
--- /dev/null
+++ b/src/Common/src/TypeSystem/Aot/TargetDetails.Aot.cs
@@ -0,0 +1,17 @@
+// 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.
+
+namespace Internal.TypeSystem
+{
+ // Extension to TargetDetails related to Aot
+ partial class TargetDetails
+ {
+ ///
+ /// Offset by which fat function pointers are shifted to distinguish them
+ /// from real function pointers.
+ /// WebAssembly uses index tables, not addresses for function pointers, so the lower bits are not free to use.
+ ///
+ public int FatFunctionPointerOffset => Architecture == TargetArchitecture.Wasm32 ? 1 << 31 : 2;
+ }
+}
diff --git a/src/Common/src/TypeSystem/IL/ILImporter.cs b/src/Common/src/TypeSystem/IL/ILImporter.cs
index 0f83634f047..0461df638df 100644
--- a/src/Common/src/TypeSystem/IL/ILImporter.cs
+++ b/src/Common/src/TypeSystem/IL/ILImporter.cs
@@ -575,7 +575,7 @@ private void ImportBasicBlock(BasicBlock basicBlock)
ImportConvert(WellKnownType.UInt32, false, false);
break;
case ILOpcode.conv_u8:
- ImportConvert(WellKnownType.UInt64, false, false);
+ ImportConvert(WellKnownType.UInt64, false, true);
break;
case ILOpcode.callvirt:
ImportCall(opCode, ReadILToken());
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
index 3a1a0b131cd..cbf320f177a 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
@@ -16,7 +16,7 @@ namespace ILCompiler.DependencyAnalysis
/// they are used by the dynamic type loader when dynamically instantiating types at runtime.
/// The data that we emit on canonical type instantiations should just be the minimum that is needed by the template
/// type loader.
- /// Similarly, the dependencies that we track for canonicl type instantiations are minimal, and are just the ones used
+ /// Similarly, the dependencies that we track for canonical type instantiations are minimal, and are just the ones used
/// by the dynamic type loader
///
public sealed class CanonicalEETypeNode : EETypeNode
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs
index c7c68651843..f93a0e9ea68 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs
@@ -6,7 +6,6 @@
using Internal.TypeSystem;
using Debug = System.Diagnostics.Debug;
-using FatFunctionPointerConstants = Internal.Runtime.FatFunctionPointerConstants;
namespace ILCompiler.DependencyAnalysis
{
@@ -37,7 +36,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
}
int ISymbolDefinitionNode.Offset => 0;
- int ISymbolNode.Offset => FatFunctionPointerConstants.Offset;
+ int ISymbolNode.Offset => Method.Context.Target.FatFunctionPointerOffset;
public override bool IsShareable => true;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs
index af6d1df59a5..b6d2aee0d85 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs
@@ -9,7 +9,6 @@
using Internal.NativeFormat;
using InvokeTableFlags = Internal.Runtime.InvokeTableFlags;
-using FatFunctionPointerConstants = Internal.Runtime.FatFunctionPointerConstants;
namespace ILCompiler.DependencyAnalysis
{
diff --git a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
index 521ada40541..f6764ba5ae1 100644
--- a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
+++ b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
@@ -17,7 +17,6 @@
using Internal.TypeSystem.Ecma;
using Debug = System.Diagnostics.Debug;
-using FatFunctionPointerConstants = Internal.Runtime.FatFunctionPointerConstants;
namespace ILCompiler.CppCodeGen
{
@@ -2102,7 +2101,7 @@ private string GetCodeForReadyToRunGenericHelper(ReadyToRunGenericHelperNode nod
sb.Append(" = ((intptr_t)");
sb.Append(resVarName);
sb.Append(") + ");
- sb.Append(FatFunctionPointerConstants.Offset.ToString());
+ sb.Append(constructor.Context.Target.FatFunctionPointerOffset.ToString());
sb.Append(";");
sb.AppendLine();
@@ -2290,7 +2289,7 @@ private void OutputStaticsCode(NodeFactory factory, Dictionary 0)
@@ -2116,13 +2114,13 @@ private void ImportCalli(int token)
Append("if (");
Append(fatPtr);
Append(" & ");
- Append(FatFunctionPointerConstants.Offset.ToString());
+ Append(_typeSystemContext.Target.FatFunctionPointerOffset.ToString());
Append(") {");
Append(fnPtrValue);
Append(" = *(intptr_t*)(");
Append(fatPtr);
Append(" - ");
- Append(FatFunctionPointerConstants.Offset.ToString());
+ Append(_typeSystemContext.Target.FatFunctionPointerOffset.ToString());
Append(")");
AppendSemicolon();
@@ -2155,7 +2153,7 @@ private void ImportCalli(int token)
Append("**(void***)(");
Append(fatPtr);
Append(" - ");
- Append(FatFunctionPointerConstants.Offset.ToString());
+ Append(_typeSystemContext.Target.FatFunctionPointerOffset.ToString());
Append(" + sizeof(void*))");
if (methodSignature.Length > 0)
@@ -2309,14 +2307,14 @@ private void ImportLdFtn(int token, ILOpcode opCode)
Append("(");
Append(GetGenericContext());
Append(")) + ");
- Append(FatFunctionPointerConstants.Offset.ToString());
+ Append(_typeSystemContext.Target.FatFunctionPointerOffset.ToString());
}
else
{
Append("((intptr_t)");
AppendFatFunctionPointer(runtimeDeterminedMethod);
Append("()) + ");
- Append(FatFunctionPointerConstants.Offset.ToString());
+ Append(_typeSystemContext.Target.FatFunctionPointerOffset.ToString());
}
}
else
diff --git a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
index 15d169e95e2..00d9343ca9f 100644
--- a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
+++ b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
@@ -79,6 +79,9 @@
TypeSystem\Canon\TypeSystemContext.Canon.cs
+
+ TypeSystem\Aot\TargetDetails.Aot.cs
+
TypeSystem\CodeGen\FieldDesc.CodeGen.cs
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs
index 2f1dbd15dea..270482ae171 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs
@@ -403,6 +403,20 @@ public override StackEntry Duplicate(LLVMBuilderRef builder)
}
}
+ ///
+ /// Entry representing the generic return value of a function.
+ ///
+ internal class GenericReturnExpressionEntry : ExpressionEntry
+ {
+ public GenericReturnExpressionEntry(TypeDesc genericReturnTypeDesc, StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null)
+ : base(kind, name, llvmValue, type)
+ {
+ GenericReturnTypeDesc = genericReturnTypeDesc;
+ }
+
+ public TypeDesc GenericReturnTypeDesc { get; }
+ }
+
///
/// Entry representing some expression
///
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
index ef16bc5b4e3..fc1d11032e8 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
@@ -12,7 +12,6 @@
using LLVMSharp;
using ILCompiler.CodeGen;
using ILCompiler.DependencyAnalysis;
-using ILCompiler.DependencyAnalysisFramework;
using ILCompiler.WebAssembly;
using Internal.IL.Stubs;
using Internal.TypeSystem.Ecma;
@@ -42,12 +41,14 @@ public IEnumerable GetDependencies()
private static MetadataFieldLayoutAlgorithm LayoutAlgorithm { get; } = new MetadataFieldLayoutAlgorithm();
private readonly MethodDesc _method;
private readonly MethodIL _methodIL;
+ private readonly MethodIL _canonMethodIL;
private readonly MethodSignature _signature;
private readonly TypeDesc _thisType;
private readonly WebAssemblyCodegenCompilation _compilation;
private readonly string _mangledName;
private LLVMValueRef _llvmFunction;
private LLVMValueRef _currentFunclet;
+ private bool _isUnboxingThunk;
private LLVMBasicBlockRef _curBasicBlock;
private LLVMBuilderRef _builder;
private readonly LocalVariableDefinition[] _locals;
@@ -59,13 +60,14 @@ public IEnumerable GetDependencies()
private MethodDebugInformation _debugInformation;
private LLVMMetadataRef _debugFunction;
private TypeDesc _constrainedType = null;
+
List _exceptionFunclets;
///
/// Stack of values pushed onto the IL stack: locals, arguments, values, function pointer, ...
///
private EvaluationStack _stack = new EvaluationStack(0);
-
+
private class BasicBlock
{
// Common fields
@@ -87,6 +89,8 @@ public enum ImportState : byte
public bool HandlerStart;
public LLVMBasicBlockRef Block;
+ public LLVMBasicBlockRef LastInternalIf;
+ public LLVMBasicBlockRef LastBlock => LastInternalIf.Pointer == IntPtr.Zero ? Block : LastInternalIf;
}
private class ExceptionRegion
@@ -94,13 +98,12 @@ private class ExceptionRegion
public ILExceptionRegion ILRegion;
}
private ExceptionRegion[] _exceptionRegions;
-
- public ILImporter(WebAssemblyCodegenCompilation compilation, MethodDesc method, MethodIL methodIL, string mangledName)
+ public ILImporter(WebAssemblyCodegenCompilation compilation, MethodDesc method, MethodIL methodIL, string mangledName, bool isUnboxingThunk)
{
Module = compilation.Module;
_compilation = compilation;
_method = method;
-
+ _isUnboxingThunk = isUnboxingThunk;
// stubs for Unix calls which are not available to this target yet
if ((method.OwningType as EcmaType)?.Name == "Interop" && method.Name == "GetRandomBytes")
{
@@ -113,7 +116,20 @@ public ILImporter(WebAssemblyCodegenCompilation compilation, MethodDesc method,
methodIL = new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ldc_i4_0, (byte)ILOpcode.ret }, Array.Empty(), null);
}
- _methodIL = methodIL;
+ _canonMethodIL = methodIL;
+ // Get the runtime determined method IL so that this works right in shared code
+ // and tokens in shared code resolve to runtime determined types.
+ MethodIL uninstantiatiedMethodIL = methodIL.GetMethodILDefinition();
+ if (methodIL != uninstantiatiedMethodIL)
+ {
+ MethodDesc sharedMethod = method.GetSharedRuntimeFormMethodTarget();
+ _methodIL = new InstantiatedMethodIL(sharedMethod, uninstantiatiedMethodIL);
+ }
+ else
+ {
+ _methodIL = methodIL;
+ }
+
_mangledName = mangledName;
_ilBytes = methodIL.GetILBytes();
_locals = methodIL.GetLocals();
@@ -121,7 +137,6 @@ public ILImporter(WebAssemblyCodegenCompilation compilation, MethodDesc method,
_argSlots = new LLVMValueRef[method.Signature.Length];
_signature = method.Signature;
_thisType = method.OwningType;
-
var ilExceptionRegions = methodIL.GetExceptionRegions();
_exceptionRegions = new ExceptionRegion[ilExceptionRegions.Length];
_exceptionFunclets = new List(_exceptionRegions.Length);
@@ -130,7 +145,8 @@ public ILImporter(WebAssemblyCodegenCompilation compilation, MethodDesc method,
{
_exceptionRegions[curRegion++] = new ExceptionRegion() { ILRegion = region };
}
- _llvmFunction = GetOrCreateLLVMFunction(mangledName, method.Signature);
+
+ _llvmFunction = GetOrCreateLLVMFunction(mangledName, method.Signature, method.RequiresInstArg());
_currentFunclet = _llvmFunction;
_builder = LLVM.CreateBuilder();
_pointerSize = compilation.NodeFactory.Target.PointerSize;
@@ -153,7 +169,7 @@ public void Import()
catch
{
LLVMBasicBlockRef trapBlock = LLVM.AppendBasicBlock(_llvmFunction, "Trap");
-
+
// Change the function body to trap
foreach (BasicBlock block in _basicBlocks)
{
@@ -210,6 +226,10 @@ private void GenerateProlog()
{
signatureIndex++;
}
+ if (_method.RequiresInstArg()) // hidden param after shadow stack pointer and return slot if present
+ {
+ signatureIndex++;
+ }
IList argNames = null;
if (_debugInformation != null)
@@ -238,13 +258,12 @@ private void GenerateProlog()
argName += $"arg{argOffset}_";
storageAddr = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_signature[i]), argName);
- _argSlots[i] = storageAddr;
+ _argSlots[i] = storageAddr;
}
else
{
storageAddr = CastIfNecessary(LoadVarAddress(argOffset, LocalVarKind.Argument, out _), LLVM.PointerType(LLVM.TypeOf(argValue), 0));
}
-
LLVM.BuildStore(_builder, argValue, storageAddr);
signatureIndex++;
}
@@ -283,10 +302,10 @@ private void GenerateProlog()
if (_methodIL.IsInitLocals)
{
- for(int i = 0; i < _locals.Length; i++)
+ for (int i = 0; i < _locals.Length; i++)
{
LLVMValueRef localAddr = LoadVarAddress(i, LocalVarKind.Local, out TypeDesc localType);
- if(CanStoreVariableOnStack(localType))
+ if (CanStoreVariableOnStack(localType))
{
LLVMTypeRef llvmType = GetLLVMTypeForTypeDesc(localType);
LLVMTypeKind typeKind = LLVM.GetTypeKind(llvmType);
@@ -337,8 +356,8 @@ private void GenerateProlog()
}
}
- if (_thisType is MetadataType metadataType && !metadataType.IsBeforeFieldInit
- && (!_method.IsStaticConstructor && _method.Signature.IsStatic || _method.IsConstructor || (_thisType.IsValueType && !_method.Signature.IsStatic))
+ if (_thisType is MetadataType metadataType && !metadataType.IsBeforeFieldInit
+ && (!_method.IsStaticConstructor && _method.Signature.IsStatic || _method.IsConstructor || (_thisType.IsValueType && !_method.Signature.IsStatic))
&& _compilation.TypeSystemContext.HasLazyStaticConstructor(metadataType))
{
TriggerCctor(metadataType);
@@ -348,18 +367,18 @@ private void GenerateProlog()
LLVM.BuildBr(_builder, block0);
}
- private LLVMValueRef CreateLLVMFunction(string mangledName, MethodSignature signature)
+ private LLVMValueRef CreateLLVMFunction(string mangledName, MethodSignature signature, bool hasHiddenParameter)
{
- return LLVM.AddFunction(Module, mangledName, GetLLVMSignatureForMethod(signature));
+ return LLVM.AddFunction(Module, mangledName, GetLLVMSignatureForMethod(signature, hasHiddenParameter));
}
- private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, MethodSignature signature)
+ private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, MethodSignature signature, bool hasHiddenParam)
{
LLVMValueRef llvmFunction = LLVM.GetNamedFunction(Module, mangledName);
- if(llvmFunction.Pointer == IntPtr.Zero)
+ if (llvmFunction.Pointer == IntPtr.Zero)
{
- return CreateLLVMFunction(mangledName, signature);
+ return CreateLLVMFunction(mangledName, signature, hasHiddenParam);
}
return llvmFunction;
}
@@ -370,6 +389,7 @@ private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, LLVMTypeRef fun
if (llvmFunction.Pointer == IntPtr.Zero)
{
+
return LLVM.AddFunction(Module, mangledName, functionType);
}
return llvmFunction;
@@ -399,7 +419,7 @@ private void ImportCallMemset(LLVMValueRef targetPointer, byte value, int length
ImportCallMemset(targetPointer, value, objectSizeValue);
}
- private void ImportCallMemset (LLVMValueRef targetPointer, byte value, LLVMValueRef length)
+ private void ImportCallMemset(LLVMValueRef targetPointer, byte value, LLVMValueRef length)
{
var memsetSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.Int8Type(), LLVM.Int32Type(), LLVM.Int32Type(), LLVM.Int1Type() }, false);
LLVM.BuildCall(_builder, GetOrCreateLLVMFunction("llvm.memset.p0i8.i32", memsetSignature), new LLVMValueRef[] { targetPointer, BuildConstInt8(value), length, BuildConstInt32(1), BuildConstInt1(0) }, String.Empty);
@@ -530,7 +550,7 @@ private void StartImportingBasicBlock(BasicBlock basicBlock)
private void EndImportingBasicBlock(BasicBlock basicBlock)
{
- var terminator = basicBlock.Block.GetBasicBlockTerminator();
+ var terminator = basicBlock.LastBlock.GetBasicBlockTerminator();
if (terminator.Pointer == IntPtr.Zero)
{
if (_basicBlocks.Length > _currentOffset)
@@ -538,6 +558,7 @@ private void EndImportingBasicBlock(BasicBlock basicBlock)
if (_basicBlocks[_currentOffset].StartOffset == 0)
throw new InvalidProgramException();
MarkBasicBlock(_basicBlocks[_currentOffset]);
+
LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(_basicBlocks[_currentOffset]));
}
}
@@ -637,7 +658,7 @@ private void ImportLoadVar(int index, bool argument)
private LLVMValueRef LoadTemp(int index)
{
LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type);
- return LLVM.BuildLoad(_builder, CastToPointerToTypeDesc(address, type, $"Temp{index}_"), $"LdTemp{index}_");
+ return LLVM.BuildLoad(_builder, CastToPointerToTypeDesc(address, type, $"Temp{index}_"), $"LdTemp{index}_");
}
internal LLVMValueRef LoadTemp(int index, LLVMTypeRef asType)
@@ -646,10 +667,11 @@ internal LLVMValueRef LoadTemp(int index, LLVMTypeRef asType)
return LLVM.BuildLoad(_builder, CastIfNecessary(address, LLVM.PointerType(asType, 0), $"Temp{index}_"), $"LdTemp{index}_");
}
- private void StoreTemp(int index, LLVMValueRef value, string name = null)
+ private LLVMValueRef StoreTemp(int index, LLVMValueRef value, string name = null)
{
LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type);
LLVM.BuildStore(_builder, CastToTypeDesc(value, type, name), CastToPointerToTypeDesc(address, type, $"Temp{index}_"));
+ return address;
}
internal static LLVMValueRef LoadValue(LLVMBuilderRef builder, LLVMValueRef address, TypeDesc sourceType, LLVMTypeRef targetType, bool signExtend, string loadName = null)
@@ -760,7 +782,7 @@ private LLVMValueRef LoadVarAddress(int index, LocalVarKind kind, out TypeDesc t
// If the argument can be passed as a real argument rather than on the shadow stack,
// get its address here
- if(realArgIndex != -1)
+ if (realArgIndex != -1)
{
return _argSlots[realArgIndex];
}
@@ -771,7 +793,7 @@ private LLVMValueRef LoadVarAddress(int index, LocalVarKind kind, out TypeDesc t
GetLocalSizeAndOffsetAtIndex(index, out int localSize, out varOffset);
valueType = GetLLVMTypeForTypeDesc(_locals[index].Type);
type = _locals[index].Type;
- if(varOffset == -1)
+ if (varOffset == -1)
{
Debug.Assert(_localSlots[index].Pointer != IntPtr.Zero);
return _localSlots[index];
@@ -871,6 +893,11 @@ private LLVMValueRef CastToPointerToTypeDesc(LLVMValueRef source, TypeDesc type,
private void CastingStore(LLVMValueRef address, StackEntry value, TypeDesc targetType, string targetName = null)
{
+ if (value is GenericReturnExpressionEntry)
+ {
+ targetType = ((GenericReturnExpressionEntry)value).GenericReturnTypeDesc;
+ }
+
var typedStoreLocation = CastToPointerToTypeDesc(address, targetType, targetName);
LLVM.BuildStore(_builder, value.ValueAsType(targetType, _builder), typedStoreLocation);
}
@@ -944,7 +971,7 @@ internal static LLVMValueRef CastIfNecessary(LLVMBuilderRef builder, LLVMValueRe
//TODO: keep track of the TypeDesc so we can call BuildUIToFP when the integer is unsigned
typedToStore = LLVM.BuildSIToFP(builder, source, valueType, "CastSIToFloat" + (name ?? ""));
}
- else if ((toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind || toStoreKind == LLVMTypeKind.LLVMFloatTypeKind) &&
+ else if ((toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind || toStoreKind == LLVMTypeKind.LLVMFloatTypeKind) &&
valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind)
{
//TODO: keep track of the TypeDesc so we can call BuildFPToUI when the integer is unsigned
@@ -1062,7 +1089,7 @@ internal static LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type)
FieldDesc[] instanceFields = type.GetFields().Where(field => !field.IsStatic).ToArray();
FieldAndOffset[] fieldLayout = new FieldAndOffset[instanceFields.Length];
- for(int i = 0; i < instanceFields.Length; i++)
+ for (int i = 0; i < instanceFields.Length; i++)
{
fieldLayout[i] = new FieldAndOffset(instanceFields[i], instanceFields[i].Offset);
}
@@ -1087,7 +1114,7 @@ internal static LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type)
Debug.Assert(curOffset > lastOffset);
int prevElementSize;
- if(prevType == null)
+ if (prevType == null)
{
lastOffset = 0;
prevElementSize = 0;
@@ -1129,7 +1156,7 @@ internal static LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type)
LlvmStructs[type] = llvmStructType;
}
- return llvmStructType;
+ return llvmStructType;
}
case TypeFlags.Enum:
@@ -1155,7 +1182,7 @@ private static bool StructIsWrappedPrimitive(TypeDesc type, TypeDesc primitiveTy
Debug.Assert(type.IsValueType);
Debug.Assert(primitiveType.IsPrimitive);
- if(type.GetElementSize().AsInt != primitiveType.GetElementSize().AsInt)
+ if (type.GetElementSize().AsInt != primitiveType.GetElementSize().AsInt)
{
return false;
}
@@ -1166,7 +1193,7 @@ private static bool StructIsWrappedPrimitive(TypeDesc type, TypeDesc primitiveTy
foreach (FieldDesc field in fields)
{
- if(field.IsStatic)
+ if (field.IsStatic)
{
continue;
}
@@ -1190,7 +1217,7 @@ private static bool StructIsWrappedPrimitive(TypeDesc type, TypeDesc primitiveTy
}
}
- if(instanceFieldCount == 1 && foundPrimitive)
+ if (instanceFieldCount == 1 && foundPrimitive)
{
return true;
}
@@ -1470,8 +1497,8 @@ private void ImportJmp(int token)
private void ImportCasting(ILOpcode opcode, int token)
{
- TypeDesc type = ResolveTypeToken(token);
-
+ TypeDesc type = (TypeDesc)_methodIL.GetObject(token);
+
//TODO: call GetCastingHelperNameForType from JitHelper.cs (needs refactoring)
string function;
bool throwing = opcode == ILOpcode.castclass;
@@ -1482,15 +1509,40 @@ private void ImportCasting(ILOpcode opcode, int token)
else
function = throwing ? "CheckCastClass" : "IsInstanceOfClass";
- var arguments = new StackEntry[]
+ StackEntry[] arguments;
+ if (type.IsRuntimeDeterminedSubtype)
{
- new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(type, true), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr")),
- _stack.Pop()
- };
+ //TODO refactor argument creation with else below
+ arguments = new StackEntry[]
+ {
+ new ExpressionEntry(StackValueKind.ValueType, "eeType", CallGenericHelper(ReadyToRunHelperId.TypeHandle, type),
+ GetEETypePtrTypeDesc()),
+ _stack.Pop()
+ };
+ }
+ else
+ {
+ arguments = new StackEntry[]
+ {
+ new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(type, true),
+ GetEETypePtrTypeDesc()),
+ _stack.Pop()
+ };
+ }
_stack.Push(CallRuntime(_compilation.TypeSystemContext, TypeCast, function, arguments, GetWellKnownType(WellKnownType.Object)));
}
+ LLVMValueRef CallGenericHelper(ReadyToRunHelperId helperId, object helperArg)
+ {
+ _dependencies.Add(GetGenericLookupHelperAndAddReference(helperId, helperArg, out LLVMValueRef helper));
+ return LLVM.BuildCall(_builder, helper, new LLVMValueRef[]
+ {
+ GetShadowStack(),
+ GetGenericContext()
+ }, "getHelper");
+ }
+
private void ImportLoadNull()
{
_stack.Push(new ExpressionEntry(StackValueKind.ObjRef, "null", LLVM.ConstInt(LLVM.Int32Type(), 0, LLVMMisc.False)));
@@ -1510,7 +1562,8 @@ private void ImportReturn()
if (NeedsReturnStackSlot(_signature))
{
- ImportStoreHelper(castValue, valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0);
+ var retParam = LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction));
+ ImportStoreHelper(castValue, valueType, retParam, 0);
LLVM.BuildRetVoid(_builder);
}
else
@@ -1521,10 +1574,11 @@ private void ImportReturn()
private void ImportCall(ILOpcode opcode, int token)
{
- MethodDesc callee = (MethodDesc)_methodIL.GetObject(token);
+ MethodDesc runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token);
+ MethodDesc callee = (MethodDesc)_canonMethodIL.GetObject(token);
if (callee.IsIntrinsic)
{
- if (ImportIntrinsicCall(callee))
+ if (ImportIntrinsicCall(callee, runtimeDeterminedMethod))
{
return;
}
@@ -1554,13 +1608,22 @@ private void ImportCall(ILOpcode opcode, int token)
}
var arguments = new StackEntry[]
{
- new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(newType, true), eeTypeDesc),
+ null,
new Int32ConstantEntry(paramCnt),
new AddressExpressionEntry(StackValueKind.ValueType, "newobj_array_pdims", dimensions)
};
+ if (!runtimeDeterminedMethod.OwningType.IsRuntimeDeterminedSubtype)
+ {
+ arguments[0] = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(newType, true), eeTypeDesc);
+ }
+ else
+ {
+ var typeRef = CallGenericHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType);
+ arguments[0] = new ExpressionEntry(StackValueKind.ValueType, "eeType", typeRef, eeTypeDesc);
+ }
MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime.CompilerHelpers", "ArrayHelpers");
MethodDesc helperMethod = helperType.GetKnownMethod("NewObjArray", null);
- PushNonNull(HandleCall(helperMethod, helperMethod.Signature, arguments, forcedReturnType: newType));
+ PushNonNull(HandleCall(helperMethod, helperMethod.Signature, helperMethod, arguments, runtimeDeterminedMethod, forcedReturnType: newType));
return;
}
else if (newType.IsString)
@@ -1573,10 +1636,9 @@ private void ImportCall(ILOpcode opcode, int token)
}
else
{
- if (callee.Signature.Length > _stack.Length) //System.Reflection.MemberFilter.ctor
+ if (callee.Signature.Length > _stack.Length)
throw new InvalidProgramException();
- StackEntry newObjResult;
if (newType.IsValueType)
{
// Allocate a slot on the shadow stack for the value type
@@ -1593,7 +1655,23 @@ private void ImportCall(ILOpcode opcode, int token)
}
else
{
- newObjResult = AllocateObject(callee.OwningType);
+ StackEntry newObjResult;
+ TypeDesc typeToAlloc;
+ var runtimeDeterminedRetType = runtimeDeterminedMethod.OwningType;
+
+ var eeTypePtrTypeDesc = GetEETypePtrTypeDesc();
+ if (runtimeDeterminedRetType.IsRuntimeDeterminedSubtype)
+ {
+ typeToAlloc = _compilation.ConvertToCanonFormIfNecessary(runtimeDeterminedRetType, CanonicalFormKind.Specific);
+ var typeRef = CallGenericHelper(ReadyToRunHelperId.TypeHandle, typeToAlloc);
+ newObjResult = AllocateObject(new ExpressionEntry(StackValueKind.ValueType, "eeType", typeRef, eeTypePtrTypeDesc));
+ }
+ else
+ {
+ typeToAlloc = callee.OwningType;
+ MetadataType metadataType = (MetadataType)typeToAlloc;
+ newObjResult = AllocateObject(new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(metadataType, true), eeTypePtrTypeDesc), typeToAlloc);
+ }
//one for the real result and one to be consumed by ctor
_stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length);
@@ -1601,45 +1679,138 @@ private void ImportCall(ILOpcode opcode, int token)
}
}
}
- else
- {
- // !newobj
- if (opcode == ILOpcode.callvirt && localConstrainedType != null)
- {
- if (localConstrainedType.IsRuntimeDeterminedSubtype)
- localConstrainedType = localConstrainedType.ConvertToCanonForm(CanonicalFormKind.Specific);
- }
- }
if (opcode == ILOpcode.newobj && callee.OwningType.IsDelegate)
{
FunctionPointerEntry functionPointer = ((FunctionPointerEntry)_stack.Peek());
- DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(callee.OwningType, functionPointer.Method, functionPointer.IsVirtual);
+ TypeDesc canonDelegateType = callee.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific);
+ DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(canonDelegateType, functionPointer.Method, followVirtualDispatch: false);
+ MethodDesc delegateTargetMethod = delegateInfo.TargetMethod;
callee = delegateInfo.Constructor.Method;
- if (callee.Signature.Length == 3)
+ if (delegateInfo.NeedsRuntimeLookup && !functionPointer.IsVirtual)
+ {
+ LLVMValueRef helper;
+ List additionalTypes = new List();
+ var shadowStack = GetShadowStack();
+ if (delegateInfo.Thunk != null)
+ {
+ MethodDesc thunkMethod = delegateInfo.Thunk.Method;
+ AddMethodReference(thunkMethod);
+ PushExpression(StackValueKind.NativeInt, "invokeThunk",
+ GetOrCreateLLVMFunction(
+ _compilation.NameMangler.GetMangledMethodName(thunkMethod).ToString(),
+ thunkMethod.Signature,
+ false));
+ }
+ var sigLength = callee.Signature.Length;
+ var stackCopy = new StackEntry[sigLength];
+ for (var i = 0; i < sigLength; i++)
+ {
+ stackCopy[i] = _stack.Pop();
+ }
+ var thisEntry = _stack.Pop(); // the extra newObjResult which we dont want as we are not going through HandleCall
+ // by convention(?) the delegate initialize methods take this as the first parameter which is not in the ctor
+ // method sig, so add that here
+ int curOffset = 0;
+
+ // pass this (delegate obj) as first param
+ LLVMTypeRef llvmTypeRefForThis = GetLLVMTypeForTypeDesc(thisEntry.Type);
+ curOffset = PadOffset(thisEntry.Type, curOffset);
+ LLVMValueRef thisAddr = LLVM.BuildGEP(_builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "thisLoc");
+ LLVMValueRef llvmValueRefForThis = thisEntry.ValueAsType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), _builder);
+ LLVM.BuildStore(_builder, llvmValueRefForThis, CastIfNecessary(_builder, thisAddr, LLVM.PointerType(llvmTypeRefForThis, 0), "thisCast"));
+ curOffset = PadNextOffset(GetWellKnownType(WellKnownType.Object), curOffset);
+
+ List helperParams = new List
+ {
+ shadowStack,
+ GetGenericContext()
+ };
+
+ for (var i = 0; i < sigLength; i++)
+ {
+ TypeDesc argTypeDesc = callee.Signature[i];
+ LLVMTypeRef llvmTypeRefForArg = GetLLVMTypeForTypeDesc(argTypeDesc);
+ StackEntry argStackEntry = stackCopy[sigLength - i - 1];
+ if (CanStoreTypeOnStack(callee.Signature[i]))
+ {
+ LLVMValueRef llvmValueRefForArg = argStackEntry.ValueAsType(llvmTypeRefForArg, _builder);
+ additionalTypes.Add(llvmTypeRefForArg);
+ helperParams.Add(llvmValueRefForArg);
+ }
+ else
+ {
+ LLVMValueRef llvmValueRefForArg = argStackEntry.ValueAsType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), _builder);
+ curOffset = PadOffset(argTypeDesc, curOffset);
+ LLVMValueRef argAddr = LLVM.BuildGEP(_builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "arg" + i);
+ LLVM.BuildStore(_builder, llvmValueRefForArg, CastIfNecessary(_builder, argAddr, LLVM.PointerType(llvmTypeRefForArg, 0), $"parameter{i}_"));
+ curOffset = PadNextOffset(argTypeDesc, curOffset);
+ }
+ }
+
+ var node = GetGenericLookupHelperAndAddReference(ReadyToRunHelperId.DelegateCtor, delegateInfo, out helper,
+ additionalTypes);
+ LLVM.BuildCall(_builder, helper, helperParams.ToArray(), string.Empty);
+ return;
+ }
+ if (!functionPointer.IsVirtual && delegateTargetMethod.OwningType.IsValueType &&
+ !delegateTargetMethod.Signature.IsStatic)
+ {
+ _stack.Pop(); // remove the target
+
+ MethodDesc canonDelegateTargetMethod = delegateTargetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
+ ISymbolNode targetNode = delegateInfo.GetTargetNode(_compilation.NodeFactory);
+ _dependencies.Add(targetNode);
+ if (delegateTargetMethod != canonDelegateTargetMethod)
+ {
+ var funcRef = LoadAddressOfSymbolNode(targetNode);
+ var toInt = LLVM.BuildPtrToInt(_builder, funcRef, LLVMTypeRef.Int32Type(), "toInt");
+ var withOffset = LLVM.BuildOr(_builder, toInt, BuildConstUInt32((uint)_compilation.TypeSystemContext.Target.FatFunctionPointerOffset), "withOffset");
+ PushExpression(StackValueKind.NativeInt, "fatthunk", withOffset);
+ }
+ else
+ {
+ PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(targetNode.GetMangledName(_compilation.NodeFactory.NameMangler), delegateTargetMethod.Signature, false));
+ }
+ }
+ else if (callee.Signature.Length == 3)
{
- PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString(), delegateInfo.Thunk.Method.Signature));
+ // These are the invoke thunks e.g. {[S.P.CoreLib]System.Func`1.InvokeOpenStaticThunk()} that are passed to e.g. {[S.P.CoreLib]System.Delegate.InitializeOpenStaticThunk(object,native int,native int)}
+ // only push this if there is the third argument, i.e. not {[S.P.CoreLib]System.Delegate.InitializeClosedInstance(object,native int)}
+ PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString(), delegateInfo.Thunk.Method.Signature, false));
}
}
- HandleCall(callee, callee.Signature, opcode, localConstrainedType);
+ HandleCall(callee, callee.Signature, runtimeDeterminedMethod, opcode, localConstrainedType);
}
- private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPointer, bool isCallVirt, TypeDesc constrainedType)
+ private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, MethodDesc canonMethod, StackEntry thisPointer, bool isCallVirt,
+ TypeDesc constrainedType, MethodDesc runtimeDeterminedMethod, out bool hasHiddenParam,
+ out LLVMValueRef dictPtrPtrStore,
+ out LLVMValueRef fatFunctionPtr)
{
- string calleeName = _compilation.NameMangler.GetMangledMethodName(callee).ToString();
+ hasHiddenParam = false;
+ dictPtrPtrStore = default(LLVMValueRef);
+ fatFunctionPtr = default(LLVMValueRef);
- // Sealed methods must not be called virtually due to sealed vTables, so call them directly
- if(callee.IsFinal || callee.OwningType.IsSealed())
+ string canonMethodName = _compilation.NameMangler.GetMangledMethodName(canonMethod).ToString();
+ TypeDesc owningType = callee.OwningType;
+ bool delegateInvoke = owningType.IsDelegate && callee.Name == "Invoke";
+ // Sealed methods must not be called virtually due to sealed vTables, so call them directly, but not delegate Invoke
+ if ((canonMethod.IsFinal || canonMethod.OwningType.IsSealed()) && !delegateInvoke)
{
- AddMethodReference(callee);
- return GetOrCreateLLVMFunction(calleeName, callee.Signature);
+ if (!_compilation.NodeFactory.TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(canonMethod))
+ {
+ hasHiddenParam = canonMethod.RequiresInstArg();
+ }
+ AddMethodReference(canonMethod);
+ return GetOrCreateLLVMFunction(canonMethodName, canonMethod.Signature, hasHiddenParam);
}
- if (thisPointer != null && callee.IsVirtual && isCallVirt)
+ if (canonMethod.IsVirtual && isCallVirt)
{
// TODO: Full resolution of virtual methods
- if (!callee.IsNewSlot)
+ if (!canonMethod.IsNewSlot)
throw new NotImplementedException();
bool isValueTypeCall = false;
@@ -1657,7 +1828,7 @@ private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPoi
}
}
- if(constrainedType != null && constrainedType.IsValueType)
+ if (constrainedType != null && constrainedType.IsValueType)
{
isValueTypeCall = true;
}
@@ -1666,56 +1837,85 @@ private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPoi
{
if (constrainedType != null)
{
- targetMethod = constrainedType.TryResolveConstraintMethodApprox(callee.OwningType, callee, out _);
+ if (constrainedType.IsRuntimeDeterminedType)
+ {
+ constrainedType = constrainedType.ConvertToCanonForm(CanonicalFormKind.Specific);
+ }
+ targetMethod = constrainedType.TryResolveConstraintMethodApprox(canonMethod.OwningType, canonMethod, out _);
}
- else if (callee.OwningType.IsInterface)
+ else if (canonMethod.OwningType.IsInterface)
{
- targetMethod = parameterType.ResolveInterfaceMethodTarget(callee);
+ targetMethod = parameterType.ResolveInterfaceMethodTarget(canonMethod);
}
else
{
- targetMethod = parameterType.FindVirtualFunctionTargetMethodOnObjectType(callee);
+ targetMethod = parameterType.FindVirtualFunctionTargetMethodOnObjectType(canonMethod);
}
}
+ hasHiddenParam = callee.RequiresInstArg();
if (targetMethod != null)
{
AddMethodReference(targetMethod);
- return GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(targetMethod).ToString(), callee.Signature);
+ return GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(targetMethod).ToString(), canonMethod.Signature, hasHiddenParam);
+ }
+ if (canonMethod.HasInstantiation && !canonMethod.IsFinal && !canonMethod.OwningType.IsSealed())
+ {
+ return GetCallableGenericVirtualMethod(thisPointer, canonMethod, callee, runtimeDeterminedMethod, out dictPtrPtrStore, out fatFunctionPtr);
}
+ return GetCallableVirtualMethod(thisPointer, callee, runtimeDeterminedMethod);
+ }
+
+ hasHiddenParam = canonMethod.RequiresInstArg();
+ AddMethodReference(canonMethod);
+ return GetOrCreateLLVMFunction(canonMethodName, canonMethod.Signature, hasHiddenParam);
+ }
- return GetCallableVirtualMethod(thisPointer, callee);
+ private ISymbolNode GetMethodGenericDictionaryNode(MethodDesc method)
+ {
+ ISymbolNode node = _compilation.NodeFactory.MethodGenericDictionary(method);
+ _dependencies.Add(node);
- }
- else
- {
- AddMethodReference(callee);
- return GetOrCreateLLVMFunction(calleeName, callee.Signature);
- }
+ return node;
}
- private LLVMValueRef GetOrCreateMethodSlot(MethodDesc method)
+ private LLVMValueRef GetOrCreateMethodSlot(MethodDesc canonMethod, MethodDesc callee)
{
- var vtableSlotSymbol = _compilation.NodeFactory.VTableSlot(method);
+ var vtableSlotSymbol = _compilation.NodeFactory.VTableSlot(callee);
_dependencies.Add(vtableSlotSymbol);
LLVMValueRef slot = LoadAddressOfSymbolNode(vtableSlotSymbol);
- return LLVM.BuildLoad(_builder, slot, $"{method.Name}_slot");
+ return LLVM.BuildLoad(_builder, slot, $"{callee.Name}_slot");
}
- private LLVMValueRef GetCallableVirtualMethod(StackEntry objectPtr, MethodDesc method)
+ private LLVMValueRef GetCallableVirtualMethod(StackEntry objectPtr, MethodDesc callee, MethodDesc runtimeDeterminedMethod)
{
- Debug.Assert(method.IsVirtual);
- LLVMValueRef slot = GetOrCreateMethodSlot(method);
- var pointerSize = method.Context.Target.PointerSize;
- LLVMTypeRef llvmSignature = GetLLVMSignatureForMethod(method.Signature);
+ Debug.Assert(runtimeDeterminedMethod.IsVirtual);
+
+ LLVMValueRef slot = GetOrCreateMethodSlot(runtimeDeterminedMethod, callee);
+
+ LLVMTypeRef llvmSignature = GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, false);
LLVMValueRef functionPtr;
var thisPointer = objectPtr.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder);
ThrowIfNull(thisPointer);
- if (method.OwningType.IsInterface)
+ if (runtimeDeterminedMethod.OwningType.IsInterface)
{
- var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
- var interfaceEEType = new LoadExpressionEntry(StackValueKind.ValueType, "interfaceEEType", GetEETypePointerForTypeDesc(method.OwningType, true), eeTypeDesc);
- var eeTypeExpression = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", thisPointer, eeTypeDesc);
+ ExpressionEntry interfaceEEType;
+ ExpressionEntry eeTypeExpression;
+ if (runtimeDeterminedMethod.OwningType.IsRuntimeDeterminedSubtype)
+ {
+ var eeTypeDesc = GetEETypePtrTypeDesc();
+ //TODO interfaceEEType can be refactored out
+ eeTypeExpression = CallRuntime("System", _compilation.TypeSystemContext, "Object", "get_EEType",
+ new[] { new ExpressionEntry(StackValueKind.ObjRef, "thisPointer", thisPointer) });
+ interfaceEEType = new ExpressionEntry(StackValueKind.ValueType, "interfaceEEType", CallGenericHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType), eeTypeDesc);
+ }
+ else
+ {
+ var eeTypeDesc = GetEETypePtrTypeDesc();
+ interfaceEEType = new LoadExpressionEntry(StackValueKind.ValueType, "interfaceEEType", GetEETypePointerForTypeDesc(runtimeDeterminedMethod.OwningType, true), eeTypeDesc);
+ eeTypeExpression = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", thisPointer, eeTypeDesc);
+ }
+
var targetEntry = CallRuntime(_compilation.TypeSystemContext, DispatchResolve, "FindInterfaceMethodImplementationTarget", new StackEntry[] { eeTypeExpression, interfaceEEType, new ExpressionEntry(StackValueKind.Int32, "slot", slot, GetWellKnownType(WellKnownType.UInt16)) });
functionPtr = targetEntry.ValueAsType(LLVM.PointerType(llvmSignature, 0), _builder);
}
@@ -1730,7 +1930,90 @@ private LLVMValueRef GetCallableVirtualMethod(StackEntry objectPtr, MethodDesc m
return functionPtr;
}
- private LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature)
+ private LLVMValueRef GetCallableGenericVirtualMethod(StackEntry objectPtr, MethodDesc canonMethod, MethodDesc callee, MethodDesc runtimeDeterminedMethod, out LLVMValueRef dictPtrPtrStore,
+ out LLVMValueRef slotRef)
+ {
+ // this will only have a non-zero pointer the the GVM ptr is fat.
+ dictPtrPtrStore = LLVM.BuildAlloca(_builder,
+ LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), 0), 0),
+ "dictPtrPtrStore");
+
+ _dependencies.Add(_compilation.NodeFactory.GVMDependencies(canonMethod));
+ bool exactContextNeedsRuntimeLookup;
+ if (canonMethod.HasInstantiation)
+ {
+ exactContextNeedsRuntimeLookup = callee.IsSharedByGenericInstantiations;
+ }
+ else
+ {
+ exactContextNeedsRuntimeLookup = canonMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any);
+ }
+ LLVMValueRef runtimeMethodHandle;
+ if (exactContextNeedsRuntimeLookup)
+ {
+ LLVMValueRef helper;
+ var node = GetGenericLookupHelperAndAddReference(ReadyToRunHelperId.MethodHandle, runtimeDeterminedMethod, out helper);
+ _dependencies.Add(node);
+ runtimeMethodHandle = LLVM.BuildCall(_builder, helper, new LLVMValueRef[]
+ {
+ GetShadowStack(),
+ GetGenericContext()
+ }, "getHelper");
+ }
+ else
+ {
+ var runtimeMethodHandleNode = _compilation.NodeFactory.RuntimeMethodHandle(runtimeDeterminedMethod);
+ _dependencies.Add(runtimeMethodHandleNode);
+ runtimeMethodHandle = LoadAddressOfSymbolNode(runtimeMethodHandleNode);
+ }
+
+ var lookupSlotArgs = new StackEntry[]
+ {
+ objectPtr,
+ new ExpressionEntry(StackValueKind.ObjRef, "rmh", runtimeMethodHandle, GetWellKnownType(WellKnownType.Object))
+ };
+ var gvmPtr = CallRuntime(_compilation.TypeSystemContext, "TypeLoaderExports", "GVMLookupForSlot", lookupSlotArgs);
+ slotRef = gvmPtr.ValueAsType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), _builder);
+
+ var fatBranch = LLVM.AppendBasicBlock(_currentFunclet, "then");
+ var notFatBranch = LLVM.AppendBasicBlock(_currentFunclet, "else");
+ var endifBlock = LLVM.AppendBasicBlock(_currentFunclet, "endif");
+ // if
+ var andResRef = LLVM.BuildAnd(_builder, CastIfNecessary(_builder, slotRef, LLVMTypeRef.Int32Type()), LLVM.ConstInt(LLVM.Int32Type(), (ulong)_compilation.TypeSystemContext.Target.FatFunctionPointerOffset, LLVMMisc.False), "andPtrOffset");
+ var eqz = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntEQ, andResRef, BuildConstInt32(0), "eqz");
+ LLVM.BuildCondBr(_builder, eqz, notFatBranch, fatBranch);
+
+ // fat
+ LLVM.PositionBuilderAtEnd(_builder, fatBranch);
+ var gep = RemoveFatOffset(_builder, slotRef);
+ var loadFuncPtr = LLVM.BuildLoad(_builder,
+ CastIfNecessary(_builder, gep, LLVM.PointerType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), 0)),
+ "loadFuncPtr");
+ var dictPtrPtr = LLVM.BuildGEP(_builder,
+ CastIfNecessary(_builder, gep,
+ LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), 0), 0), "castDictPtrPtr"),
+ new [] {BuildConstInt32(1)}, "dictPtrPtr");
+ LLVM.BuildStore(_builder, dictPtrPtr, dictPtrPtrStore);
+ LLVM.BuildBr(_builder, endifBlock);
+
+ // not fat
+ LLVM.PositionBuilderAtEnd(_builder, notFatBranch);
+ // store null to indicate the GVM call needs no hidden param at run time
+ LLVM.BuildStore(_builder, LLVM.ConstPointerNull(LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), 0), 0)), dictPtrPtrStore);
+ LLVM.BuildBr(_builder, endifBlock);
+
+ // end if
+ LLVM.PositionBuilderAtEnd(_builder, endifBlock);
+ var loadPtr = LLVM.BuildPhi(_builder, LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), "fatNotFatPhi");
+ LLVM.AddIncoming(loadPtr, new LLVMValueRef[] { loadFuncPtr, slotRef },
+ new LLVMBasicBlockRef[] { fatBranch, notFatBranch }, 2);
+
+ // dont know the type for sure, but will generate for no hidden dict param and change if necessary before calling.
+ var asFunc = CastIfNecessary(_builder, loadPtr, LLVM.PointerType(GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, false), 0) , "castToFunc");
+ return asFunc;
+ }
+
+ private LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature, bool hasHiddenParam)
{
TypeDesc returnType = signature.ReturnType;
LLVMTypeRef llvmReturnType;
@@ -1753,6 +2036,11 @@ private LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature)
signatureTypes.Add(LLVM.PointerType(LLVM.Int8Type(), 0));
}
+ if (hasHiddenParam)
+ {
+ signatureTypes.Add(LLVM.PointerType(LLVM.Int8Type(), 0)); // *EEType
+ }
+
// Intentionally skipping the 'this' pointer since it could always be a GC reference
// and thus must be on the shadow stack
foreach (TypeDesc type in signature)
@@ -1766,13 +2054,15 @@ private LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature)
return LLVM.FunctionType(llvmReturnType, signatureTypes.ToArray(), false);
}
- private ExpressionEntry AllocateObject(TypeDesc type)
+ private ExpressionEntry AllocateObject(StackEntry eeType, TypeDesc forcedReturnType = null)
{
- MetadataType metadataType = (MetadataType)type;
- var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
- var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(metadataType, true), eeTypeDesc) };
//TODO: call GetNewObjectHelperForType from JitHelper.cs (needs refactoring)
- return CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhNewObject", arguments, type);
+ return CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhNewObject", new StackEntry[] { eeType }, forcedReturnType);
+ }
+
+ MetadataType GetEETypePtrTypeDesc()
+ {
+ return _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
}
private static LLVMValueRef BuildConstInt1(int number)
@@ -1796,6 +2086,11 @@ private static LLVMValueRef BuildConstInt32(int number)
return LLVM.ConstInt(LLVM.Int32Type(), (ulong)number, LLVMMisc.False);
}
+ private static LLVMValueRef BuildConstUInt32(uint number)
+ {
+ return LLVM.ConstInt(LLVM.Int32Type(), number, LLVMMisc.False);
+ }
+
private static LLVMValueRef BuildConstInt64(long number)
{
return LLVM.ConstInt(LLVM.Int64Type(), (ulong)number, LLVMMisc.False);
@@ -1828,7 +2123,7 @@ private LLVMValueRef GetEETypePointerForTypeDesc(TypeDesc target, bool construct
/// Implements intrinsic methods instread of calling them
///
/// True if the method was implemented
- private bool ImportIntrinsicCall(MethodDesc method)
+ private bool ImportIntrinsicCall(MethodDesc method, MethodDesc runtimeDeterminedMethod)
{
Debug.Assert(method.IsIntrinsic);
@@ -1898,7 +2193,7 @@ private bool ImportIntrinsicCall(MethodDesc method)
// TODO: Maybe a new runtime interface for this is better than hand-written code emission?
throw new NotImplementedException();
}
-
+
return true;
}
break;
@@ -1929,14 +2224,33 @@ private bool ImportIntrinsicCall(MethodDesc method)
return true;
}
break;
+ case "GetValueInternal":
+ if (metadataType.Namespace == "System" && metadataType.Name == "RuntimeTypeHandle")
+ {
+ var typeHandleSlot = (LdTokenEntry)_stack.Pop();
+ TypeDesc typeOfEEType = typeHandleSlot.LdToken;
+
+ if (typeOfEEType.IsRuntimeDeterminedSubtype)
+ {
+ var typeHandlerRef = CallGenericHelper(ReadyToRunHelperId.TypeHandle, typeOfEEType);
+ PushExpression(StackValueKind.Int32, "eeType", typeHandlerRef, GetWellKnownType(WellKnownType.IntPtr));
+ }
+ else
+ {
+ PushLoadExpression(StackValueKind.Int32, "eeType", GetEETypePointerForTypeDesc(typeOfEEType, true), GetWellKnownType(WellKnownType.IntPtr));
+ }
+ return true;
+ }
+ break;
}
return false;
}
- private void HandleCall(MethodDesc callee, MethodSignature signature, ILOpcode opcode = ILOpcode.call, TypeDesc constrainedType = null, LLVMValueRef calliTarget = default(LLVMValueRef))
+ private void HandleCall(MethodDesc callee, MethodSignature signature, MethodDesc runtimeDeterminedMethod, ILOpcode opcode = ILOpcode.call, TypeDesc constrainedType = null, LLVMValueRef calliTarget = default(LLVMValueRef), LLVMValueRef hiddenRef = default(LLVMValueRef))
{
- bool resolvedConstraint = false; // Not used yet, but will need for IsArrayAddressMethod and the generic lookup
+ bool resolvedConstraint = false;
+
var parameterCount = signature.Length + (signature.IsStatic ? 0 : 1);
// The last argument is the top of the stack. We need to reverse them and store starting at the first argument
StackEntry[] argumentValues = new StackEntry[parameterCount];
@@ -1967,18 +2281,31 @@ private bool ImportIntrinsicCall(MethodDesc method)
}
else if (opcode == ILOpcode.callvirt)
{
+ var canonConstrainedType = constrainedType;
+ if (constrainedType.IsRuntimeDeterminedSubtype)
+ canonConstrainedType = constrainedType.ConvertToCanonForm(CanonicalFormKind.Specific);
+
bool forceUseRuntimeLookup;
- MethodDesc directMethod = constrainedType.TryResolveConstraintMethodApprox(callee.OwningType, callee, out forceUseRuntimeLookup);
+ var constrainedClosestDefType = canonConstrainedType.GetClosestDefType();
+ MethodDesc directMethod = constrainedClosestDefType.TryResolveConstraintMethodApprox(callee.OwningType, callee, out forceUseRuntimeLookup);
if (directMethod == null)
{
- TypeDesc objectType = thisByRef.Type;
- var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
+ StackEntry eeTypeEntry;
+ var eeTypeDesc = GetEETypePtrTypeDesc();
+ if (constrainedType.IsRuntimeDeterminedSubtype)
+ {
+ eeTypeEntry = new ExpressionEntry(StackValueKind.ValueType, "eeType", CallGenericHelper(ReadyToRunHelperId.TypeHandle, constrainedType), eeTypeDesc.MakePointerType());
+ }
+ else
+ {
+ eeTypeEntry = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(constrainedType, true), eeTypeDesc);
+ }
+
argumentValues[0] = CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhBox",
new StackEntry[]
{
- new LoadExpressionEntry(StackValueKind.ValueType, "eeType",
- GetEETypePointerForTypeDesc(constrainedType, true), eeTypeDesc),
+ eeTypeEntry,
argumentValues[0],
});
}
@@ -1990,26 +2317,37 @@ private bool ImportIntrinsicCall(MethodDesc method)
}
}
}
-
- PushNonNull(HandleCall(callee, signature, argumentValues, opcode, constrainedType, calliTarget, resolvedConstraint: resolvedConstraint));
+ MethodDesc canonMethod = callee?.GetCanonMethodTarget(CanonicalFormKind.Specific);
+ PushNonNull(HandleCall(callee, signature, canonMethod, argumentValues, runtimeDeterminedMethod, opcode, constrainedType, calliTarget, hiddenRef, resolvedConstraint));
}
- private ExpressionEntry HandleCall(MethodDesc callee, MethodSignature signature, StackEntry[] argumentValues,
- ILOpcode opcode = ILOpcode.call, TypeDesc constrainedType = null,
- LLVMValueRef calliTarget = default(LLVMValueRef), bool resolvedConstraint = false, TypeDesc forcedReturnType = null)
+ private ExpressionEntry HandleCall(MethodDesc callee, MethodSignature signature, MethodDesc canonMethod, StackEntry[] argumentValues, MethodDesc runtimeDeterminedMethod, ILOpcode opcode = ILOpcode.call, TypeDesc constrainedType = null, LLVMValueRef calliTarget = default(LLVMValueRef), LLVMValueRef hiddenParamRef = default(LLVMValueRef), bool resolvedConstraint = false, TypeDesc forcedReturnType = null)
{
LLVMValueRef fn;
+ bool hasHiddenParam = false;
+ LLVMValueRef hiddenParam = default;
+ LLVMValueRef dictPtrPtrStore = default;
+ LLVMValueRef fatFunctionPtr = default;
if (opcode == ILOpcode.calli)
{
fn = calliTarget;
+ hiddenParam = hiddenParamRef;
}
else
{
- fn = LLVMFunctionForMethod(callee, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt, constrainedType);
+ fn = LLVMFunctionForMethod(callee, canonMethod, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt, constrainedType, runtimeDeterminedMethod, out hasHiddenParam, out dictPtrPtrStore, out fatFunctionPtr);
}
- LLVMValueRef returnAddress;
- LLVMValueRef castReturnAddress = default;
+ int offset = GetTotalParameterOffset() + GetTotalLocalOffset();
+ LLVMValueRef shadowStack = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_currentFunclet),
+ new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)offset, LLVMMisc.False) },
+ String.Empty);
+ var castShadowStack = LLVM.BuildPointerCast(_builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack");
+ List llvmArgs = new List
+ {
+ castShadowStack
+ };
+
TypeDesc returnType = signature.ReturnType;
bool needsReturnSlot = NeedsReturnStackSlot(signature);
@@ -2020,21 +2358,77 @@ private ExpressionEntry HandleCall(MethodDesc callee, MethodSignature signature,
int returnIndex = _spilledExpressions.Count;
returnSlot = new SpilledExpressionEntry(GetStackValueKind(actualReturnType), callee?.Name + "_return", actualReturnType, returnIndex, this);
_spilledExpressions.Add(returnSlot);
- returnAddress = LoadVarAddress(returnIndex, LocalVarKind.Temp, out TypeDesc unused);
- castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), callee?.Name + "_castreturn");
+ LLVMValueRef returnAddress = LoadVarAddress(returnIndex, LocalVarKind.Temp, out TypeDesc unused);
+ LLVMValueRef castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), callee?.Name + "_castreturn");
+ llvmArgs.Add(castReturnAddress);
}
- int offset = GetTotalParameterOffset() + GetTotalLocalOffset();
- LLVMValueRef shadowStack = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_currentFunclet),
- new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)offset, LLVMMisc.False) },
- String.Empty);
- var castShadowStack = LLVM.BuildPointerCast(_builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack");
+ // for GVM, the hidden param is added conditionally at runtime.
+ if (opcode != ILOpcode.calli && fatFunctionPtr.Pointer == IntPtr.Zero)
+ {
+ bool exactContextNeedsRuntimeLookup;
+ if (callee.HasInstantiation)
+ {
+ exactContextNeedsRuntimeLookup = callee.IsSharedByGenericInstantiations && !_isUnboxingThunk;
+ }
+ else
+ {
+ exactContextNeedsRuntimeLookup = callee.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any);
+ }
- List llvmArgs = new List();
- llvmArgs.Add(castShadowStack);
- if (needsReturnSlot)
+ if (hasHiddenParam)
+ {
+ if (exactContextNeedsRuntimeLookup)
+ {
+ if (!resolvedConstraint)
+ {
+ if (callee.RequiresInstMethodDescArg())
+ {
+ hiddenParam = CallGenericHelper(ReadyToRunHelperId.MethodDictionary, runtimeDeterminedMethod);
+ }
+ else
+ {
+ hiddenParam = CallGenericHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType);
+ }
+ }
+ else
+ {
+ Debug.Assert(canonMethod.RequiresInstMethodTableArg() && constrainedType != null);
+ if (constrainedType.IsRuntimeDeterminedSubtype)
+ {
+ hiddenParam = CallGenericHelper(ReadyToRunHelperId.TypeHandle, constrainedType);
+ }
+ else
+ {
+ var constrainedTypeSymbol = _compilation.NodeFactory.ConstructedTypeSymbol(constrainedType);
+ _dependencies.Add(constrainedTypeSymbol);
+ hiddenParam = LoadAddressOfSymbolNode(constrainedTypeSymbol);
+ }
+ }
+ }
+ else
+ {
+ if (_isUnboxingThunk && _method.RequiresInstArg())
+ {
+ hiddenParam = LLVM.GetParam(_currentFunclet, (uint)(1 + (NeedsReturnStackSlot(_signature) ? 1 : 0)));
+ }
+ else if (canonMethod.RequiresInstMethodDescArg())
+ {
+ hiddenParam = LoadAddressOfSymbolNode(GetMethodGenericDictionaryNode(callee));
+ }
+ else
+ {
+ var owningTypeSymbol = _compilation.NodeFactory.ConstructedTypeSymbol(callee.OwningType);
+ _dependencies.Add(owningTypeSymbol);
+ hiddenParam = LoadAddressOfSymbolNode(owningTypeSymbol);
+ }
+ }
+ }
+ }
+
+ if (hiddenParam.Pointer != IntPtr.Zero)
{
- llvmArgs.Add(castReturnAddress);
+ llvmArgs.Add(CastIfNecessary(hiddenParam, LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0)));
}
// argument offset on the shadow stack
@@ -2080,20 +2474,50 @@ private ExpressionEntry HandleCall(MethodDesc callee, MethodSignature signature,
argOffset += argType.GetElementSize().AsInt;
}
}
+ LLVMValueRef llvmReturn = default;
+ if (fatFunctionPtr.Pointer != IntPtr.Zero) // indicates GVM
+ {
+ // conditional call depending on if the function was fat/the dict hidden param is needed
+ // TODO: not sure this is always conditional, maybe there is some optimisation that can be done to not inject this conditional logic depending on the caller/callee
+ LLVMValueRef dict = LLVM.BuildLoad(_builder, dictPtrPtrStore, "dictPtrPtr");
+ LLVMValueRef dictAsInt = LLVM.BuildPtrToInt(_builder, dict, LLVMTypeRef.Int32Type(), "toInt");
+ LLVMValueRef eqZ = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntEQ, dictAsInt, BuildConstInt32(0), "eqz");
+ var notFatBranch = LLVM.AppendBasicBlock(_currentFunclet, "notFat");
+ var fatBranch = LLVM.AppendBasicBlock(_currentFunclet, "fat");
+ var endifBlock = LLVM.AppendBasicBlock(_currentFunclet, "endif");
+ LLVM.BuildCondBr(_builder, eqZ, notFatBranch, fatBranch);
+ // then
+ LLVM.PositionBuilderAtEnd(_builder, notFatBranch);
+ var notFatReturn = LLVM.BuildCall(_builder, fn, llvmArgs.ToArray(), string.Empty);
+ LLVM.BuildBr(_builder, endifBlock);
+
+ // else
+ LLVM.PositionBuilderAtEnd(_builder, fatBranch);
+ var fnWithDict = LLVM.BuildCast(_builder, LLVMOpcode.LLVMBitCast, fn, LLVM.PointerType(GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, true), 0), "fnWithDict");
+ var dictDereffed = LLVM.BuildLoad(_builder, LLVM.BuildLoad(_builder, dict, "l1"), "l2");
+ llvmArgs.Insert(needsReturnSlot ? 2 : 1, dictDereffed);
+ var fatReturn = LLVM.BuildCall(_builder, fnWithDict, llvmArgs.ToArray(), string.Empty);
+ LLVM.BuildBr(_builder, endifBlock);
+
+ // endif
+ LLVM.PositionBuilderAtEnd(_builder, endifBlock);
+ if (!returnType.IsVoid && !needsReturnSlot)
+ {
+ llvmReturn = LLVM.BuildPhi(_builder, GetLLVMTypeForTypeDesc(returnType), "callReturnPhi");
+ LLVM.AddIncoming(llvmReturn, new LLVMValueRef[] { notFatReturn, fatReturn },
+ new LLVMBasicBlockRef[] { notFatBranch, fatBranch }, 2);
+ }
+ _currentBasicBlock.LastInternalIf = endifBlock;
+ }
+ else llvmReturn = LLVM.BuildCall(_builder, fn, llvmArgs.ToArray(), string.Empty);
-
- LLVMValueRef llvmReturn = LLVM.BuildCall(_builder, fn, llvmArgs.ToArray(), string.Empty);
-
if (!returnType.IsVoid)
{
- if (needsReturnSlot)
- {
- return returnSlot;
- }
- else
- {
- return new ExpressionEntry(GetStackValueKind(actualReturnType), callee?.Name + "_return", llvmReturn, actualReturnType);
- }
+ return needsReturnSlot ? returnSlot :
+ (
+ canonMethod != null && canonMethod.Signature.ReturnType != actualReturnType
+ ? new GenericReturnExpressionEntry(canonMethod.Signature.ReturnType, GetStackValueKind(actualReturnType), callee?.Name + "_return", llvmReturn, actualReturnType)
+ : new ExpressionEntry(GetStackValueKind(actualReturnType), callee?.Name + "_return", llvmReturn, actualReturnType));
}
else
{
@@ -2101,19 +2525,15 @@ private ExpressionEntry HandleCall(MethodDesc callee, MethodSignature signature,
}
}
- private LLVMValueRef HandleCall(MethodDesc callee, MethodSignature signature, StackEntry[] argumentValues,
- ILOpcode opcode, TypeDesc constrainedType, LLVMValueRef calliTarget, int offset, LLVMValueRef baseShadowStack, LLVMBuilderRef builder, bool needsReturnSlot,
- LLVMValueRef castReturnAddress)
+
+ // simple calling cases, not virtual, not calli
+ private LLVMValueRef HandleDirectCall(MethodDesc callee, MethodSignature signature,
+ StackEntry[] argumentValues,
+ TypeDesc constrainedType, LLVMValueRef calliTarget, int offset, LLVMValueRef baseShadowStack,
+ LLVMBuilderRef builder, bool needsReturnSlot,
+ LLVMValueRef castReturnAddress, MethodDesc runtimeDeterminedMethod)
{
- LLVMValueRef fn;
- if (opcode == ILOpcode.calli)
- {
- fn = calliTarget;
- }
- else
- {
- fn = LLVMFunctionForMethod(callee, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt, constrainedType);
- }
+ LLVMValueRef fn = LLVMFunctionForMethod(callee, callee, signature.IsStatic ? null : argumentValues[0], false, constrainedType, runtimeDeterminedMethod, out bool hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out LLVMValueRef fatFunctionPtr);
LLVMValueRef shadowStack = LLVM.BuildGEP(builder, baseShadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)offset, LLVMMisc.False) }, String.Empty);
var castShadowStack = LLVM.BuildPointerCast(builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack");
@@ -2137,9 +2557,7 @@ private LLVMValueRef HandleCall(MethodDesc callee, MethodSignature signature, St
if (index == 0 && !signature.IsStatic)
{
isThisParameter = true;
- if (opcode == ILOpcode.calli)
- argType = toStore.Type;
- else if (callee.OwningType.IsValueType)
+ if (callee.OwningType.IsValueType)
argType = callee.OwningType.MakeByRefType();
else
argType = callee.OwningType;
@@ -2182,7 +2600,7 @@ private void AddMethodReference(MethodDesc method)
private void ImportRawPInvoke(MethodDesc method)
{
var arguments = new StackEntry[method.Signature.Length];
- for(int i = 0; i < arguments.Length; i++)
+ for (int i = 0; i < arguments.Length; i++)
{
// Arguments are reversed on the stack
// Coerce pointers to the native type
@@ -2204,7 +2622,7 @@ private ExpressionEntry ImportRawPInvoke(MethodDesc method, StackEntry[] argumen
if (method.IsPInvoke)
{
string entrypointName = method.GetPInvokeMethodMetadata().Name;
- if(!String.IsNullOrEmpty(entrypointName))
+ if (!String.IsNullOrEmpty(entrypointName))
{
realMethodName = entrypointName;
}
@@ -2315,7 +2733,7 @@ LLVMValueRef ShadowStackTop
s_shadowStackTop = LLVM.AddGlobal(Module, LLVM.PointerType(LLVM.Int8Type(), 0), "t_pShadowStackTop");
LLVM.SetLinkage(s_shadowStackTop, LLVMLinkage.LLVMInternalLinkage);
LLVM.SetInitializer(s_shadowStackTop, LLVM.ConstPointerNull(LLVM.PointerType(LLVM.Int8Type(), 0)));
- LLVM.SetThreadLocal(s_shadowStackTop, LLVMMisc.True);
+ LLVM.SetThreadLocal(s_shadowStackTop, LLVMMisc.True);
}
return s_shadowStackTop;
}
@@ -2433,60 +2851,167 @@ private void EmitNativeToManagedThunk(WebAssemblyCodegenCompilation compilation,
private void ImportCalli(int token)
{
- MethodSignature methodSignature = (MethodSignature)_methodIL.GetObject(token);
- HandleCall(null, methodSignature, ILOpcode.calli, calliTarget: ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVM.PointerType(GetLLVMSignatureForMethod(methodSignature), 0), _builder));
+ MethodSignature methodSignature = (MethodSignature)_canonMethodIL.GetObject(token);
+
+ var noHiddenParamSig = GetLLVMSignatureForMethod(methodSignature, false);
+ var hddenParamSig = GetLLVMSignatureForMethod(methodSignature, true);
+ var target = ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVM.PointerType(noHiddenParamSig, 0), _builder);
+
+ var functionPtrAsInt = LLVM.BuildPtrToInt(_builder, target, LLVMTypeRef.Int32Type(), "ptrToInt");
+ var andResRef = LLVM.BuildBinOp(_builder, LLVMOpcode.LLVMAnd, functionPtrAsInt, LLVM.ConstInt(LLVM.Int32Type(), (ulong)_compilation.TypeSystemContext.Target.FatFunctionPointerOffset, LLVMMisc.False), "andFatCheck");
+ var boolConv = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntEQ, andResRef, BuildConstInt32(0), "bitConv");
+ var fatBranch = LLVM.AppendBasicBlock(_currentFunclet, "fat");
+ var notFatBranch = LLVM.AppendBasicBlock(_currentFunclet, "notFat");
+ var endif = LLVM.AppendBasicBlock(_currentFunclet, "endif");
+ LLVM.BuildCondBr(_builder, boolConv, notFatBranch, fatBranch);
+ LLVM.PositionBuilderAtEnd(_builder, notFatBranch);
+
+ // non fat branch
+ var parameterCount = methodSignature.Length + (methodSignature.IsStatic ? 0 : 1);
+ StackEntry[] stackCopy = new StackEntry[parameterCount];
+ for (int i = 0; i < stackCopy.Length; i++)
+ {
+ stackCopy[i] = _stack.Pop();
+ }
+ for (int i = 0; i < stackCopy.Length; i++)
+ {
+ _stack.Push(stackCopy[stackCopy.Length - i - 1]);
+ }
+ HandleCall(null, methodSignature, null, ILOpcode.calli, calliTarget: target);
+ LLVMValueRef fatResRef = default;
+ LLVMValueRef nonFatResRef = default;
+ bool hasRes = !methodSignature.ReturnType.IsVoid;
+ if (hasRes)
+ {
+ StackEntry nonFatRes = _stack.Pop();
+ nonFatResRef = nonFatRes.ValueAsType(methodSignature.ReturnType, _builder);
+ }
+ LLVM.BuildBr(_builder, endif);
+ LLVM.PositionBuilderAtEnd(_builder, fatBranch);
+
+ // fat branch
+ var minusOffset = RemoveFatOffset(_builder, target);
+ var minusOffsetPtr = LLVM.BuildIntToPtr(_builder, minusOffset,
+ LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), "ptr");
+ var hiddenRefAddr = LLVM.BuildGEP(_builder, minusOffsetPtr, new[] { BuildConstInt32(_pointerSize) }, "fatArgPtr");
+ var hiddenRefPtrPtr = LLVM.BuildPointerCast(_builder, hiddenRefAddr, LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), 0), 0), "hiddenRefPtr");
+ var hiddenRef = LLVM.BuildLoad(_builder, LLVM.BuildLoad(_builder, hiddenRefPtrPtr, "hiddenRefPtr"), "hiddenRef");
+
+ for (int i = 0; i < stackCopy.Length; i++)
+ {
+ _stack.Push(stackCopy[stackCopy.Length - i - 1]);
+ }
+ var funcPtrPtrWithHidden = LLVM.BuildPointerCast(_builder, minusOffsetPtr, LLVM.PointerType(LLVM.PointerType(hddenParamSig, 0), 0), "hiddenFuncPtr");
+ var funcWithHidden = LLVM.BuildLoad(_builder, funcPtrPtrWithHidden, "funcPtr");
+ HandleCall(null, methodSignature, null, ILOpcode.calli, calliTarget: funcWithHidden, hiddenRef: hiddenRef);
+ StackEntry fatRes = null;
+ if (hasRes)
+ {
+ fatRes = _stack.Pop();
+ fatResRef = fatRes.ValueAsType(methodSignature.ReturnType, _builder);
+ }
+ LLVM.BuildBr(_builder, endif);
+ LLVM.PositionBuilderAtEnd(_builder, endif);
+
+ // choose the right return value
+ if (hasRes)
+ {
+ var phi = LLVM.BuildPhi(_builder, GetLLVMTypeForTypeDesc(methodSignature.ReturnType), "phi");
+ LLVM.AddIncoming(phi, new LLVMValueRef[] { fatResRef, nonFatResRef },
+ new LLVMBasicBlockRef[] { fatBranch, notFatBranch }, 2);
+ PushExpression(fatRes.Kind, "phi", phi, fatRes.Type);
+ }
+ _currentBasicBlock.LastInternalIf = endif;
}
private void ImportLdFtn(int token, ILOpcode opCode)
{
- MethodDesc method = (MethodDesc)_methodIL.GetObject(token);
- LLVMValueRef targetLLVMFunction = default(LLVMValueRef);
+ MethodDesc runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token);
+ MethodDesc method = ((MethodDesc)_canonMethodIL.GetObject(token));
+ MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific);
+ LLVMValueRef targetLLVMFunction = default;
+ bool hasHiddenParam = false;
+
if (opCode == ILOpcode.ldvirtftn)
{
StackEntry thisPointer = _stack.Pop();
- if (method.IsVirtual)
+ if (runtimeDeterminedMethod.IsVirtual)
{
- targetLLVMFunction = LLVMFunctionForMethod(method, thisPointer, true, null);
+ // we want the fat function ptr here
+ LLVMValueRef fatFunctionPtr;
+ targetLLVMFunction = LLVMFunctionForMethod(method, canonMethod, thisPointer, true, null, runtimeDeterminedMethod, out hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out fatFunctionPtr);
+ if (fatFunctionPtr.Pointer != IntPtr.Zero)
+ {
+ targetLLVMFunction = fatFunctionPtr;
+ }
}
else
{
- AddMethodReference(method);
+ AddMethodReference(runtimeDeterminedMethod);
}
}
else
{
- AddMethodReference(method);
+ if (canonMethod.IsSharedByGenericInstantiations && (canonMethod.HasInstantiation || canonMethod.Signature.IsStatic))
+ {
+ var exactContextNeedsRuntimeLookup = method.HasInstantiation
+ ? method.IsSharedByGenericInstantiations
+ : method.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any);
+ if (exactContextNeedsRuntimeLookup)
+ {
+ targetLLVMFunction = CallGenericHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod);
+ if (!(canonMethod.IsVirtual && !canonMethod.IsFinal && !canonMethod.OwningType.IsSealed()))
+ {
+ // fat function pointer
+ targetLLVMFunction = MakeFatPointer(_builder, targetLLVMFunction, _compilation);
+ }
+ }
+ else
+ {
+ var fatFunctionSymbol = GetAndAddFatFunctionPointer(runtimeDeterminedMethod);
+ targetLLVMFunction = MakeFatPointer(_builder, LoadAddressOfSymbolNode(fatFunctionSymbol), _compilation);
+ }
+ }
+ else AddMethodReference(canonMethod);
}
if (targetLLVMFunction.Pointer.Equals(IntPtr.Zero))
{
- if (method.IsNativeCallable)
+ if (runtimeDeterminedMethod.IsNativeCallable)
{
- EcmaMethod ecmaMethod = ((EcmaMethod)method);
+ EcmaMethod ecmaMethod = ((EcmaMethod)runtimeDeterminedMethod);
string mangledName = ecmaMethod.GetNativeCallableExportName();
if (mangledName == null)
{
mangledName = ecmaMethod.Name;
}
- LLVMTypeRef[] llvmParams = new LLVMTypeRef[method.Signature.Length];
+ LLVMTypeRef[] llvmParams = new LLVMTypeRef[runtimeDeterminedMethod.Signature.Length];
for (int i = 0; i < llvmParams.Length; i++)
{
- llvmParams[i] = GetLLVMTypeForTypeDesc(method.Signature[i]);
+ llvmParams[i] = GetLLVMTypeForTypeDesc(runtimeDeterminedMethod.Signature[i]);
}
- LLVMTypeRef thunkSig = LLVM.FunctionType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), llvmParams, false);
+ LLVMTypeRef thunkSig = LLVM.FunctionType(GetLLVMTypeForTypeDesc(runtimeDeterminedMethod.Signature.ReturnType), llvmParams, false);
targetLLVMFunction = GetOrCreateLLVMFunction(mangledName, thunkSig);
}
else
{
- targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(method).ToString(), method.Signature);
+ hasHiddenParam = canonMethod.RequiresInstArg();
+ targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(canonMethod).ToString(), runtimeDeterminedMethod.Signature, hasHiddenParam);
}
}
- var entry = new FunctionPointerEntry("ldftn", method, targetLLVMFunction, GetWellKnownType(WellKnownType.IntPtr), opCode == ILOpcode.ldvirtftn);
+ var entry = new FunctionPointerEntry("ldftn", runtimeDeterminedMethod, targetLLVMFunction, GetWellKnownType(WellKnownType.IntPtr), opCode == ILOpcode.ldvirtftn);
_stack.Push(entry);
}
+ ISymbolNode GetAndAddFatFunctionPointer(MethodDesc method, bool isUnboxingStub = false)
+ {
+ ISymbolNode node = _compilation.NodeFactory.FatFunctionPointer(method, isUnboxingStub);
+ _dependencies.Add(node);
+ return node;
+ }
+
private void ImportLoadInt(long value, StackValueKind kind)
{
switch (kind)
@@ -2502,7 +3027,7 @@ private void ImportLoadInt(long value, StackValueKind kind)
default:
throw new InvalidOperationException(kind.ToString());
- }
+ }
}
@@ -2676,7 +3201,7 @@ 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)
+ if (type == null)
{
type = GetWellKnownType(WellKnownType.Object);
}
@@ -2744,7 +3269,7 @@ private void ImportBinaryOperation(ILOpcode opcode)
LLVMValueRef right = op1.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op1.Type));
if (kind == StackValueKind.Float)
{
- if(op1.Type.IsWellKnownType(WellKnownType.Double) && op2.Type.IsWellKnownType(WellKnownType.Single))
+ if (op1.Type.IsWellKnownType(WellKnownType.Double) && op2.Type.IsWellKnownType(WellKnownType.Single))
{
left = LLVM.BuildFPExt(_builder, left, LLVM.DoubleType(), "fpextop2");
}
@@ -2899,16 +3424,16 @@ private void ImportShiftOperation(ILOpcode opcode)
default:
throw new InvalidOperationException(); // Should be unreachable
}
-
+ //TODO: do we need this if we sign extend above?
PushExpression(valueToShift.Kind, "shiftop", result, WidenBytesAndShorts(valueToShift.Type));
}
bool TypeNeedsSignExtension(TypeDesc targetType)
{
var enumCleanTargetType = targetType?.UnderlyingType;
- if(enumCleanTargetType != null && targetType.IsPrimitive)
+ if (enumCleanTargetType != null && targetType.IsPrimitive)
{
- if(enumCleanTargetType.IsWellKnownType(WellKnownType.Byte) ||
+ if (enumCleanTargetType.IsWellKnownType(WellKnownType.Byte) ||
enumCleanTargetType.IsWellKnownType(WellKnownType.Char) ||
enumCleanTargetType.IsWellKnownType(WellKnownType.UInt16) ||
enumCleanTargetType.IsWellKnownType(WellKnownType.UInt32) ||
@@ -3017,7 +3542,7 @@ private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool
private void ImportUnaryOperation(ILOpcode opCode)
{
var argument = _stack.Pop();
-
+
LLVMValueRef result;
switch (opCode)
{
@@ -3025,7 +3550,7 @@ private void ImportUnaryOperation(ILOpcode opCode)
if (argument.Kind == StackValueKind.Float)
{
result = LLVM.BuildFNeg(_builder, argument.ValueForStackKind(argument.Kind, _builder, false), "neg");
- }
+ }
else
{
result = LLVM.BuildNeg(_builder, argument.ValueForStackKind(argument.Kind, _builder, true), "neg");
@@ -3074,32 +3599,52 @@ private void ImportCpOpj(int token)
private void ImportUnbox(int token, ILOpcode opCode)
{
TypeDesc type = ResolveTypeToken(token);
- LLVMValueRef eeType = GetEETypePointerForTypeDesc(type, true);
- var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
+ TypeDesc methodType = (TypeDesc)_methodIL.GetObject(token);
+ LLVMValueRef eeType;
+ var eeTypeDesc = GetEETypePtrTypeDesc();
+ ExpressionEntry eeTypeExp;
+ if (methodType.IsRuntimeDeterminedSubtype)
+ {
+ eeType = CallGenericHelper(ReadyToRunHelperId.TypeHandle, methodType);
+ eeTypeExp = new ExpressionEntry(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc);
+ }
+ else
+ {
+ eeType = GetEETypePointerForTypeDesc(methodType, true);
+ eeTypeExp = new LoadExpressionEntry(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc);
+ }
StackEntry boxedObject = _stack.Pop();
if (opCode == ILOpcode.unbox)
{
- if (type.IsNullable)
+ if (methodType.IsNullable)
throw new NotImplementedException();
- var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc), boxedObject };
+ var arguments = new StackEntry[] { eeTypeExp, boxedObject };
PushNonNull(CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhUnbox2", arguments));
}
else //unbox_any
{
Debug.Assert(opCode == ILOpcode.unbox_any);
- LLVMValueRef untypedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(type), "objptr");
+ LLVMValueRef untypedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(methodType), "objptr");
var arguments = new StackEntry[]
{
boxedObject,
- new ExpressionEntry(StackValueKind.ByRef, "objPtr", untypedObjectValue, type.MakePointerType()),
- new LoadExpressionEntry(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc)
+ new ExpressionEntry(StackValueKind.ByRef, "objPtr", untypedObjectValue, methodType.MakePointerType()),
+ eeTypeExp
};
CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhUnboxAny", arguments);
- PushLoadExpression(GetStackValueKind(type), "unboxed", untypedObjectValue, type);
+ PushLoadExpression(GetStackValueKind(methodType), "unboxed", untypedObjectValue, methodType);
}
}
+ LLVMValueRef GetShadowStack()
+ {
+ int offset = GetTotalParameterOffset() + GetTotalLocalOffset();
+ return LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_currentFunclet),
+ new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)offset, LLVMMisc.False) },
+ String.Empty);
+ }
+
private void ImportRefAnyVal(int token)
{
}
@@ -3115,20 +3660,36 @@ private void ImportMkRefAny(int token)
private void ImportLdToken(int token)
{
var ldtokenValue = _methodIL.GetObject(token);
- StackEntry value;
if (ldtokenValue is TypeDesc)
{
+ TypeDesc runtimeTypeHandleTypeDesc = GetWellKnownType(WellKnownType.RuntimeTypeHandle);
var typeDesc = (TypeDesc)ldtokenValue;
- PushLoadExpression(StackValueKind.ByRef, "ldtoken", GetEETypePointerForTypeDesc(typeDesc, false), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr"));
MethodDesc helper = _compilation.TypeSystemContext.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeTypeHandle");
AddMethodReference(helper);
- HandleCall(helper, helper.Signature);
- _dependencies.Add(_compilation.NodeFactory.MaximallyConstructableType(typeDesc));
+ var fn = LLVMFunctionForMethod(helper, helper, null/* static method */, false /* not virt */, _constrainedType, helper, out bool hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out LLVMValueRef fatFunctionPtr);
+
+ if (typeDesc.IsRuntimeDeterminedSubtype)
+ {
+ var hiddenParam = CallGenericHelper(ReadyToRunHelperId.TypeHandle, typeDesc);
+ var handleRef = LLVM.BuildCall(_builder, fn, new LLVMValueRef[]
+ {
+ GetShadowStack(),
+ hiddenParam
+ }, "getHelper");
+ _stack.Push(new LdTokenEntry(StackValueKind.ValueType, "ldtoken", typeDesc, handleRef, runtimeTypeHandleTypeDesc));
+ }
+ else
+ {
+ PushLoadExpression(StackValueKind.ByRef, "ldtoken", GetEETypePointerForTypeDesc(typeDesc, true), GetEETypePtrTypeDesc());
+ HandleCall(helper, helper.Signature, helper);
+ var callExp = _stack.Pop();
+ _stack.Push(new LdTokenEntry(StackValueKind.ValueType, "ldtoken", typeDesc, callExp.ValueAsInt32(_builder, false), runtimeTypeHandleTypeDesc));
+ }
}
else if (ldtokenValue is FieldDesc)
{
LLVMValueRef fieldHandle = LLVM.ConstStruct(new LLVMValueRef[] { BuildConstInt32(0) }, true);
- value = new LdTokenEntry(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, fieldHandle, GetWellKnownType(WellKnownType.RuntimeFieldHandle));
+ StackEntry value = new LdTokenEntry(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, fieldHandle, GetWellKnownType(WellKnownType.RuntimeFieldHandle));
_stack.Push(value);
}
else if (ldtokenValue is MethodDesc)
@@ -3234,19 +3795,18 @@ private void ThrowIfNull(LLVMValueRef entry)
LLVM.PositionBuilderAtEnd(builder, throwBlock);
MetadataType nullRefType = _compilation.NodeFactory.TypeSystemContext.SystemModule.GetType("System", "NullReferenceException");
- var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
- var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(nullRefType, true), eeTypeDesc) };
+ var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(nullRefType, true), GetEETypePtrTypeDesc()) };
MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System.Runtime", RuntimeExport);
MethodDesc helperMethod = helperType.GetKnownMethod("RhNewObject", null);
var resultAddress = LLVM.BuildIntCast(builder, LLVM.BuildAlloca(builder, LLVM.Int32Type(), "resultAddress"), LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), "castResultAddress");
- HandleCall(helperMethod, helperMethod.Signature, arguments, ILOpcode.call, null, default(LLVMValueRef), 0, LLVM.GetParam(NullRefFunction, 0), builder, true, resultAddress);
+ HandleDirectCall(helperMethod, helperMethod.Signature, arguments, null, default(LLVMValueRef), 0, LLVM.GetParam(NullRefFunction, 0), builder, true, resultAddress, helperMethod);
var exceptionEntry = new ExpressionEntry(GetStackValueKind(nullRefType), "RhNewObject_return", resultAddress, nullRefType);
var ctorDef = nullRefType.GetDefaultConstructor();
- var constructedExceptionObject = HandleCall(ctorDef, ctorDef.Signature, new StackEntry[] { exceptionEntry }, ILOpcode.call, null, default(LLVMValueRef), 0, LLVM.GetParam(NullRefFunction, 0), builder, false, default(LLVMValueRef));
+ var constructedExceptionObject = HandleDirectCall(ctorDef, ctorDef.Signature, new StackEntry[] { exceptionEntry }, null, default(LLVMValueRef), 0, LLVM.GetParam(NullRefFunction, 0), builder, false, default(LLVMValueRef), ctorDef);
EmitTrapCall(builder);
LLVM.PositionBuilderAtEnd(builder, retBlock);
@@ -3293,7 +3853,7 @@ private LLVMValueRef GetInstanceFieldAddress(StackEntry objectEntry, FieldDesc f
}
}
- private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic)
+ private LLVMValueRef GetFieldAddress(FieldDesc runtimeDeterminedField, FieldDesc field, bool isStatic)
{
if (field.IsStatic)
{
@@ -3301,16 +3861,17 @@ private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic)
if (!isStatic)
_stack.Pop();
- ISymbolNode node;
- MetadataType owningType = (MetadataType)field.OwningType;
+ ISymbolNode node = null;
+ MetadataType owningType = (MetadataType)_compilation.ConvertToCanonFormIfNecessary(field.OwningType, CanonicalFormKind.Specific);
LLVMValueRef staticBase;
int fieldOffset;
// If the type is non-BeforeFieldInit, this is handled before calling any methods on it
+ //TODO : this seems to call into the cctor if the cctor itself accesses static fields. e.g. SR. Try a test with an ++ in the cctor
bool needsCctorCheck = (owningType.IsBeforeFieldInit || (!owningType.IsBeforeFieldInit && owningType != _thisType)) && _compilation.TypeSystemContext.HasLazyStaticConstructor(owningType);
if (field.HasRva)
{
- node = (ISymbolNode)_compilation.GetFieldRvaData(field);
+ node = _compilation.GetFieldRvaData(field);
staticBase = LoadAddressOfSymbolNode(node);
fieldOffset = 0;
// Run static constructor if necessary
@@ -3322,26 +3883,48 @@ private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic)
else
{
fieldOffset = field.Offset.AsInt;
-
+ TypeDesc runtimeDeterminedOwningType = runtimeDeterminedField.OwningType;
if (field.IsThreadStatic)
{
- // TODO: We need the right thread static per thread
- ExpressionEntry returnExp;
- node = TriggerCctorWithThreadStaticStorage(owningType, needsCctorCheck, out returnExp);
- staticBase = returnExp.ValueAsType(returnExp.Type, _builder);
+ if (runtimeDeterminedOwningType.IsRuntimeDeterminedSubtype)
+ {
+ staticBase = CallGenericHelper(ReadyToRunHelperId.GetThreadStaticBase, runtimeDeterminedOwningType);
+ }
+ else
+ {
+ ExpressionEntry returnExp;
+ node = TriggerCctorWithThreadStaticStorage((MetadataType)runtimeDeterminedOwningType, needsCctorCheck, out returnExp);
+ staticBase = returnExp.ValueAsType(returnExp.Type, _builder);
+ }
}
else
{
if (field.HasGCStaticBase)
{
- node = _compilation.NodeFactory.TypeGCStaticsSymbol(owningType);
- 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");
+ if (runtimeDeterminedOwningType.IsRuntimeDeterminedSubtype)
+ {
+ needsCctorCheck = false; // no cctor for canonical types
+ staticBase = CallGenericHelper(ReadyToRunHelperId.GetGCStaticBase, runtimeDeterminedOwningType);
+ }
+ else
+ {
+ node = _compilation.NodeFactory.TypeGCStaticsSymbol(owningType);
+ 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");
+ }
}
else
{
- node = _compilation.NodeFactory.TypeNonGCStaticsSymbol(owningType);
- staticBase = LoadAddressOfSymbolNode(node);
+ if (runtimeDeterminedOwningType.IsRuntimeDeterminedSubtype)
+ {
+ needsCctorCheck = false; // no cctor for canonical types
+ staticBase = CallGenericHelper(ReadyToRunHelperId.GetNonGCStaticBase, runtimeDeterminedOwningType);
+ }
+ else
+ {
+ node = _compilation.NodeFactory.TypeNonGCStaticsSymbol(owningType);
+ staticBase = LoadAddressOfSymbolNode(node);
+ }
}
// Run static constructor if necessary
if (needsCctorCheck)
@@ -3351,7 +3934,7 @@ private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic)
}
}
- _dependencies.Add(node);
+ if (node != null) _dependencies.Add(node);
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");
@@ -3365,11 +3948,46 @@ private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic)
}
}
+ ISymbolNode GetGenericLookupHelperAndAddReference(ReadyToRunHelperId helperId, object helperArg, out LLVMValueRef helper, IEnumerable additionalArgs = null)
+ {
+ ISymbolNode node;
+ var retType = helperId == ReadyToRunHelperId.DelegateCtor
+ ? LLVMTypeRef.VoidType()
+ : LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0);
+ var helperArgs = new List
+ {
+ LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0),
+ LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0),
+ };
+ if (additionalArgs != null) helperArgs.AddRange(additionalArgs);
+ if (_method.RequiresInstMethodDescArg())
+ {
+ node = _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup(helperId, helperArg, _method);
+ helper = GetOrCreateLLVMFunction(node.GetMangledName(_compilation.NameMangler),
+ LLVMTypeRef.FunctionType(retType, helperArgs.ToArray(), false));
+ }
+ else
+ {
+ Debug.Assert(_method.RequiresInstMethodTableArg() || _method.AcquiresInstMethodTableFromThis());
+ node = _compilation.NodeFactory.ReadyToRunHelperFromTypeLookup(helperId, helperArg, _method.OwningType);
+ helper = GetOrCreateLLVMFunction(node.GetMangledName(_compilation.NameMangler),
+ LLVMTypeRef.FunctionType(retType, helperArgs.ToArray(), false));
+ }
+ // cpp backend relies on a lazy static constructor to get this node added during the dependency generation.
+ // If left to when the code is written that uses the helper then its too late.
+ IMethodNode helperNode = (IMethodNode)_compilation.NodeFactory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase);
+
+ _dependencies.Add(node);
+ _dependencies.Add(helperNode);
+ return node;
+ }
+
///
/// Triggers a static constructor check and call for types that have them
///
private void TriggerCctor(MetadataType type)
{
+ if (type.IsCanonicalSubtype(CanonicalFormKind.Specific)) return; // TODO - what to do here?
ISymbolNode classConstructionContextSymbol = _compilation.NodeFactory.TypeNonGCStaticsSymbol(type);
_dependencies.Add(classConstructionContextSymbol);
LLVMValueRef firstNonGcStatic = LoadAddressOfSymbolNode(classConstructionContextSymbol);
@@ -3425,26 +4043,48 @@ private ISymbolNode TriggerCctorWithThreadStaticStorage(MetadataType type, bool
}
}
+ private void TriggerCctor(MetadataType type, LLVMValueRef staticBaseValueRef, string runnerMethodName)
+ {
+ var classConstCtx = LLVM.BuildGEP(_builder,
+ LLVM.BuildBitCast(_builder, staticBaseValueRef, LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0),
+ "ptr8"), new LLVMValueRef[] { BuildConstInt32(-8) }, "backToClassCtx");
+ StackEntry classConstructionContext = new AddressExpressionEntry(StackValueKind.NativeInt, "classConstructionContext", classConstCtx,
+ GetWellKnownType(WellKnownType.IntPtr));
+ StackEntry staticBaseEntry = new AddressExpressionEntry(StackValueKind.NativeInt, "staticBase", staticBaseValueRef,
+ GetWellKnownType(WellKnownType.IntPtr));
+
+ CallRuntime("System.Runtime.CompilerServices", _compilation.TypeSystemContext, ClassConstructorRunner, runnerMethodName, new StackEntry[]
+ {
+ classConstructionContext,
+ staticBaseEntry
+ });
+ }
+
private void ImportLoadField(int token, bool isStatic)
{
FieldDesc field = (FieldDesc)_methodIL.GetObject(token);
- LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic);
+ LLVMValueRef fieldAddress = GetFieldAddress(field, (FieldDesc)_canonMethodIL.GetObject(token), isStatic);
+
PushLoadExpression(GetStackValueKind(field.FieldType), $"Field_{field.Name}", fieldAddress, field.FieldType);
}
private void ImportAddressOfField(int token, bool isStatic)
{
FieldDesc field = (FieldDesc)_methodIL.GetObject(token);
- LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic);
+ LLVMValueRef fieldAddress = GetFieldAddress(field, (FieldDesc)_canonMethodIL.GetObject(token), isStatic);
_stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, $"FieldAddress_{field.Name}", fieldAddress, field.FieldType.MakeByRefType()));
}
private void ImportStoreField(int token, bool isStatic)
{
- FieldDesc field = (FieldDesc)_methodIL.GetObject(token);
+ FieldDesc runtimeDeterminedField = (FieldDesc)_methodIL.GetObject(token);
+ FieldDesc field = (FieldDesc)_canonMethodIL.GetObject(token);
StackEntry valueEntry = _stack.Pop();
- LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic);
- CastingStore(fieldAddress, valueEntry, field.FieldType);
+ // TypeDesc owningType = _compilation.ConvertToCanonFormIfNecessary(field.OwningType, CanonicalFormKind.Specific);
+ TypeDesc fieldType = _compilation.ConvertToCanonFormIfNecessary(field.FieldType, CanonicalFormKind.Specific);
+
+ LLVMValueRef fieldAddress = GetFieldAddress(runtimeDeterminedField, field, isStatic);
+ CastingStore(fieldAddress, valueEntry, fieldType);
}
// Loads symbol address. Address is represented as a i32*
@@ -3487,11 +4127,25 @@ private void ImportInitObj(int token)
private void ImportBox(int token)
{
- TypeDesc type = ResolveTypeToken(token);
- LLVMValueRef eeType = GetEETypePointerForTypeDesc(type, true);
- var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr");
+ LLVMValueRef eeType;
+ TypeDesc type = (TypeDesc)_methodIL.GetObject(token);
+
+ StackEntry eeTypeEntry;
+ var eeTypeDesc = GetEETypePtrTypeDesc();
bool truncDouble = type.Equals(GetWellKnownType(WellKnownType.Single));
+ if (type.IsRuntimeDeterminedSubtype)
+ {
+ eeType = CallGenericHelper(ReadyToRunHelperId.TypeHandle, type);
+ eeTypeEntry = new ExpressionEntry(StackValueKind.ValueType, "eeType", eeType, eeTypeDesc.MakePointerType());
+ type = type.ConvertToCanonForm(CanonicalFormKind.Specific);
+ }
+ else
+ {
+ eeType = GetEETypePointerForTypeDesc(type, true);
+ eeTypeEntry = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", eeType, eeTypeDesc.MakePointerType());
+ }
var toBoxValue = _stack.Pop();
+ StackEntry valueAddress;
if (truncDouble)
{
var doubleToBox = toBoxValue.ValueAsType(LLVMTypeRef.DoubleType(), _builder);
@@ -3499,8 +4153,7 @@ private void ImportBox(int token)
toBoxValue = new ExpressionEntry(StackValueKind.Float, "singleToBox", singleToBox,
GetWellKnownType(WellKnownType.Single));
}
- StackEntry valueAddress = TakeAddressOf(toBoxValue);
- var eeTypeEntry = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", eeType, eeTypeDesc.MakePointerType());
+ valueAddress = TakeAddressOf(toBoxValue);
if (type.IsValueType)
{
var arguments = new StackEntry[] { eeTypeEntry, valueAddress };
@@ -3541,12 +4194,39 @@ private static bool IsOffsetContained(int offset, int start, int length)
private void ImportNewArray(int token)
{
- TypeDesc arrayType = ResolveTypeToken(token).MakeArrayType();
+ TypeDesc runtimeDeterminedType = (TypeDesc)_methodIL.GetObject(token);
+ TypeDesc runtimeDeterminedArrayType = runtimeDeterminedType.MakeArrayType();
var sizeOfArray = _stack.Pop();
+ StackEntry[] arguments;
var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime", "EEType").MakePointerType();
- var arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(arrayType, true), eeTypeDesc), sizeOfArray };
- //TODO: call GetNewArrayHelperForType from JitHelper.cs (needs refactoring)
- PushNonNull(CallRuntime(_compilation.TypeSystemContext, InternalCalls, "RhpNewArray", arguments, arrayType));
+ if (runtimeDeterminedArrayType.IsRuntimeDeterminedSubtype)
+ {
+ var lookedUpType = CallGenericHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedArrayType);
+ arguments = new StackEntry[] { new ExpressionEntry(StackValueKind.ValueType, "eeType", lookedUpType, eeTypeDesc), sizeOfArray };
+ }
+ else
+ {
+ arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(runtimeDeterminedArrayType, true), eeTypeDesc), sizeOfArray };
+ //TODO: call GetNewArrayHelperForType from JitHelper.cs (needs refactoring)
+ }
+ PushNonNull(CallRuntime(_compilation.TypeSystemContext, InternalCalls, "RhpNewArray", arguments, runtimeDeterminedArrayType));
+ }
+
+ LLVMValueRef GetGenericContext()
+ {
+ Debug.Assert(_method.IsSharedByGenericInstantiations);
+ if (_method.AcquiresInstMethodTableFromThis())
+ {
+ LLVMValueRef typedAddress;
+ LLVMValueRef thisPtr;
+
+ typedAddress = CastIfNecessary(_builder, LLVM.GetFirstParam(_currentFunclet),
+ LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), 0), 0));
+ thisPtr = LLVM.BuildLoad(_builder, typedAddress, "loadThis");
+
+ return LLVM.BuildLoad(_builder, thisPtr, "methodTablePtrRef");
+ }
+ return CastIfNecessary(_builder, LLVM.GetParam(_llvmFunction, 1 + (NeedsReturnStackSlot(_method.Signature) ? (uint)1 : 0) /* hidden param after shadow stack and return slot if present */), LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), "HiddenArg");
}
private LLVMValueRef ArrayBaseSizeRef()
@@ -3681,7 +4361,6 @@ private void ImportFallthrough(BasicBlock next)
}
private const string RuntimeExport = "RuntimeExports";
- private const string RuntimeImport = "RuntimeImports";
private const string InternalCalls = "InternalCalls";
private const string TypeCast = "TypeCast";
private const string DispatchResolve = "DispatchResolve";
@@ -3700,7 +4379,7 @@ private ExpressionEntry CallRuntime(string @namespace, TypeSystemContext context
if ((helperMethod.IsInternalCall && helperMethod.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")))
return ImportRawPInvoke(helperMethod, arguments, forcedReturnType: forcedReturnType);
else
- return HandleCall(helperMethod, helperMethod.Signature, arguments, forcedReturnType: forcedReturnType);
+ return HandleCall(helperMethod, helperMethod.Signature, helperMethod, arguments, helperMethod);
}
private void PushNonNull(StackEntry entry)
@@ -3725,7 +4404,7 @@ private StackEntry TakeAddressOf(StackEntry entry)
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
LLVMValueRef addressValue;
- if(entry is LoadExpressionEntry)
+ if (entry is LoadExpressionEntry)
{
addressValue = ((LoadExpressionEntry)entry).RawLLVMValue;
}
@@ -3743,11 +4422,9 @@ private StackEntry TakeAddressOf(StackEntry entry)
_spilledExpressions.Add(newEntry);
if (entry is ExpressionEntry)
- StoreTemp(entryIndex, ((ExpressionEntry)entry).RawLLVMValue);
+ addressValue = StoreTemp(entryIndex, ((ExpressionEntry)entry).RawLLVMValue);
else
- StoreTemp(entryIndex, entry.ValueForStackKind(entry.Kind, _builder, false));
-
- addressValue = LoadVarAddress(entryIndex, LocalVarKind.Temp, out TypeDesc type);
+ addressValue = StoreTemp(entryIndex, entry.ValueForStackKind(entry.Kind, _builder, false));
}
return new AddressExpressionEntry(StackValueKind.NativeInt, "address_of", addressValue, entry.Type.MakePointerType());
@@ -3755,7 +4432,7 @@ private StackEntry TakeAddressOf(StackEntry entry)
private TypeDesc ResolveTypeToken(int token)
{
- return (TypeDesc)_methodIL.GetObject(token);
+ return (TypeDesc)_canonMethodIL.GetObject(token);
}
private TypeDesc GetWellKnownType(WellKnownType wellKnownType)
@@ -3809,5 +4486,32 @@ public override string ToString()
{
return _method.ToString();
}
+
+ //TOOD refactor with cctor
+ public ExpressionEntry OutputCodeForGetThreadStaticBaseForType(LLVMValueRef threadStaticIndex)
+ {
+ var threadStaticIndexPtr = LLVM.BuildPointerCast(_builder, threadStaticIndex,
+ LLVMTypeRef.PointerType(LLVMTypeRef.PointerType(LLVMTypeRef.Int32Type(), 0), 0), "tsiPtr");
+ LLVMValueRef typeTlsIndexPtr =
+ LLVM.BuildGEP(_builder, threadStaticIndexPtr, new LLVMValueRef[] { BuildConstInt32(1) }, "typeTlsIndexPtr"); // index is the second field after the ptr.
+
+ StackEntry typeManagerSlotEntry = new LoadExpressionEntry(StackValueKind.ValueType, "typeManagerSlot", threadStaticIndexPtr, GetWellKnownType(WellKnownType.Int32));
+ StackEntry tlsIndexExpressionEntry = new LoadExpressionEntry(StackValueKind.ValueType, "typeTlsIndex", typeTlsIndexPtr, GetWellKnownType(WellKnownType.Int32));
+
+ var expressionEntry = CallRuntime("Internal.Runtime", _compilation.TypeSystemContext, ThreadStatics,
+ "GetThreadStaticBaseForType", new StackEntry[]
+ {
+ typeManagerSlotEntry,
+ tlsIndexExpressionEntry
+ });
+ return expressionEntry;
+ }
+
+ private LLVMValueRef RemoveFatOffset(LLVMBuilderRef builder, LLVMValueRef fatFunctionRef)
+ {
+ return LLVM.BuildAnd(builder,
+ CastIfNecessary(builder, fatFunctionRef, LLVMTypeRef.Int32Type()),
+ BuildConstUInt32(~(uint)_compilation.TypeSystemContext.Target.FatFunctionPointerOffset), "minusFatOffset");
+ }
}
}
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs
index 8bc152f841d..861507744bf 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs
@@ -42,7 +42,6 @@ public static void CompileMethod(WebAssemblyCodegenCompilation compilation, WebA
//CompileExternMethod(methodCodeNodeNeedingCode, method.GetPInvokeMethodMetadata().Name ?? method.Name);
//return;
}
-
var methodIL = compilation.GetMethodIL(method);
if (methodIL == null)
return;
@@ -62,7 +61,7 @@ public static void CompileMethod(WebAssemblyCodegenCompilation compilation, WebA
mangledName = compilation.NameMangler.GetMangledMethodName(methodCodeNodeNeedingCode.Method).ToString();
}
- ilImporter = new ILImporter(compilation, method, methodIL, mangledName);
+ ilImporter = new ILImporter(compilation, method, methodIL, mangledName, methodCodeNodeNeedingCode is WebAssemblyUnboxingThunkNode);
CompilerTypeSystemContext typeSystemContext = compilation.TypeSystemContext;
@@ -117,6 +116,12 @@ public static void CompileMethod(WebAssemblyCodegenCompilation compilation, WebA
static LLVMValueRef DoNothingFunction = default(LLVMValueRef);
static LLVMValueRef NullRefFunction = default(LLVMValueRef);
+ internal static LLVMValueRef MakeFatPointer(LLVMBuilderRef builder, LLVMValueRef targetLlvmFunction, WebAssemblyCodegenCompilation compilation)
+ {
+ var asInt = LLVM.BuildPtrToInt(builder, targetLlvmFunction, LLVMTypeRef.Int32Type(), "toInt");
+ return LLVM.BuildBinOp(builder, LLVMOpcode.LLVMOr, asInt, LLVM.ConstInt(LLVM.Int32Type(), (ulong)compilation.TypeSystemContext.Target.FatFunctionPointerOffset, LLVMMisc.False), "makeFat");
+ }
+
private static IList GetParameterNamesForMethod(MethodDesc method)
{
// TODO: The uses of this method need revision. The right way to get to this info is from
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
index 974358de2f4..a4ed58c1148 100644
--- a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
+++ b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs
@@ -6,7 +6,7 @@
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
-
+using ILCompiler;
using ILCompiler.DependencyAnalysisFramework;
using Internal.Text;
@@ -15,6 +15,8 @@
using LLVMSharp;
using ILCompiler.CodeGen;
+using ILCompiler.DependencyAnalysis;
+using Internal.IL;
namespace ILCompiler.DependencyAnalysis
{
@@ -69,7 +71,7 @@ private string GetBaseSymbolName(ISymbolNode symbol, NameMangler nameMangler, bo
}
}
- private static Dictionary s_symbolValues = new Dictionary();
+ public static Dictionary s_symbolValues = new Dictionary();
private static Dictionary s_staticFieldMapping = new Dictionary();
public static LLVMValueRef GetSymbolValuePointer(LLVMModuleRef module, ISymbolNode symbol, NameMangler nameMangler, bool objectWriterUse = false)
@@ -87,7 +89,7 @@ public static LLVMValueRef GetSymbolValuePointer(LLVMModuleRef module, ISymbolNo
}
var intPtrType = LLVM.PointerType(LLVM.Int32Type(), 0);
var myGlobal = LLVM.AddGlobalInAddressSpace(module, intPtrType, symbolAddressGlobalName, 0);
- LLVM.SetGlobalConstant(myGlobal, (LLVMBool)true);
+ LLVM.SetGlobalConstant(myGlobal, (LLVMBool) true);
LLVM.SetLinkage(myGlobal, LLVMLinkage.LLVMInternalLinkage);
s_symbolValues.Add(symbolAddressGlobalName, myGlobal);
return myGlobal;
@@ -184,7 +186,7 @@ public void FinishObjWriter()
#if DEBUG
LLVM.PrintModuleToFile(Module, Path.ChangeExtension(_objectFilePath, ".txt"), out string unused2);
#endif //DEBUG
- LLVM.VerifyModule(Module, LLVMVerifierFailureAction.LLVMAbortProcessAction, out string unused);
+ var res = LLVM.VerifyModule(Module, LLVMVerifierFailureAction.LLVMAbortProcessAction, out string unused);
LLVM.WriteBitcodeToFile(Module, _objectFilePath);
@@ -317,7 +319,7 @@ public void EnsureCurrentSection()
ArrayBuilder _currentObjectData = new ArrayBuilder();
struct SymbolRefData
{
- public SymbolRefData(bool isFunction, string symbolName, int offset)
+ public SymbolRefData(bool isFunction, string symbolName, uint offset)
{
IsFunction = isFunction;
SymbolName = symbolName;
@@ -326,7 +328,7 @@ public SymbolRefData(bool isFunction, string symbolName, int offset)
readonly bool IsFunction;
readonly string SymbolName;
- readonly int Offset;
+ readonly uint Offset;
public LLVMValueRef ToLLVMValueRef(LLVMModuleRef module)
{
@@ -336,7 +338,7 @@ public LLVMValueRef ToLLVMValueRef(LLVMModuleRef module)
{
var pointerType = LLVM.PointerType(LLVM.Int8Type(), 0);
var bitCast = LLVM.ConstBitCast(valRef, pointerType);
- LLVMValueRef[] index = new LLVMValueRef[] {LLVM.ConstInt(LLVM.Int32Type(), (uint)Offset, (LLVMBool)false)};
+ LLVMValueRef[] index = new LLVMValueRef[] {LLVM.ConstInt(LLVM.Int32Type(), Offset, (LLVMBool)false)};
valRef = LLVM.ConstGEP(bitCast, index);
}
@@ -422,7 +424,9 @@ public void DoneObjectNode()
string realName = GetBaseSymbolName(symNode, _nodeFactory.NameMangler, true);
var intPtrType = LLVM.PointerType(LLVM.Int32Type(), 0);
+
var arrayglobal = LLVM.AddGlobalInAddressSpace(Module, LLVM.ArrayType(intPtrType, (uint)countOfPointerSizedElements), realName, 0);
+
LLVM.SetLinkage(arrayglobal, LLVMLinkage.LLVMExternalLinkage);
_dataToFill.Add(new ObjectNodeDataEmission(arrayglobal, _currentObjectData.ToArray(), _currentObjectSymbolRefs));
@@ -509,15 +513,13 @@ public int EmitSymbolRef(string realSymbolName, int offsetFromSymbolName, bool i
relocType = RelocType.IMAGE_REL_BASED_REL32;
delta = checked(delta + sizeof(int));
}
-
- int totalOffset = checked(delta + offsetFromSymbolName);
+ uint totalOffset = checked((uint)delta + unchecked((uint)offsetFromSymbolName));
EmitBlob(new byte[this._nodeFactory.Target.PointerSize]);
if (relocType == RelocType.IMAGE_REL_BASED_REL32)
{
return this._nodeFactory.Target.PointerSize;
}
-
_currentObjectSymbolRefs.Add(symbolStartOffset, new SymbolRefData(isFunction, realSymbolName, totalOffset));
return _nodeFactory.Target.PointerSize;
}
@@ -572,7 +574,8 @@ public int EmitSymbolReference(ISymbolNode target, int delta, RelocType relocTyp
EmitBlob(new byte[pointerSize]);
return pointerSize;
}
- int offsetFromBase = GetNumericOffsetFromBaseSymbolValue(target);
+ int offsetFromBase = GetNumericOffsetFromBaseSymbolValue(target) + target.Offset;
+
return EmitSymbolRef(realSymbolName, offsetFromBase, target is WebAssemblyMethodCodeNode, relocType, delta);
}
@@ -654,7 +657,6 @@ public void EmitSymbolDefinition(int currentOffset)
//System.IO.FileStream _file;
string _objectFilePath;
-
public WebAssemblyObjectWriter(string objectFilePath, NodeFactory factory, WebAssemblyCodegenCompilation compilation)
{
_nodeFactory = factory;
@@ -755,6 +757,12 @@ public static void EmitObject(string objectFilePath, IEnumerable
if (node.ShouldSkipEmittingObjectNode(factory))
continue;
+ if (node is ReadyToRunGenericHelperNode readyToRunGenericHelperNode)
+ {
+ objectWriter.GetCodeForReadyToRunGenericHelper(compilation, readyToRunGenericHelperNode, factory);
+ continue;
+ }
+
objectWriter.StartObjectNode(node);
ObjectData nodeContents = node.GetData(factory);
@@ -914,5 +922,257 @@ WebAssembly has no thumb
}
}
}
+
+ private void GetCodeForReadyToRunGenericHelper(WebAssemblyCodegenCompilation compilation, ReadyToRunGenericHelperNode node, NodeFactory factory)
+ {
+ LLVMBuilderRef builder = LLVM.CreateBuilder();
+ var args = new List();
+ MethodDesc delegateCtor = null;
+ if (node.Id == ReadyToRunHelperId.DelegateCtor)
+ {
+ DelegateCreationInfo target = (DelegateCreationInfo)node.Target;
+ delegateCtor = target.Constructor.Method;
+ bool isStatic = delegateCtor.Signature.IsStatic;
+ int argCount = delegateCtor.Signature.Length;
+ if (!isStatic) argCount++;
+ for (int i = 0; i < argCount; i++)
+ {
+ TypeDesc argType;
+ if (i == 0 && !isStatic)
+ {
+ argType = delegateCtor.OwningType;
+ }
+ else
+ {
+ argType = delegateCtor.Signature[i - (isStatic ? 0 : 1)];
+ }
+ args.Add(ILImporter.GetLLVMTypeForTypeDesc(argType));
+ }
+ }
+
+ LLVMValueRef helperFunc = LLVM.GetNamedFunction(Module, node.GetMangledName(factory.NameMangler));
+
+ if (helperFunc.Pointer == IntPtr.Zero)
+ {
+ throw new Exception("if the function is requested here, it should have been created earlier");
+ }
+ var helperBlock = LLVM.AppendBasicBlock(helperFunc, "genericHelper");
+ LLVM.PositionBuilderAtEnd(builder, helperBlock);
+ var importer = new ILImporter(builder, compilation, Module, helperFunc, delegateCtor);
+ LLVMValueRef ctx;
+ string gepName;
+ if (node is ReadyToRunGenericLookupFromTypeNode)
+ {
+ // Locate the VTable slot that points to the dictionary
+ int vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)node.DictionaryOwner);
+
+ int pointerSize = factory.Target.PointerSize;
+ // Load the dictionary pointer from the VTable
+ int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize);
+ var slotGep = LLVM.BuildGEP(builder, LLVM.GetParam(helperFunc, 1), new[] {LLVM.ConstInt(LLVM.Int32Type(), (ulong)slotOffset, LLVMMisc.False)}, "slotGep");
+ var slotGepPtrPtr = LLVM.BuildPointerCast(builder, slotGep,
+ LLVMTypeRef.PointerType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), 0), "slotGepPtrPtr");
+ ctx = LLVM.BuildLoad(builder, slotGepPtrPtr, "dictGep");
+ gepName = "typeNodeGep";
+ }
+ else
+ {
+ ctx = LLVM.GetParam(helperFunc, 1);
+ gepName = "paramGep";
+ }
+
+ LLVMValueRef resVar = OutputCodeForDictionaryLookup(builder, factory, node, node.LookupSignature, ctx, gepName);
+
+ switch (node.Id)
+ {
+ case ReadyToRunHelperId.GetNonGCStaticBase:
+ {
+ MetadataType target = (MetadataType)node.Target;
+
+ if (compilation.TypeSystemContext.HasLazyStaticConstructor(target))
+ {
+ importer.OutputCodeForTriggerCctor(target, resVar);
+ }
+ }
+ break;
+
+ case ReadyToRunHelperId.GetGCStaticBase:
+ {
+ MetadataType target = (MetadataType)node.Target;
+
+ var ptrPtrPtr = LLVM.BuildBitCast(builder, resVar, LLVMTypeRef.PointerType(LLVMTypeRef.PointerType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), 0), 0), "ptrPtrPtr");
+
+ resVar = LLVM.BuildLoad(builder, LLVM.BuildLoad(builder, ptrPtrPtr, "ind1"), "ind2");
+
+ if (compilation.TypeSystemContext.HasLazyStaticConstructor(target))
+ {
+ GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
+ var nonGcStaticsBase = OutputCodeForDictionaryLookup(builder, factory, node, nonGcRegionLookup, ctx, "lazyGep");
+ importer.OutputCodeForTriggerCctor(target, nonGcStaticsBase);
+ }
+ }
+ break;
+
+ case ReadyToRunHelperId.GetThreadStaticBase:
+ {
+ MetadataType target = (MetadataType)node.Target;
+
+ if (compilation.TypeSystemContext.HasLazyStaticConstructor(target))
+ {
+ GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
+ var threadStaticBase = OutputCodeForDictionaryLookup(builder, factory, node, nonGcRegionLookup, ctx, "tsGep");
+ importer.OutputCodeForTriggerCctor(target, threadStaticBase);
+ }
+ resVar = importer.OutputCodeForGetThreadStaticBaseForType(resVar).ValueAsType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), builder);
+ }
+ break;
+
+ case ReadyToRunHelperId.DelegateCtor:
+ {
+ DelegateCreationInfo target = (DelegateCreationInfo)node.Target;
+ MethodDesc constructor = target.Constructor.Method;
+ var fatPtr = ILImporter.MakeFatPointer(builder, resVar, compilation);
+ importer.OutputCodeForDelegateCtorInit(builder, helperFunc, constructor, fatPtr);
+ }
+ break;
+
+ // These are all simple: just get the thing from the dictionary and we're done
+ case ReadyToRunHelperId.TypeHandle:
+ case ReadyToRunHelperId.MethodHandle:
+ case ReadyToRunHelperId.FieldHandle:
+ case ReadyToRunHelperId.MethodDictionary:
+ case ReadyToRunHelperId.MethodEntry:
+ case ReadyToRunHelperId.VirtualDispatchCell:
+ case ReadyToRunHelperId.DefaultConstructor:
+ break;
+ default:
+ throw new NotImplementedException();
+ }
+
+ if (node.Id != ReadyToRunHelperId.DelegateCtor)
+ {
+ LLVM.BuildRet(builder, resVar);
+ }
+ else
+ {
+ LLVM.BuildRetVoid(builder);
+ }
+ }
+
+ private LLVMValueRef OutputCodeForDictionaryLookup(LLVMBuilderRef builder, NodeFactory factory,
+ ReadyToRunGenericHelperNode node, GenericLookupResult lookup, LLVMValueRef ctx, string gepName)
+ {
+ // Find the generic dictionary slot
+ int dictionarySlot = factory.GenericDictionaryLayout(node.DictionaryOwner).GetSlotForEntry(lookup);
+ int offset = dictionarySlot * factory.Target.PointerSize;
+
+ // Load the generic dictionary cell
+ LLVMValueRef retGep = LLVM.BuildGEP(builder, ctx, new[] {LLVM.ConstInt(LLVM.Int32Type(), (ulong)offset, LLVMMisc.False)}, "retGep");
+ LLVMValueRef castGep = LLVM.BuildBitCast(builder, retGep, LLVMTypeRef.PointerType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), 0), "ptrPtr");
+ LLVMValueRef retRef = LLVM.BuildLoad(builder, castGep, gepName);
+
+ switch (lookup.LookupResultReferenceType(factory))
+ {
+ case GenericLookupResultReferenceType.Indirect:
+ var ptrPtr = LLVM.BuildBitCast(builder, retRef, LLVMTypeRef.PointerType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), 0), "ptrPtr");
+ retRef = LLVM.BuildLoad(builder, ptrPtr, "indLoad");
+ break;
+
+ case GenericLookupResultReferenceType.ConditionalIndirect:
+ throw new NotImplementedException();
+
+ default:
+ break;
+ }
+
+ return retRef;
+ }
+
+ }
+}
+
+namespace Internal.IL
+{
+ partial class ILImporter
+ {
+ public ILImporter(LLVMBuilderRef builder, WebAssemblyCodegenCompilation compilation, LLVMModuleRef module, LLVMValueRef helperFunc, MethodDesc delegateCtor)
+ {
+ this._builder = builder;
+ this._compilation = compilation;
+ this.Module = module;
+ this._currentFunclet = helperFunc;
+ _locals = new LocalVariableDefinition[0];
+ if (delegateCtor == null)
+ {
+ _signature = new MethodSignature(MethodSignatureFlags.None, 0, GetWellKnownType(WellKnownType.Void),
+ new TypeDesc[0]);
+ }
+ else
+ {
+ _signature = delegateCtor.Signature;
+ _argSlots = new LLVMValueRef[_signature.Length];
+ int signatureIndex = 2; // past hidden param
+ int thisOffset = 0;
+ if (!_signature.IsStatic)
+ {
+ thisOffset = 1;
+ }
+ for (int i = 0; i < _signature.Length; i++)
+ {
+ if (CanStoreTypeOnStack(_signature[i]))
+ {
+ LLVMValueRef storageAddr;
+ LLVMValueRef argValue = LLVM.GetParam(helperFunc, (uint)signatureIndex);
+
+ // The caller will always pass the argument on the stack. If this function doesn't have
+ // EH, we can put it in an alloca for efficiency and better debugging. Otherwise,
+ // copy it to the shadow stack so funclets can find it
+ int argOffset = i + thisOffset;
+ string argName = $"arg{argOffset}_";
+ storageAddr = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_signature[i]), argName);
+ _argSlots[i] = storageAddr;
+ LLVM.BuildStore(_builder, argValue, storageAddr);
+ signatureIndex++;
+ }
+ }
+ }
+ _thisType = GetWellKnownType(WellKnownType.Void);
+ _pointerSize = compilation.NodeFactory.Target.PointerSize;
+ _exceptionRegions = new ExceptionRegion[0];
+ }
+
+ internal void OutputCodeForTriggerCctor(TypeDesc type, LLVMValueRef staticBaseValueRef)
+ {
+ IMethodNode helperNode = (IMethodNode)_compilation.NodeFactory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase);
+ TriggerCctor((MetadataType)helperNode.Method.OwningType, staticBaseValueRef, helperNode.Method.Name);
+ }
+
+ public void OutputCodeForDelegateCtorInit(LLVMBuilderRef builder, LLVMValueRef helperFunc,
+ MethodDesc constructor,
+ LLVMValueRef fatFunction)
+ {
+ StackEntry[] argValues = new StackEntry [constructor.Signature.Length + 1]; // for delegate this
+ var shadowStack = LLVM.GetFirstParam(helperFunc);
+ argValues[0] = new LoadExpressionEntry(StackValueKind.ObjRef, "this", shadowStack, GetWellKnownType(WellKnownType.Object));
+ for (var i = 0; i < constructor.Signature.Length; i++)
+ {
+ if (i == 1)
+ {
+ argValues[i + 1] = new ExpressionEntry(StackValueKind.Int32, "arg" + (i + 1),
+ LLVM.BuildIntToPtr(builder, fatFunction, LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), "toPtr"),
+ GetWellKnownType(WellKnownType.IntPtr));
+ }
+ else
+ {
+ var argRef = LoadVarAddress(i + 1, LocalVarKind.Argument, out TypeDesc type);
+ var ptrPtr = LLVM.BuildPointerCast(builder, argRef,
+ LLVMTypeRef.PointerType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), 0), "ptrPtr");
+ var loadArg = LLVM.BuildLoad(builder, ptrPtr, "arg" + (i + 1));
+ argValues[i + 1] = new ExpressionEntry(GetStackValueKind(constructor.Signature[i]), "arg" + i + 1, loadArg,
+ constructor.Signature[i]);
+ }
+ }
+ HandleCall(constructor, constructor.Signature, constructor, argValues, null);
+ }
}
}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs
index 31bdafec6dd..c7111cd28aa 100644
--- a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs
+++ b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs
@@ -34,6 +34,13 @@ public WebAssemblyCodegenNodeFactory(CompilerTypeSystemContext context, Compilat
protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method)
{
+ if (method.IsInternalCall)
+ {
+ if (TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(method))
+ {
+ return MethodEntrypoint(TypeSystemContext.GetRealSpecialUnboxingThunkTargetMethod(method));
+ }
+ }
if (CompilationModuleGroup.ContainsMethodBody(method, false))
{
return new WebAssemblyMethodBodyNode(method);
@@ -51,12 +58,34 @@ public WebAssemblyVTableSlotNode VTableSlot(MethodDesc method)
protected override IMethodNode CreateUnboxingStubNode(MethodDesc method)
{
- return new WebAssemblyUnboxingThunkNode(TypeSystemContext.GetUnboxingThunk(method, TypeSystemContext.GeneratedAssembly));
+ if (method.IsCanonicalMethod(CanonicalFormKind.Specific) && !method.HasInstantiation)
+ {
+ // Unboxing stubs to canonical instance methods need a special unboxing stub that unboxes
+ // 'this' and also provides an instantiation argument (we do a calling convention conversion).
+ // We don't do this for generic instance methods though because they don't use the EEType
+ // for the generic context anyway.
+ return new WebAssemblyMethodBodyNode(TypeSystemContext.GetSpecialUnboxingThunk(method, TypeSystemContext.GeneratedAssembly));
+ }
+ else
+ {
+ // Otherwise we just unbox 'this' and don't touch anything else.
+ return new WebAssemblyUnboxingThunkNode(TypeSystemContext.GetUnboxingThunk(method, TypeSystemContext.GeneratedAssembly));
+ }
}
protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall)
{
throw new NotSupportedException();
}
+
+ protected override ISymbolNode CreateGenericLookupFromDictionaryNode(ReadyToRunGenericHelperKey helperKey)
+ {
+ return new WebAssemblyReadyToRunGenericLookupFromDictionaryNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner);
+ }
+
+ protected override ISymbolNode CreateGenericLookupFromTypeNode(ReadyToRunGenericHelperKey helperKey)
+ {
+ return new WebAssemblyReadyToRunGenericLookupFromTypeNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner);
+ }
}
}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyMethodCodeNode.cs b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyMethodCodeNode.cs
index 89385811155..43312c5187d 100644
--- a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyMethodCodeNode.cs
+++ b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyMethodCodeNode.cs
@@ -85,31 +85,4 @@ int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer)
return comparer.Compare(_method, ((WebAssemblyMethodBodyNode)other)._method);
}
}
-
- internal class WebAssemblyUnboxingThunkNode : WebAssemblyMethodCodeNode, IMethodNode
- {
- public WebAssemblyUnboxingThunkNode(MethodDesc method)
- : base(method)
- {
- }
-
- protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
-
- public override IEnumerable GetStaticDependencies(NodeFactory factory)
- {
- var dependencies = new DependencyList();
-
- foreach (Object node in _dependencies)
- dependencies.Add(node, "Wasm code ");
-
- return dependencies;
- }
-
- int ISortableNode.ClassCode => -18942467;
-
- int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer)
- {
- return comparer.Compare(_method, ((WebAssemblyUnboxingThunkNode)other)._method);
- }
- }
}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyReadyToRunGenericLookupFromDictionaryNode.cs b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyReadyToRunGenericLookupFromDictionaryNode.cs
new file mode 100644
index 00000000000..fd76656dd81
--- /dev/null
+++ b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyReadyToRunGenericLookupFromDictionaryNode.cs
@@ -0,0 +1,22 @@
+// 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
+{
+ internal class WebAssemblyReadyToRunGenericLookupFromDictionaryNode : ReadyToRunGenericLookupFromDictionaryNode
+ {
+ public WebAssemblyReadyToRunGenericLookupFromDictionaryNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner)
+ : base(factory, helperId, target, dictionaryOwner)
+ {
+ }
+
+ public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
+ {
+ // this code for this node is written out in WebAssemblyObjectWriter.GetCodeForReadyToRunGenericHelper
+ return new ObjectData(new byte[0], new Relocation[0], 1, new ISymbolDefinitionNode[0]);
+ }
+ }
+}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyReadyToRunGenericLookupFromTypeNode.cs b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyReadyToRunGenericLookupFromTypeNode.cs
new file mode 100644
index 00000000000..776ed98e6d2
--- /dev/null
+++ b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyReadyToRunGenericLookupFromTypeNode.cs
@@ -0,0 +1,22 @@
+// 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
+{
+ internal class WebAssemblyReadyToRunGenericLookupFromTypeNode : ReadyToRunGenericLookupFromTypeNode
+ {
+ public WebAssemblyReadyToRunGenericLookupFromTypeNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner)
+ : base(factory, helperId, target, dictionaryOwner)
+ {
+ }
+
+ public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
+ {
+ // this code for this node is written out in WebAssemblyObjectWriter.GetCodeForReadyToRunGenericHelper
+ return new ObjectData(new byte[0], new Relocation[0], 1, new ISymbolDefinitionNode[0]);
+ }
+ }
+}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyUnboxingThunkNode.cs b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyUnboxingThunkNode.cs
new file mode 100644
index 00000000000..5106a7dcdd9
--- /dev/null
+++ b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyUnboxingThunkNode.cs
@@ -0,0 +1,37 @@
+// 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;
+using Internal.TypeSystem;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ internal class WebAssemblyUnboxingThunkNode : WebAssemblyMethodCodeNode, IMethodNode
+ {
+ public WebAssemblyUnboxingThunkNode(MethodDesc method)
+ : base(method)
+ {
+ }
+
+ protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
+
+ public override IEnumerable GetStaticDependencies(NodeFactory factory)
+ {
+ var dependencies = new DependencyList();
+
+ foreach (Object node in _dependencies)
+ dependencies.Add(node, "Wasm code ");
+
+ return dependencies;
+ }
+
+ int ISortableNode.ClassCode => -18942467;
+
+ int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer)
+ {
+ return comparer.Compare(_method, ((WebAssemblyUnboxingThunkNode)other)._method);
+ }
+ }
+}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs
index bd3cf50b698..f5799801813 100644
--- a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs
+++ b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs
@@ -17,8 +17,8 @@ public class WebAssemblyVTableSlotNode : ObjectNode, ISymbolDefinitionNode
public WebAssemblyVTableSlotNode(MethodDesc targetMethod)
{
Debug.Assert(targetMethod.IsVirtual);
- Debug.Assert(!targetMethod.IsSharedByGenericInstantiations);
Debug.Assert(!targetMethod.HasInstantiation);
+ Debug.Assert(!targetMethod.IsRuntimeDeterminedExactMethod);
_targetMethod = targetMethod;
}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
index eee951f3030..aa640fa6f2a 100644
--- a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
+++ b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
@@ -56,10 +56,45 @@ protected override void CompileInternal(string outputFile, ObjectDumper dumper)
protected override void ComputeDependencyNodeDependencies(List> obj)
{
- foreach (WebAssemblyMethodCodeNode methodCodeNodeNeedingCode in obj)
+ foreach (var dependency in obj)
{
+ var methodCodeNodeNeedingCode = dependency as WebAssemblyMethodCodeNode;
+ if (methodCodeNodeNeedingCode == null)
+ {
+ // To compute dependencies of the shadow method that tracks dictionary
+ // dependencies we need to ensure there is code for the canonical method body.
+ var dependencyMethod = (ShadowConcreteMethodNode)dependency;
+ methodCodeNodeNeedingCode = (WebAssemblyMethodCodeNode)dependencyMethod.CanonicalMethodNode;
+ }
+
+ // We might have already compiled this method.
+ if (methodCodeNodeNeedingCode.StaticDependenciesAreComputed)
+ continue;
+
ILImporter.CompileMethod(this, methodCodeNodeNeedingCode);
}
}
+
+ public TypeDesc ConvertToCanonFormIfNecessary(TypeDesc type, CanonicalFormKind policy)
+ {
+ if (!type.IsCanonicalSubtype(CanonicalFormKind.Any))
+ return type;
+
+ if (type.IsPointer || type.IsByRef)
+ {
+ ParameterizedType parameterizedType = (ParameterizedType)type;
+ TypeDesc paramTypeConverted = ConvertToCanonFormIfNecessary(parameterizedType.ParameterType, policy);
+ if (paramTypeConverted == parameterizedType.ParameterType)
+ return type;
+
+ if (type.IsPointer)
+ return TypeSystemContext.GetPointerType(paramTypeConverted);
+
+ if (type.IsByRef)
+ return TypeSystemContext.GetByRefType(paramTypeConverted);
+ }
+
+ return type.ConvertToCanonForm(policy);
+ }
}
}
diff --git a/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj b/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj
index 5860d460439..f25b88e2df3 100644
--- a/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj
+++ b/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj
@@ -47,6 +47,9 @@
+
+
+
diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs
index 1e8b453028e..223241b6166 100644
--- a/src/ILCompiler/src/Program.cs
+++ b/src/ILCompiler/src/Program.cs
@@ -46,7 +46,6 @@ internal class Program
private bool _multiFile;
private bool _nativeLib;
private string _exportsFile;
- private bool _useSharedGenerics;
private bool _useScanner;
private bool _noScanner;
private bool _emitStackTraceData;
@@ -169,7 +168,6 @@ private ArgumentSyntax ParseCommandLine(string[] args)
syntax.DefineOption("systemmodule", ref _systemModuleName, "System module name (default: System.Private.CoreLib)");
syntax.DefineOption("multifile", ref _multiFile, "Compile only input files (do not compile referenced assemblies)");
syntax.DefineOption("waitfordebugger", ref waitForDebugger, "Pause to give opportunity to attach debugger");
- syntax.DefineOption("usesharedgenerics", ref _useSharedGenerics, "Enable shared generics");
syntax.DefineOptionList("codegenopt", ref _codegenOptions, "Define a codegen option");
syntax.DefineOptionList("rdxml", ref _rdXmlFilePaths, "RD.XML file(s) for compilation");
syntax.DefineOption("rootallapplicationassemblies", ref _rootAllApplicationAssemblies, "Consider all non-framework assemblies dynamically used");
@@ -311,8 +309,7 @@ private int Run(string[] args)
// Initialize type system context
//
- SharedGenericsMode genericsMode = _useSharedGenerics || !_isWasmCodegen ?
- SharedGenericsMode.CanonicalReferenceTypes : SharedGenericsMode.Disabled;
+ SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes;
// TODO: compiler switch for SIMD support?
var simdVectorLength = (_isCppCodegen || _isWasmCodegen) ? SimdVectorLength.None : SimdVectorLength.Vector128Bit;
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs
index 81d8a5b4c51..bf67f377a1e 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs
@@ -12,6 +12,12 @@ namespace Internal.Runtime.CompilerServices
[System.Runtime.CompilerServices.ReflectionBlocked]
public static class FunctionPointerOps
{
+#if WASM
+ private const int FatFunctionPointerOffset = 1 << 31;
+#else
+ private const int FatFunctionPointerOffset = 2;
+#endif
+
private struct GenericMethodDescriptorInfo : IEquatable
{
public override bool Equals(object obj)
@@ -118,7 +124,7 @@ public static unsafe IntPtr GetGenericMethodFunctionPointer(IntPtr canonFunction
System.Diagnostics.Debug.Assert(canonFunctionPointer == genericFunctionPointer->MethodFunctionPointer);
System.Diagnostics.Debug.Assert(instantiationArgument == genericFunctionPointer->InstantiationArgument);
- return (IntPtr)((byte*)genericFunctionPointer + FatFunctionPointerConstants.Offset);
+ return (IntPtr)((byte*)genericFunctionPointer + FatFunctionPointerOffset);
}
}
@@ -126,9 +132,9 @@ public static unsafe bool IsGenericMethodPointer(IntPtr functionPointer)
{
// Check the low bit to find out what kind of function pointer we have here.
#if BIT64
- if ((functionPointer.ToInt64() & FatFunctionPointerConstants.Offset) == FatFunctionPointerConstants.Offset)
+ if ((functionPointer.ToInt64() & FatFunctionPointerOffset) == FatFunctionPointerOffset)
#else
- if ((functionPointer.ToInt32() & FatFunctionPointerConstants.Offset) == FatFunctionPointerConstants.Offset)
+ if ((functionPointer.ToInt32() & FatFunctionPointerOffset) == FatFunctionPointerOffset)
#endif
{
return true;
@@ -139,7 +145,7 @@ public static unsafe bool IsGenericMethodPointer(IntPtr functionPointer)
[CLSCompliant(false)]
public static unsafe GenericMethodDescriptor* ConvertToGenericDescriptor(IntPtr functionPointer)
{
- return (GenericMethodDescriptor*)((byte*)functionPointer - FatFunctionPointerConstants.Offset);
+ return (GenericMethodDescriptor*)((byte*)functionPointer - FatFunctionPointerOffset);
}
public static unsafe bool Compare(IntPtr functionPointerA, IntPtr functionPointerB)
diff --git a/tests/src/Simple/Generics/Generics.cmd b/tests/src/Simple/Generics/Generics.cmd
index 2167b8ecfe3..872d0fa257e 100644
--- a/tests/src/Simple/Generics/Generics.cmd
+++ b/tests/src/Simple/Generics/Generics.cmd
@@ -1,6 +1,12 @@
@echo off
setlocal
-"%1\%2"
+
+IF /i "%__Mode%"=="wasm" (
+ call emrun --browser=firefox --browser_args=-headless --safe_firefox_profile --silence_timeout 100 "%1\%2"
+) ELSE (
+ "%1\%2"
+)
+
set ErrorCode=%ERRORLEVEL%
IF "%ErrorCode%"=="100" (
echo %~n0: pass
diff --git a/tests/src/Simple/Generics/Generics.cs b/tests/src/Simple/Generics/Generics.cs
index ad19a63d62d..d49ec913c09 100644
--- a/tests/src/Simple/Generics/Generics.cs
+++ b/tests/src/Simple/Generics/Generics.cs
@@ -6,6 +6,10 @@
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+#if CODEGEN_WASM
+using System.Runtime.InteropServices;
+using Console=Program.Console;
+#endif
class Program
{
@@ -33,11 +37,13 @@ static int Main()
TestFieldAccess.Run();
TestDevirtualization.Run();
TestGenericInlining.Run();
-#if !CODEGEN_CPP
+#if !CODEGEN_CPP
+#if !CODEGEN_WASM
TestNullableCasting.Run();
TestVariantCasting.Run();
TestMDArrayAddressMethod.Run();
TestNativeLayoutGeneration.Run();
+#endif
TestByRefLikeVTables.Run();
#endif
return 100;
@@ -2452,4 +2458,38 @@ public static void Run()
throw new Exception();
}
}
+
+#if CODEGEN_WASM
+ internal class Console
+ {
+ private static unsafe void PrintString(string s)
+ {
+ int length = s.Length;
+ fixed (char* curChar = s)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ TwoByteStr curCharStr = new TwoByteStr();
+ curCharStr.first = (byte)(*(curChar + i));
+ printf((byte*)&curCharStr, null);
+ }
+ }
+ }
+
+ internal static void WriteLine(string s)
+ {
+ PrintString(s);
+ PrintString("\n");
+ }
+ }
+
+ struct TwoByteStr
+ {
+ public byte first;
+ public byte second;
+ }
+
+ [DllImport("*")]
+ private static unsafe extern int printf(byte* str, byte* unused);
+#endif
}
diff --git a/tests/src/Simple/Generics/wasm b/tests/src/Simple/Generics/wasm
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/src/Simple/HelloWasm/Program.cs b/tests/src/Simple/HelloWasm/Program.cs
index dba81fb5dab..435d1a59556 100644
--- a/tests/src/Simple/HelloWasm/Program.cs
+++ b/tests/src/Simple/HelloWasm/Program.cs
@@ -21,7 +21,12 @@ internal static class Program
private static unsafe int Main(string[] args)
{
Success = true;
- PrintLine("Starting");
+ PrintLine("Starting " + 1);
+
+ TestBox();
+
+ TestSByteExtend();
+ TestMetaData();
Add(1, 2);
PrintLine("Hello from C#!");
@@ -265,8 +270,6 @@ private static unsafe int Main(string[] args)
TestArrayItfDispatch();
- TestMetaData();
-
TestTryFinally();
StartTest("RVA static field test");
@@ -304,10 +307,20 @@ private static unsafe int Main(string[] args)
TestSByteExtend();
+ TestSharedDelegate();
+
TestUlongUintMultiply();
TestBoxSingle();
-
+
+ TestGvmCallInIf(new GenDerived(), "hello");
+
+ TestStoreFromGenericMethod();
+
+ TestConstrainedValueTypeCallVirt();
+
+ TestBoxToGenericTypeFromDirectMethod();
+
TestInitializeArray();
// This test should remain last to get other results before stopping the debugger
@@ -347,6 +360,18 @@ internal static void FailTest(string failMessage = null)
if (failMessage != null) PrintLine(failMessage + "-");
}
+ private static void TestBox()
+ {
+ StartTest("Box int test");
+ object o = (Int32)1;
+ string virtCallRes = o.ToString();
+ PrintLine(virtCallRes);
+ var i = (int)o;
+ PrintLine("i");
+ PrintLine(i.ToString());
+ EndTest(virtCallRes == "1");
+ }
+
private static int StaticDelegateTarget()
{
return 7;
@@ -773,6 +798,16 @@ private static void TestMetaData()
instance.ReturnTrueIf1AndThis(0, null); // force method output
ClassForMetaTests.ReturnsParam(null); // force method output
+ NewMethod(classForMetaTestsType, instance);
+
+ StartTest("Class get+invoke static method with ref param via reflection");
+ var staticMtd = classForMetaTestsType.GetMethod("ReturnsParam");
+ var retVal = (ClassForMetaTests)staticMtd.Invoke(null, new object[] { instance });
+ EndTest(Object.ReferenceEquals(retVal, instance));
+ }
+
+ private static void NewMethod(Type classForMetaTestsType, ClassForMetaTests instance)
+ {
StartTest("Class get+invoke simple method via reflection");
var mtd = classForMetaTestsType.GetMethod("ReturnTrueIf1");
bool shouldBeTrue = (bool)mtd.Invoke(instance, new object[] { 1 });
@@ -785,10 +820,6 @@ private static void TestMetaData()
shouldBeFalse = (bool)mtdWith2Params.Invoke(instance, new object[] { 1, new ClassForMetaTests() });
EndTest(shouldBeTrue && !shouldBeFalse);
- StartTest("Class get+invoke static method with ref param via reflection");
- var staticMtd = classForMetaTestsType.GetMethod("ReturnsParam");
- var retVal = (ClassForMetaTests)staticMtd.Invoke(null, new object[] { instance });
- EndTest(Object.ReferenceEquals(retVal, instance));
}
public class ClassForMetaTests
@@ -862,6 +893,33 @@ private static uint TryFinallyInner()
return result;
}
+ class GenBase
+ {
+ public virtual string GMethod1(T t1, T t2) { return "GenBase<" + typeof(A) + ">.GMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; }
+ }
+ class GenDerived : GenBase
+ {
+ public override string GMethod1(T t1, T t2) { return "GenDerived<" + typeof(A) + ">.GMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; }
+ }
+
+ private static void TestGvmCallInIf(GenBase g, T p)
+ {
+ var i = 1;
+ if (i == 1)
+ {
+ g.GMethod1(p, p);
+ }
+ }
+
+ private static void TestStoreFromGenericMethod()
+ {
+ StartTest("TestStoreFromGenericMethod");
+ var values = new string[1];
+ // testing that the generic return value type from the function can be stored in a concrete type
+ values = values.AsSpan(0, 1).ToArray();
+ PassTest();
+ }
+
private static void TestCallToGenericInterfaceMethod()
{
StartTest("Call generic method on interface test");
@@ -871,6 +929,43 @@ private static void TestCallToGenericInterfaceMethod()
EndTest(true);
}
+ private static void TestConstrainedValueTypeCallVirt()
+ {
+ StartTest("Call constrained callvirt");
+ //TODO: create simpler test that doesn't need Dictionary<>/KVP<>/Span
+ var dict = new Dictionary, string>();
+ var notContainsKey = dict.ContainsKey(new KeyValuePair());
+
+ EndTest(!notContainsKey);
+ }
+
+ private static void TestBoxToGenericTypeFromDirectMethod()
+ {
+ StartTest("Callvirt on generic struct boxing to looked up generic type");
+
+ new GetHashCodeCaller, string>().CallValueTypeGetHashCodeFromGeneric(new GenStruct(""));
+
+ PassTest();
+ }
+
+ public struct GenStruct
+ {
+ private TKey key;
+
+ public GenStruct(TKey key)
+ {
+ this.key = key;
+ }
+ }
+
+ public class GetHashCodeCaller
+ {
+ public void CallValueTypeGetHashCodeFromGeneric(TKey k)
+ {
+ k.GetHashCode();
+ }
+ }
+
public interface ITestGenItf
{
bool Log(TState state);
@@ -1015,7 +1110,7 @@ private static void TestInitObjDouble()
EndTest(strt.DoubleField == 0d);
}
- private static void TestSByteExtend()
+ private static unsafe void TestSByteExtend()
{
StartTest("SByte extend");
sbyte s = -1;
@@ -1054,7 +1149,22 @@ private static void TestSByteExtend()
}
StartTest("Negative SByte br");
- EndTest(ILHelpers.ILHelpersTest.BneSbyteExtend());
+ if (s == -1) // this only creates the bne opcode, which it is testing, in Release mode.
+ {
+ PassTest();
+ }
+ else
+ {
+ FailTest();
+ }
+ }
+
+ public static void TestSharedDelegate()
+ {
+ StartTest("Shared Delegate");
+ var shouldBeFalse = SampleClassWithGenericDelegate.CallDelegate(new object[0]);
+ var shouldBeTrue = SampleClassWithGenericDelegate.CallDelegate(new object[1]);
+ EndTest(!shouldBeFalse && shouldBeTrue);
}
internal static void TestUlongUintMultiply()
@@ -1066,6 +1176,14 @@ internal static void TestUlongUintMultiply()
EndTest(f == 0x100000000);
}
+ internal static void TestBoxSingle()
+ {
+ StartTest("Test box single");
+ var fi = typeof(ClassWithFloat).GetField("F");
+ fi.SetValue(null, 1.1f);
+ EndTest(1.1f == ClassWithFloat.F);
+ }
+
static void TestInitializeArray()
{
StartTest("Test InitializeArray");
@@ -1097,15 +1215,7 @@ static void TestInitializeArray()
PassTest();
}
- internal static void TestBoxSingle()
- {
- StartTest("Test box single");
- var fi = typeof(ClassWithFloat).GetField("F");
- fi.SetValue(null, 1.1f);
- EndTest(1.1f == ClassWithFloat.F);
- }
-
- [DllImport("*")]
+ [DllImport("*")]
private static unsafe extern int printf(byte* str, byte* unused);
}
@@ -1114,6 +1224,39 @@ public class ClassWithFloat
public static float F;
}
+public class SampleClassWithGenericDelegate
+{
+ public static bool CallDelegate(T[] items)
+ {
+ return new Stack(items).CallDelegate(DoWork);
+ }
+
+ public static bool DoWork(T[] items)
+ {
+ Program.PrintLine("DoWork");
+ return items.Length > 0;
+ }
+}
+
+public class Stack
+{
+ T[] items;
+
+ public Stack(T[] items)
+ {
+ this.items = items;
+ }
+
+ public bool CallDelegate(StackDelegate d)
+ {
+ Program.PrintLine("CallDelegate");
+ Program.PrintLine(items.Length.ToString());
+ return d(items);
+ }
+
+ public delegate bool StackDelegate(T[] items);
+}
+
public struct TwoByteStr
{
public byte first;
diff --git a/tests/src/Simple/SimpleTest.targets b/tests/src/Simple/SimpleTest.targets
index 426c2115f83..0be51b51ff1 100644
--- a/tests/src/Simple/SimpleTest.targets
+++ b/tests/src/Simple/SimpleTest.targets
@@ -7,6 +7,7 @@
$(MSBuildProjectDirectory)\obj\$(Configuration)\$(Platform)\
portable
$(DefineConstants);CODEGEN_CPP
+ $(DefineConstants);CODEGEN_WASM
false
false