diff options
author | Morgan Brown <morganbr@users.noreply.github.com> | 2018-01-13 15:48:48 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-13 15:48:48 +0300 |
commit | e32aeceee788a18bb4ced99e629ce8acfcec53b2 (patch) | |
tree | a476d154b9c197169c749104a29562d69974299a /src/ILCompiler.WebAssembly | |
parent | c91a527a15ee429b776e57d1930c5841c66e1dcf (diff) |
Implement WebAssembly delegates (#5143)
* Implement WebAssembly delegates and fix other minor codegen issues.
Diffstat (limited to 'src/ILCompiler.WebAssembly')
-rw-r--r-- | src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs | 28 | ||||
-rw-r--r-- | src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs | 157 |
2 files changed, 144 insertions, 41 deletions
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs index c218dd440..bdc2b4564 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs @@ -484,20 +484,44 @@ namespace Internal.IL } /// <summary> + /// Represents the result of a ldftn or ldvirtftn + /// </summary> + internal class FunctionPointerEntry : ExpressionEntry + { + /// <summary> + /// True if the function pointer was loaded as a virtual function pointer + /// </summary> + public bool IsVirtual { get; } + + public MethodDesc Method { get; } + + public FunctionPointerEntry(string name, MethodDesc method, LLVMValueRef llvmValue, TypeDesc type, bool isVirtual) : base(StackValueKind.NativeInt, name, llvmValue, type) + { + Method = method; + IsVirtual = isVirtual; + } + + public override StackEntry Duplicate() + { + return new FunctionPointerEntry(Name, Method, RawLLVMValue, Type, IsVirtual); + } + } + + /// <summary> /// Entry representing some token (either of TypeDesc, MethodDesc or FieldDesc) along with its string representation /// </summary> internal class LdTokenEntry<T> : ExpressionEntry { public T LdToken { get; } - public LdTokenEntry(StackValueKind kind, string name, T token, TypeDesc type = null) : base(kind, name, default(LLVMValueRef), type) + public LdTokenEntry(StackValueKind kind, string name, T token, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) { LdToken = token; } public override StackEntry Duplicate() { - return new LdTokenEntry<T>(Kind, Name, LdToken, Type); + return new LdTokenEntry<T>(Kind, Name, LdToken, RawLLVMValue, Type); } protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index 71da8d50a..44a04b8d1 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -52,6 +52,8 @@ namespace Internal.IL /// </summary> private EvaluationStack<StackEntry> _stack = new EvaluationStack<StackEntry>(0); + LLVMTypeRef _universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); + private class BasicBlock { // Common fields @@ -156,8 +158,7 @@ namespace Internal.IL private LLVMValueRef CreateLLVMFunction(string mangledName) { - LLVMTypeRef universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); - return LLVM.AddFunction(Module, mangledName , universalSignature); + return LLVM.AddFunction(Module, mangledName , _universalSignature); } private LLVMValueRef GetOrCreateLLVMFunction(string mangledName) @@ -563,6 +564,15 @@ namespace Internal.IL { throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}"); } + else if (toStoreKind == LLVMTypeKind.LLVMFloatTypeKind && valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind) + { + typedToStore = LLVM.BuildFPExt(builder, source, valueType, "FloatToDouble"); + } + + else if (toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind && valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind) + { + typedToStore = LLVM.BuildFPTrunc(builder, source, valueType, "DoubleToFloat"); + } else if (toStoreKind != valueTypeKind && toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) { throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}"); @@ -827,17 +837,19 @@ namespace Internal.IL { throw new NotImplementedException(); } - if (opcode == ILOpcode.callvirt && callee.IsAbstract) - { - throw new NotImplementedException(); - } - if (callee.OwningType.IsDelegate) + if (opcode == ILOpcode.newobj && callee.OwningType.IsDelegate) { - throw new NotImplementedException(); + FunctionPointerEntry functionPointer = ((FunctionPointerEntry)_stack.Peek()); + DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(callee.OwningType, functionPointer.Method, functionPointer.IsVirtual); + callee = delegateInfo.Constructor.Method; + if (callee.Signature.Length == 3) + { + PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString())); + } } - HandleCall(callee, opcode); + HandleCall(callee, callee.Signature, opcode); } private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPointer, bool isCallVirt) @@ -850,11 +862,11 @@ namespace Internal.IL throw new NotImplementedException(); if (!_compilation.HasFixedSlotVTable(callee.OwningType)) - _dependencies.Add(_compilation.NodeFactory.VirtualMethodUse(callee)); + AddVirtualMethodReference(callee); //TODO: needs runtime support for DispatchByInterface if (callee.OwningType.IsInterface) - throw new NotImplementedException(); + throw new NotImplementedException("Interface call"); return GetCallableVirtualMethod(thisPointer.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), callee); } @@ -863,7 +875,7 @@ namespace Internal.IL return GetOrCreateLLVMFunction(calleeName); } } - + private LLVMValueRef GetOrCreateMethodSlot(MethodDesc method) { var vtableSlotSymbol = _compilation.NodeFactory.VTableSlot(method); @@ -970,32 +982,49 @@ namespace Internal.IL return false; } - private void HandleCall(MethodDesc callee, ILOpcode opcode = ILOpcode.call) - { - AddMethodReference(callee); - int offset = GetTotalParameterOffset() + GetTotalLocalOffset() + callee.Signature.ReturnType.GetElementSize().AsInt; + private void HandleCall(MethodDesc callee, MethodSignature signature, ILOpcode opcode = ILOpcode.call, LLVMValueRef calliTarget = default(LLVMValueRef)) + { + if (callee != null && !callee.IsAbstract) + { + AddMethodReference(callee); + } + + int offset = GetTotalParameterOffset() + GetTotalLocalOffset(); + + LLVMValueRef returnAddress; + LLVMValueRef castReturnAddress; + if (signature.ReturnType != GetWellKnownType(WellKnownType.Void)) + { + offset += signature.ReturnType.GetElementSize().AsInt; + + int returnOffset = GetTotalParameterOffset() + GetTotalLocalOffset(); + returnAddress = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), + new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)returnOffset, LLVMMisc.False) }, + String.Empty); + castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), "castreturnaddress"); + } + else + { + returnAddress = LLVM.ConstNull(LLVM.PointerType(LLVM.Int8Type(), 0)); + castReturnAddress = returnAddress; + } LLVMValueRef shadowStack = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)offset, LLVMMisc.False) }, String.Empty); var castShadowStack = LLVM.BuildPointerCast(_builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack"); - int returnOffset = GetTotalParameterOffset() + GetTotalLocalOffset(); - var returnAddress = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), - new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)returnOffset, LLVMMisc.False) }, - String.Empty); - var castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), "castreturnaddress"); // argument offset uint argOffset = 0; int instanceAdjustment = 0; - if (!callee.Signature.IsStatic) + if (!signature.IsStatic) { instanceAdjustment = 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[callee.Signature.Length + instanceAdjustment]; + StackEntry[] argumentValues = new StackEntry[signature.Length + instanceAdjustment]; for(int i = 0; i < argumentValues.Length; i++) { @@ -1007,16 +1036,18 @@ namespace Internal.IL StackEntry toStore = argumentValues[index]; TypeDesc argType; - if (index == 0 && !callee.Signature.IsStatic) + if (index == 0 && !signature.IsStatic) { - if(callee.OwningType.IsValueType) + if (opcode == ILOpcode.calli) + argType = toStore.Type; + else if (callee.OwningType.IsValueType) argType = callee.OwningType.MakeByRefType(); else argType = callee.OwningType; } else { - argType = callee.Signature[index - instanceAdjustment]; + argType = signature[index - instanceAdjustment]; } LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType); @@ -1026,17 +1057,26 @@ namespace Internal.IL argOffset += (uint)argType.GetElementSize().AsInt; } - LLVMValueRef fn = LLVMFunctionForMethod(callee, callee.Signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt); + LLVMValueRef fn; + if (opcode == ILOpcode.calli) + { + fn = calliTarget; + } + else + { + fn = LLVMFunctionForMethod(callee, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt); + } + LLVM.BuildCall(_builder, fn, new LLVMValueRef[] { castShadowStack, castReturnAddress}, string.Empty); - if (!callee.Signature.ReturnType.IsVoid) + if (!signature.ReturnType.IsVoid) { - LLVMTypeRef returnLLVMType = GetLLVMTypeForTypeDesc(callee.Signature.ReturnType); + LLVMTypeRef returnLLVMType = GetLLVMTypeForTypeDesc(signature.ReturnType); LLVMValueRef returnLLVMPointer = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(returnLLVMType, 0), "castreturnpointer"); - PushLoadExpression(GetStackValueKind(callee.Signature.ReturnType), String.Empty, returnLLVMPointer, callee.Signature.ReturnType); + PushLoadExpression(GetStackValueKind(signature.ReturnType), String.Empty, returnLLVMPointer, signature.ReturnType); } } @@ -1045,6 +1085,11 @@ namespace Internal.IL _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(method)); } + private void AddVirtualMethodReference(MethodDesc method) + { + _dependencies.Add(_compilation.NodeFactory.VirtualMethodUse(method)); + } + private void ImportRawPInvoke(MethodDesc method) { LLVMValueRef nativeFunc = LLVM.GetNamedFunction(Module, method.Name); @@ -1146,10 +1191,35 @@ namespace Internal.IL private void ImportCalli(int token) { + MethodSignature methodSignature = (MethodSignature)_methodIL.GetObject(token); + HandleCall(null, methodSignature, ILOpcode.calli, ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVM.PointerType(_universalSignature, 0), _builder)); } private void ImportLdFtn(int token, ILOpcode opCode) { + MethodDesc method = (MethodDesc)_methodIL.GetObject(token); + LLVMValueRef targetLLVMFunction = default(LLVMValueRef); + if (opCode == ILOpcode.ldvirtftn) + { + StackEntry thisPointer = _stack.Pop(); + if (method.IsVirtual) + { + targetLLVMFunction = LLVMFunctionForMethod(method, thisPointer, true); + AddVirtualMethodReference(method); + } + } + else + { + AddMethodReference(method); + } + + if (targetLLVMFunction.Pointer.Equals(IntPtr.Zero)) + { + targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(method).ToString()); + } + + var entry = new FunctionPointerEntry("ldftn", method, targetLLVMFunction, GetWellKnownType(WellKnownType.IntPtr), opCode == ILOpcode.ldvirtftn); + _stack.Push(entry); } private void ImportLoadInt(long value, StackValueKind kind) @@ -1339,10 +1409,14 @@ namespace Internal.IL var pointer = _stack.Pop(); Debug.Assert(pointer is ExpressionEntry || pointer is ConstantEntry); var expressionPointer = pointer as ExpressionEntry; - TypeDesc pointerElementType = pointer.Type.GetParameterType(); - LLVMValueRef rawValue = expressionPointer?.RawLLVMValue ?? LLVM.ConstNull(GetLLVMTypeForTypeDesc(pointerElementType)); + if(type == null) + { + type = GetWellKnownType(WellKnownType.Object).MakeByRefType(); + } + + LLVMValueRef pointerElementType = pointer.ValueAsType(type.MakePointerType(), _builder); _stack.Push(new LoadExpressionEntry(type != null ? GetStackValueKind(type) : StackValueKind.ByRef, "ldind", - rawValue, pointer.Type.GetParameterType())); + pointerElementType, type.MakePointerType())); } private void ImportStoreIndirect(int token) @@ -1603,6 +1677,8 @@ namespace Internal.IL { StackEntry value = _stack.Pop(); LLVMValueRef convertedValue; + TypeDesc destType = GetWellKnownType(wellKnownType); + //conv.u for a pointer should change to a int8* if (wellKnownType == WellKnownType.UIntPtr) { @@ -1612,14 +1688,14 @@ namespace Internal.IL } else { - convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder); + convertedValue = value.ValueAsType(destType, _builder); } } else { - convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder); + convertedValue = value.ValueAsType(destType, _builder); } - PushExpression(value.Kind, "conv", convertedValue, value.Type); + PushExpression(GetStackValueKind(destType), "conv", convertedValue, destType); } private void ImportUnaryOperation(ILOpcode opCode) @@ -1739,13 +1815,13 @@ namespace Internal.IL PushExpression(StackValueKind.ByRef, "ldtoken", GetEETypeForTypeDesc(ldtokenValue as TypeDesc, false), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr")); MethodDesc helper = _compilation.TypeSystemContext.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeTypeHandle"); AddMethodReference(helper); - HandleCall(helper); + HandleCall(helper, helper.Signature); name = ldtokenValue.ToString(); } else if (ldtokenValue is FieldDesc) { ldtokenKind = WellKnownType.RuntimeFieldHandle; - value = new LdTokenEntry<FieldDesc>(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, GetWellKnownType(ldtokenKind)); + value = new LdTokenEntry<FieldDesc>(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, LLVM.ConstInt(LLVM.Int32Type(), 0, LLVMMisc.False), GetWellKnownType(ldtokenKind)); _stack.Push(value); } else if (ldtokenValue is MethodDesc) @@ -1781,6 +1857,9 @@ namespace Internal.IL private void ImportSizeOf(int token) { + TypeDesc type = (TypeDesc)_methodIL.GetObject(token); + int size = type.GetElementSize().AsInt; + PushExpression(StackValueKind.Int32, "sizeof", LLVM.ConstInt(LLVM.Int32Type(), (ulong)size, LLVMMisc.False), GetWellKnownType(WellKnownType.Int32)); } private void ImportRefAnyType() @@ -2061,7 +2140,7 @@ namespace Internal.IL { MetadataType helperType = context.SystemModule.GetKnownType("System.Runtime", "RuntimeExports"); MethodDesc helperMethod = helperType.GetKnownMethod(methodName, null); - HandleCall(helperMethod); + HandleCall(helperMethod, helperMethod.Signature); } private StackEntry NewSpillSlot(StackEntry entry) |