diff options
Diffstat (limited to 'src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs')
-rw-r--r-- | src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs | 1018 |
1 files changed, 819 insertions, 199 deletions
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index 8b0936d1e..e828d6d97 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Linq; using Internal.TypeSystem; using ILCompiler; @@ -12,8 +14,8 @@ using LLVMSharp; using ILCompiler.CodeGen; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.WebAssembly; using Internal.TypeSystem.Ecma; -using System.Linq; namespace Internal.IL { @@ -35,6 +37,7 @@ namespace Internal.IL } public LLVMModuleRef Module { get; } + public LLVMContextRef Context { get; } private readonly MethodDesc _method; private readonly MethodIL _methodIL; private readonly MethodSignature _signature; @@ -44,17 +47,20 @@ namespace Internal.IL private LLVMBasicBlockRef _curBasicBlock; private LLVMBuilderRef _builder; private readonly LocalVariableDefinition[] _locals; + private readonly LLVMValueRef[] _localSlots; + private readonly LLVMValueRef[] _argSlots; private List<SpilledExpressionEntry> _spilledExpressions = new List<SpilledExpressionEntry>(); private int _pointerSize; private readonly byte[] _ilBytes; + private MethodDebugInformation _debugInformation; + private LLVMMetadataRef _debugFunction; + private TypeDesc _constrainedType = null; /// <summary> /// Stack of values pushed onto the IL stack: locals, arguments, values, function pointer, ... /// </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 @@ -92,6 +98,8 @@ namespace Internal.IL _methodIL = methodIL; _ilBytes = methodIL.GetILBytes(); _locals = methodIL.GetLocals(); + _localSlots = new LLVMValueRef[_locals.Length]; + _argSlots = new LLVMValueRef[method.Signature.Length]; _signature = method.Signature; _thisType = method.OwningType; @@ -101,15 +109,21 @@ namespace Internal.IL { _exceptionRegions[i] = new ExceptionRegion() { ILRegion = ilExceptionRegions[i] }; } - _llvmFunction = GetOrCreateLLVMFunction(mangledName); + _llvmFunction = GetOrCreateLLVMFunction(mangledName, method.Signature); _builder = LLVM.CreateBuilder(); _pointerSize = compilation.NodeFactory.Target.PointerSize; + + _debugInformation = _compilation.GetDebugInfo(_methodIL); + + Context = LLVM.GetModuleContext(Module); } public void Import() { FindBasicBlocks(); + GenerateProlog(); + try { ImportBasicBlocks(); @@ -130,53 +144,182 @@ namespace Internal.IL LLVM.PositionBuilderAtEnd(_builder, trapBlock); EmitTrapCall(); - LLVM.BuildRetVoid(_builder); throw; } finally { // Generate thunk for runtime exports - if (_method.IsRuntimeExport) + if (_method.IsRuntimeExport || _method.IsNativeCallable) { - EmitNativeToManagedThunk(_compilation, _method, ((EcmaMethod)_method).GetRuntimeExportName(), _llvmFunction); + EcmaMethod ecmaMethod = ((EcmaMethod)_method); + string exportName = ecmaMethod.IsRuntimeExport ? ecmaMethod.GetRuntimeExportName() : ecmaMethod.GetNativeCallableExportName(); + if (exportName == null) + { + exportName = ecmaMethod.Name; + } + + EmitNativeToManagedThunk(_compilation, _method, exportName, _llvmFunction); } } } private void GenerateProlog() { - if (!_methodIL.IsInitLocals) + LLVMBasicBlockRef prologBlock = LLVM.AppendBasicBlock(_llvmFunction, "Prolog"); + LLVM.PositionBuilderAtEnd(_builder, prologBlock); + + // Copy arguments onto the stack to allow + // them to be referenced by address + int thisOffset = 0; + if (!_signature.IsStatic) { - return; + thisOffset = 1; + } + + // Keep track of where we are in the llvm signature, starting after the + // shadow stack pointer and return adress + int signatureIndex = 1; + if (NeedsReturnStackSlot(_signature)) + { + signatureIndex++; + } + + string[] argNames = null; + if (_debugInformation != null) + { + argNames = _debugInformation.GetParameterNames()?.ToArray(); + } + + for (int i = 0; i < _signature.Length; i++) + { + if (CanStoreTypeOnStack(_signature[i])) + { + string argName = String.Empty; + if (argNames != null && argNames[i] != null) + { + argName = argNames[i] + "_"; + } + argName += $"arg{i + thisOffset}_"; + + LLVMValueRef argStackSlot = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_signature[i]), argName); + LLVM.BuildStore(_builder, LLVM.GetParam(_llvmFunction, (uint)signatureIndex), argStackSlot); + _argSlots[i] = argStackSlot; + signatureIndex++; + } } - int totalLocalSize = 0; - foreach(LocalVariableDefinition local in _locals) + string[] localNames = new string[_locals.Length]; + if (_debugInformation != null) { - totalLocalSize = PadNextOffset(local.Type, totalLocalSize); + foreach (ILLocalVariable localDebugInfo in _debugInformation.GetLocalVariables() ?? Enumerable.Empty<ILLocalVariable>()) + { + // Check whether the slot still exists as the compiler may remove it for intrinsics + int slot = localDebugInfo.Slot; + if (slot < localNames.Length) + { + localNames[localDebugInfo.Slot] = localDebugInfo.Name; + } + } } - var sp = LLVM.GetFirstParam(_llvmFunction); - int paramOffset = GetTotalParameterOffset(); - for (int i = 0; i < totalLocalSize; i++) + for (int i = 0; i < _locals.Length; i++) { - var stackOffset = LLVM.BuildGEP(_builder, sp, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)(paramOffset + i), LLVMMisc.False) }, String.Empty); - LLVM.BuildStore(_builder, LLVM.ConstInt(LLVM.Int8Type(), 0, LLVMMisc.False), stackOffset); + if (CanStoreLocalOnStack(_locals[i].Type)) + { + string localName = String.Empty; + if (localNames[i] != null) + { + localName = localNames[i] + "_"; + } + + localName += $"local{i}_"; + + LLVMValueRef localStackSlot = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_locals[i].Type), localName); + _localSlots[i] = localStackSlot; + } } + + if (_methodIL.IsInitLocals) + { + for(int i = 0; i < _locals.Length; i++) + { + LLVMValueRef localAddr = LoadVarAddress(i, LocalVarKind.Local, out TypeDesc localType); + if(CanStoreLocalOnStack(localType)) + { + LLVMTypeRef llvmType = GetLLVMTypeForTypeDesc(localType); + LLVMTypeKind typeKind = LLVM.GetTypeKind(llvmType); + switch (typeKind) + { + case LLVMTypeKind.LLVMIntegerTypeKind: + if (llvmType.Equals(LLVM.Int1Type())) + { + LLVM.BuildStore(_builder, BuildConstInt1(0), localAddr); + } + else if (llvmType.Equals(LLVM.Int8Type())) + { + LLVM.BuildStore(_builder, BuildConstInt8(0), localAddr); + } + else if (llvmType.Equals(LLVM.Int16Type())) + { + LLVM.BuildStore(_builder, BuildConstInt16(0), localAddr); + } + else if (llvmType.Equals(LLVM.Int32Type())) + { + LLVM.BuildStore(_builder, BuildConstInt32(0), localAddr); + } + else if (llvmType.Equals(LLVM.Int64Type())) + { + LLVM.BuildStore(_builder, BuildConstInt64(0), localAddr); + } + else + { + throw new Exception("Unexpected LLVM int type"); + } + break; + + case LLVMTypeKind.LLVMPointerTypeKind: + LLVM.BuildStore(_builder, LLVM.ConstPointerNull(llvmType), localAddr); + break; + + default: + LLVMValueRef castAddr = LLVM.BuildPointerCast(_builder, localAddr, LLVM.PointerType(LLVM.Int8Type(), 0), $"cast_local{i}_"); + ImportCallMemset(castAddr, 0, localType.GetElementSize().AsInt); + break; + } + } + else + { + LLVMValueRef castAddr = LLVM.BuildPointerCast(_builder, localAddr, LLVM.PointerType(LLVM.Int8Type(), 0), $"cast_local{i}_"); + ImportCallMemset(castAddr, 0, localType.GetElementSize().AsInt); + } + } + } + + MetadataType metadataType = (MetadataType)_thisType; + if (!metadataType.IsBeforeFieldInit) + { + if (!_method.IsStaticConstructor && _method.Signature.IsStatic || _method.IsConstructor || (_thisType.IsValueType && !_method.Signature.IsStatic)) + { + TriggerCctor(metadataType); + } + } + + LLVMBasicBlockRef block0 = GetLLVMBasicBlockForBlock(_basicBlocks[0]); + LLVM.BuildBr(_builder, block0); } - private LLVMValueRef CreateLLVMFunction(string mangledName) + private LLVMValueRef CreateLLVMFunction(string mangledName, MethodSignature signature) { - return LLVM.AddFunction(Module, mangledName , _universalSignature); + return LLVM.AddFunction(Module, mangledName, GetLLVMSignatureForMethod(signature)); } - private LLVMValueRef GetOrCreateLLVMFunction(string mangledName) + private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, MethodSignature signature) { LLVMValueRef llvmFunction = LLVM.GetNamedFunction(Module, mangledName); if(llvmFunction.Pointer == IntPtr.Zero) { - return CreateLLVMFunction(mangledName); + return CreateLLVMFunction(mangledName, signature); } return llvmFunction; } @@ -274,23 +417,13 @@ namespace Internal.IL int n = entryStack.Length; for (int i = 0; i < n; i++) { - _stack.Push(entryStack[i].Duplicate()); + _stack.Push(entryStack[i].Duplicate(_builder)); } } - bool isFirstBlock = false; - if(_curBasicBlock.Equals(default(LLVMBasicBlockRef))) - { - isFirstBlock = true; - } _curBasicBlock = GetLLVMBasicBlockForBlock(basicBlock); LLVM.PositionBuilderAtEnd(_builder, _curBasicBlock); - - if(isFirstBlock) - { - GenerateProlog(); - } } private void EndImportingBasicBlock(BasicBlock basicBlock) @@ -302,22 +435,81 @@ namespace Internal.IL { if (_basicBlocks[_currentOffset].StartOffset == 0) throw new InvalidProgramException(); - + MarkBasicBlock(_basicBlocks[_currentOffset]); LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(_basicBlocks[_currentOffset])); } - else - { - LLVM.BuildRet(_builder, default(LLVMValueRef)); - } } } private void StartImportingInstruction() { + if (_debugInformation != null) + { + bool foundSequencePoint = false; + ILSequencePoint curSequencePoint = default; + foreach (var sequencePoint in _debugInformation.GetSequencePoints() ?? Enumerable.Empty<ILSequencePoint>()) + { + if (sequencePoint.Offset == _currentOffset) + { + curSequencePoint = sequencePoint; + foundSequencePoint = true; + break; + } + else if (sequencePoint.Offset < _currentOffset) + { + curSequencePoint = sequencePoint; + foundSequencePoint = true; + } + } + + if (!foundSequencePoint) + { + return; + } + + // LLVM can't process empty string file names + if (String.IsNullOrWhiteSpace(curSequencePoint.Document)) + { + return; + } + + DebugMetadata debugMetadata; + if (!_compilation.DebugMetadataMap.TryGetValue(curSequencePoint.Document, out debugMetadata)) + { + string fullPath = curSequencePoint.Document; + string fileName = Path.GetFileName(fullPath); + string directory = Path.GetDirectoryName(fullPath) ?? String.Empty; + LLVMMetadataRef fileMetadata = LLVMPInvokes.LLVMDIBuilderCreateFile(_compilation.DIBuilder, fullPath, fullPath.Length, + directory, directory.Length); + + // todo: get the right value for isOptimized + LLVMMetadataRef compileUnitMetadata = LLVMPInvokes.LLVMDIBuilderCreateCompileUnit(_compilation.DIBuilder, LLVMDWARFSourceLanguage.LLVMDWARFSourceLanguageC, + fileMetadata, "ILC", 3, isOptimized: false, String.Empty, 0, 1, String.Empty, 0, LLVMDWARFEmissionKind.LLVMDWARFEmissionFull, 0, false, false); + LLVM.AddNamedMetadataOperand(Module, "llvm.dbg.cu", LLVM.MetadataAsValue(Context, compileUnitMetadata)); + + debugMetadata = new DebugMetadata(fileMetadata, compileUnitMetadata); + _compilation.DebugMetadataMap[fullPath] = debugMetadata; + } + + if (_debugFunction.Pointer == IntPtr.Zero) + { + _debugFunction = LLVM.DIBuilderCreateFunction(_compilation.DIBuilder, debugMetadata.CompileUnit, _method.Name, String.Empty, debugMetadata.File, + (uint)_debugInformation.GetSequencePoints().FirstOrDefault().LineNumber, default(LLVMMetadataRef), 1, 1, 1, 0, IsOptimized: 0, _llvmFunction); + } + + LLVMMetadataRef currentLine = LLVMPInvokes.LLVMDIBuilderCreateDebugLocation(Context, (uint)curSequencePoint.LineNumber, 0, _debugFunction, default(LLVMMetadataRef)); + LLVM.SetCurrentDebugLocation(_builder, LLVM.MetadataAsValue(Context, currentLine)); + } } private void EndImportingInstruction() { + // If this was constrained used in a call, it's already been cleared, + // but if it was on some other instruction, it shoudln't carry forward + _constrainedType = null; + + // Reset the debug position so it doesn't end up applying to the wrong instructions + LLVM.SetCurrentDebugLocation(_builder, default(LLVMValueRef)); } private void ImportNop() @@ -337,40 +529,40 @@ namespace Internal.IL private void ImportLoadVar(int index, bool argument) { LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out TypeDesc type); - PushLoadExpression(GetStackValueKind(type), "ld" + (argument ? "arg" : "loc") + index + "_", typedLoadLocation, type); + PushLoadExpression(GetStackValueKind(type), (argument ? "arg" : "loc") + index + "_", typedLoadLocation, type); } private LLVMValueRef LoadTemp(int index) { LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); - return LLVM.BuildLoad(_builder, CastToPointerToTypeDesc(address, type), "ldtemp"); + return LLVM.BuildLoad(_builder, CastToPointerToTypeDesc(address, type, $"Temp{index}_"), $"LdTemp{index}_"); } internal LLVMValueRef LoadTemp(int index, LLVMTypeRef asType) { LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); - return LLVM.BuildLoad(_builder, CastIfNecessary(address, LLVM.PointerType(asType, 0)), "ldtemp"); + return LLVM.BuildLoad(_builder, CastIfNecessary(address, LLVM.PointerType(asType, 0), $"Temp{index}_"), $"LdTemp{index}_"); } - private void StoreTemp(int index, LLVMValueRef value) + private void StoreTemp(int index, LLVMValueRef value, string name = null) { LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); - LLVM.BuildStore(_builder, CastToTypeDesc(value, type), CastToPointerToTypeDesc(address, type)); + LLVM.BuildStore(_builder, CastToTypeDesc(value, type, name), CastToPointerToTypeDesc(address, type, $"Temp{index}_")); } - internal static LLVMValueRef LoadValue(LLVMBuilderRef builder, LLVMValueRef address, TypeDesc sourceType, LLVMTypeRef targetType, bool signExtend) + internal static LLVMValueRef LoadValue(LLVMBuilderRef builder, LLVMValueRef address, TypeDesc sourceType, LLVMTypeRef targetType, bool signExtend, string loadName = null) { var underlyingSourceType = sourceType.UnderlyingType; if (targetType.TypeKind == LLVMTypeKind.LLVMIntegerTypeKind && underlyingSourceType.IsPrimitive && !underlyingSourceType.IsPointer) { var sourceLLVMType = ILImporter.GetLLVMTypeForTypeDesc(underlyingSourceType); var typedAddress = CastIfNecessary(builder, address, LLVM.PointerType(sourceLLVMType, 0)); - return CastIntValue(builder, LLVM.BuildLoad(builder, typedAddress, "ldvalue"), targetType, signExtend); + return CastIntValue(builder, LLVM.BuildLoad(builder, typedAddress, loadName ?? "ldvalue"), targetType, signExtend); } else { var typedAddress = CastIfNecessary(builder, address, LLVM.PointerType(targetType, 0)); - return LLVM.BuildLoad(builder, typedAddress, "ldvalue"); + return LLVM.BuildLoad(builder, typedAddress, loadName ?? "ldvalue"); } } @@ -427,7 +619,7 @@ namespace Internal.IL varCountBase = 1; } - GetArgSizeAndOffsetAtIndex(index, out int argSize, out varOffset); + GetArgSizeAndOffsetAtIndex(index, out int argSize, out varOffset, out int realArgIndex); if (!_signature.IsStatic && index == 0) { @@ -442,6 +634,13 @@ namespace Internal.IL type = _signature[index - varCountBase]; } valueType = GetLLVMTypeForTypeDesc(type); + + // If the argument can be passed as a real argument rather than on the shadow stack, + // get its address here + if(realArgIndex != -1) + { + return _argSlots[realArgIndex]; + } } else if (kind == LocalVarKind.Local) { @@ -449,6 +648,11 @@ namespace Internal.IL GetLocalSizeAndOffsetAtIndex(index, out int localSize, out varOffset); valueType = GetLLVMTypeForTypeDesc(_locals[index].Type); type = _locals[index].Type; + if(varOffset == -1) + { + Debug.Assert(_localSlots[index].Pointer != IntPtr.Zero); + return _localSlots[index]; + } } else { @@ -460,7 +664,7 @@ namespace Internal.IL return LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)(varBase + varOffset), LLVMMisc.False) }, - String.Empty); + $"{kind}{index}_"); } @@ -510,71 +714,71 @@ namespace Internal.IL TypeDesc varType; StackEntry toStore = _stack.Pop(); LLVMValueRef varAddress = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out varType); - CastingStore(varAddress, toStore, varType); + CastingStore(varAddress, toStore, varType, $"Variable{index}_"); } - private void ImportStoreHelper(LLVMValueRef toStore, LLVMTypeRef valueType, LLVMValueRef basePtr, uint offset) + private void ImportStoreHelper(LLVMValueRef toStore, LLVMTypeRef valueType, LLVMValueRef basePtr, uint offset, string name = null) { - LLVMValueRef typedToStore = CastIfNecessary(toStore, valueType); + LLVMValueRef typedToStore = CastIfNecessary(toStore, valueType, name); var storeLocation = LLVM.BuildGEP(_builder, basePtr, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), offset, LLVMMisc.False) }, String.Empty); - var typedStoreLocation = CastIfNecessary(storeLocation, LLVM.PointerType(valueType, 0)); + var typedStoreLocation = CastIfNecessary(storeLocation, LLVM.PointerType(valueType, 0), "TypedStore" + (name ?? "")); LLVM.BuildStore(_builder, typedToStore, typedStoreLocation); } - private LLVMValueRef CastToRawPointer(LLVMValueRef source) + private LLVMValueRef CastToRawPointer(LLVMValueRef source, string name = null) { - return CastIfNecessary(source, LLVM.PointerType(LLVM.Int8Type(), 0)); + return CastIfNecessary(source, LLVM.PointerType(LLVM.Int8Type(), 0), name); } - private LLVMValueRef CastToTypeDesc(LLVMValueRef source, TypeDesc type) + private LLVMValueRef CastToTypeDesc(LLVMValueRef source, TypeDesc type, string name = null) { - return CastIfNecessary(source, GetLLVMTypeForTypeDesc(type)); + return CastIfNecessary(source, GetLLVMTypeForTypeDesc(type), (name ?? "") + type.ToString()); } - private LLVMValueRef CastToPointerToTypeDesc(LLVMValueRef source, TypeDesc type) + private LLVMValueRef CastToPointerToTypeDesc(LLVMValueRef source, TypeDesc type, string name = null) { - return CastIfNecessary(source, LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0)); + return CastIfNecessary(source, LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0), (name ?? "") + type.ToString()); } - private void CastingStore(LLVMValueRef address, StackEntry value, TypeDesc targetType) + private void CastingStore(LLVMValueRef address, StackEntry value, TypeDesc targetType, string targetName = null) { - var typedStoreLocation = CastToPointerToTypeDesc(address, targetType); + var typedStoreLocation = CastToPointerToTypeDesc(address, targetType, targetName); LLVM.BuildStore(_builder, value.ValueAsType(targetType, _builder), typedStoreLocation); } - private LLVMValueRef CastIfNecessary(LLVMValueRef source, LLVMTypeRef valueType) + private LLVMValueRef CastIfNecessary(LLVMValueRef source, LLVMTypeRef valueType, string name = null) { - return CastIfNecessary(_builder, source, valueType); + return CastIfNecessary(_builder, source, valueType, name); } - internal static LLVMValueRef CastIfNecessary(LLVMBuilderRef builder, LLVMValueRef source, LLVMTypeRef valueType) + internal static LLVMValueRef CastIfNecessary(LLVMBuilderRef builder, LLVMValueRef source, LLVMTypeRef valueType, string name = null) { LLVMTypeRef sourceType = LLVM.TypeOf(source); if (sourceType.Pointer == valueType.Pointer) return source; - LLVMTypeKind toStoreKind = LLVM.GetTypeKind(LLVM.TypeOf(source)); + LLVMTypeKind toStoreKind = LLVM.GetTypeKind(sourceType); LLVMTypeKind valueTypeKind = LLVM.GetTypeKind(valueType); LLVMValueRef typedToStore = source; if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) { - typedToStore = LLVM.BuildPointerCast(builder, source, valueType, "CastIfNecessaryPtr"); + typedToStore = LLVM.BuildPointerCast(builder, source, valueType, "CastPtr" + (name ?? "")); } else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind) { - typedToStore = LLVM.BuildPtrToInt(builder, source, valueType, "CastIfNecessaryInt"); + typedToStore = LLVM.BuildPtrToInt(builder, source, valueType, "CastInt" + (name ?? "")); } else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMArrayTypeKind) { - typedToStore = LLVM.BuildLoad(builder, CastIfNecessary(builder, source, LLVM.PointerType(valueType, 0)), "CastIfNecessaryArrayLoad"); + typedToStore = LLVM.BuildLoad(builder, CastIfNecessary(builder, source, LLVM.PointerType(valueType, 0), name), "CastArrayLoad" + (name ?? "")); } else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMArrayTypeKind) { - typedToStore = LLVM.BuildLoad(builder, CastIfNecessary(builder, source, LLVM.PointerType(valueType, 0)), "CastIfNecessaryArrayLoad"); + typedToStore = LLVM.BuildLoad(builder, CastIfNecessary(builder, source, LLVM.PointerType(valueType, 0), name), "CastArrayLoad" + (name ?? "")); } else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) { @@ -582,7 +786,7 @@ namespace Internal.IL } else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) { - typedToStore = LLVM.BuildIntToPtr(builder, source, valueType, "CastIfNecessaryPtr"); + typedToStore = LLVM.BuildIntToPtr(builder, source, valueType, "CastPtr" + (name ?? "")); } else if (toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) { @@ -590,12 +794,12 @@ namespace Internal.IL } else if (toStoreKind == LLVMTypeKind.LLVMFloatTypeKind && valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind) { - typedToStore = LLVM.BuildFPExt(builder, source, valueType, "FloatToDouble"); + typedToStore = LLVM.BuildFPExt(builder, source, valueType, "CastFloatToDouble" + (name ?? "")); } else if (toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind && valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind) { - typedToStore = LLVM.BuildFPTrunc(builder, source, valueType, "DoubleToFloat"); + typedToStore = LLVM.BuildFPTrunc(builder, source, valueType, "CastDoubleToFloat" + (name ?? "")); } else if (toStoreKind != valueTypeKind && toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) { @@ -604,21 +808,18 @@ namespace Internal.IL else if (toStoreKind == valueTypeKind && toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind) { Debug.Assert(toStoreKind != LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMPointerTypeKind); - typedToStore = LLVM.BuildIntCast(builder, source, valueType, "CastIfNecessaryInt"); + typedToStore = LLVM.BuildIntCast(builder, source, valueType, "CastInt" + (name ?? "")); } - else if (toStoreKind != LLVMTypeKind.LLVMFloatTypeKind && valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind) + else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && (valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind || valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind)) { - typedToStore = LLVM.BuildFPCast(builder, source, valueType, "CastIfNecessaryFloat"); + //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) && valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind) { //TODO: keep track of the TypeDesc so we can call BuildFPToUI when the integer is unsigned - typedToStore = LLVM.BuildFPToSI(builder, source, valueType, "CastIfNecessaryFloat"); - } - else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind) - { - throw new NotImplementedException(); + typedToStore = LLVM.BuildFPToSI(builder, source, valueType, "CastFloatSI" + (name ?? "")); } return typedToStore; @@ -667,7 +868,37 @@ namespace Internal.IL case TypeFlags.ValueType: case TypeFlags.Nullable: - return LLVM.ArrayType(LLVM.Int8Type(), (uint)type.GetElementSize().AsInt); + { + int structSize = type.GetElementSize().AsInt; + + // LLVM thinks certain sizes of struct have a different calling convention than Clang does. + // Treating them as ints fixes that and is more efficient in general + switch (structSize) + { + case 1: + return LLVM.Int8Type(); + case 2: + return LLVM.Int16Type(); + case 4: + return LLVM.Int32Type(); + case 8: + return LLVM.Int64Type(); + } + + int numInts = structSize / 4; + int numBytes = structSize - numInts * 4; + LLVMTypeRef[] structMembers = new LLVMTypeRef[numInts + numBytes]; + for (int i = 0; i < numInts; i++) + { + structMembers[i] = LLVM.Int32Type(); + } + for (int i = 0; i < numBytes; i++) + { + structMembers[i + numInts] = LLVM.Int8Type(); + } + + return LLVM.StructType(structMembers, true); + } case TypeFlags.Enum: return GetLLVMTypeForTypeDesc(type.UnderlyingType); @@ -695,17 +926,65 @@ namespace Internal.IL int offset = 0; for (int i = 0; i < _locals.Length; i++) { - offset = PadNextOffset(_locals[i].Type, offset); + TypeDesc localType = _locals[i].Type; + if (!CanStoreLocalOnStack(localType)) + { + offset = PadNextOffset(localType, offset); + } } return offset.AlignUp(_pointerSize); } + private bool CanStoreLocalOnStack(TypeDesc localType) + { + // Keep all locals on the shadow stack if there is exception + // handling so funclets can access them + if (_exceptionRegions.Length == 0) + { + return CanStoreTypeOnStack(localType); + } + return false; + } + + /// <summary> + /// Returns true if the type can be stored on the local stack + /// instead of the shadow stack in this method. + /// </summary> + private static bool CanStoreTypeOnStack(TypeDesc type) + { + if (type is DefType defType) + { + if (!defType.IsGCPointer && !defType.ContainsGCPointers) + { + return true; + } + } + else if (type is PointerType) + { + return true; + } + + return false; + } + + /// <summary> + /// Returns true if the method returns a type that must be kept + /// on the shadow stack + /// </summary> + private static bool NeedsReturnStackSlot(MethodSignature signature) + { + return !signature.ReturnType.IsVoid && !CanStoreTypeOnStack(signature.ReturnType); + } + private int GetTotalParameterOffset() { int offset = 0; for (int i = 0; i < _signature.Length; i++) { - offset = PadNextOffset(_signature[i], offset); + if (!CanStoreTypeOnStack(_signature[i])) + { + offset = PadNextOffset(_signature[i], offset); + } } if (!_signature.IsStatic) { @@ -723,8 +1002,10 @@ namespace Internal.IL return offset.AlignUp(_pointerSize); } - private void GetArgSizeAndOffsetAtIndex(int index, out int size, out int offset) + private void GetArgSizeAndOffsetAtIndex(int index, out int size, out int offset, out int realArgIndex) { + realArgIndex = -1; + int thisSize = 0; if (!_signature.IsStatic) { @@ -744,10 +1025,28 @@ namespace Internal.IL var argType = _signature[index]; size = argType.GetElementSize().AsInt; + int potentialRealArgIndex = 0; + offset = thisSize; for (int i = 0; i < index; i++) { - offset = PadNextOffset(_signature[i], offset); + // We could compact the set of argSlots to only those that we'd keep on the stack, but currently don't + potentialRealArgIndex++; + + if (!CanStoreTypeOnStack(_signature[i])) + { + offset = PadNextOffset(_signature[i], offset); + } + } + + if (CanStoreTypeOnStack(argType)) + { + realArgIndex = potentialRealArgIndex; + offset = -1; + } + else + { + offset = PadOffset(argType, offset); } } @@ -756,10 +1055,21 @@ namespace Internal.IL LocalVariableDefinition local = _locals[index]; size = local.Type.GetElementSize().AsInt; - offset = 0; - for (int i = 0; i < index; i++) + if (CanStoreLocalOnStack(local.Type)) { - offset = PadNextOffset(_locals[i].Type, offset); + offset = -1; + } + else + { + offset = 0; + for (int i = 0; i < index; i++) + { + if (!CanStoreLocalOnStack(_locals[i].Type)) + { + offset = PadNextOffset(_locals[i].Type, offset); + } + } + offset = PadOffset(local.Type, offset); } } @@ -773,6 +1083,7 @@ namespace Internal.IL { offset = PadNextOffset(_spilledExpressions[i].Type, offset); } + offset = PadOffset(spill.Type, offset); } public int PadNextOffset(TypeDesc type, int atOffset) @@ -810,12 +1121,14 @@ namespace Internal.IL { TypeDesc type; LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out type); - _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldloca", typedLoadLocation, type.MakePointerType())); + _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldloca", typedLoadLocation, type.MakeByRefType())); } private void ImportDup() { - _stack.Push(_stack.Peek().Duplicate()); + var entry = _stack.Pop(); + _stack.Push(entry.Duplicate(_builder)); + _stack.Push(entry.Duplicate(_builder)); } private void ImportPop() @@ -858,14 +1171,25 @@ namespace Internal.IL private void ImportReturn() { - if (_signature.ReturnType != GetWellKnownType(WellKnownType.Void)) + if (_signature.ReturnType.IsVoid) { - StackEntry retVal = _stack.Pop(); - LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); - ImportStoreHelper(retVal.ValueAsType(valueType, _builder), valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); + LLVM.BuildRetVoid(_builder); + return; } - LLVM.BuildRetVoid(_builder); + StackEntry retVal = _stack.Pop(); + LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); + LLVMValueRef castValue = retVal.ValueAsType(valueType, _builder); + + if (NeedsReturnStackSlot(_signature)) + { + ImportStoreHelper(castValue, valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); + LLVM.BuildRetVoid(_builder); + } + else + { + LLVM.BuildRet(_builder, castValue); + } } private void ImportCall(ILOpcode opcode, int token) @@ -879,7 +1203,7 @@ namespace Internal.IL } } - if (callee.IsPInvoke || (callee.IsInternalCall && callee.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute"))) + if (callee.IsRawPInvoke() || (callee.IsInternalCall && callee.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute"))) { ImportRawPInvoke(callee); return; @@ -888,7 +1212,28 @@ namespace Internal.IL if (opcode == ILOpcode.newobj) { TypeDesc newType = callee.OwningType; - if (newType.IsString) + if (newType.IsArray) + { + var paramCnt = callee.Signature.Length; + var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime", "EEType").MakePointerType(); + LLVMValueRef dimensions = LLVM.BuildArrayAlloca(_builder, LLVMTypeRef.Int32Type(), BuildConstInt32(paramCnt), "newobj_array_pdims_" + _currentOffset); + for (int i = paramCnt - 1; i >= 0; --i) + { + LLVM.BuildStore(_builder, _stack.Pop().ValueAsInt32(_builder, true), + LLVM.BuildGEP(_builder, dimensions, new LLVMValueRef[] { BuildConstInt32(i) }, "pdims_ptr")); + } + var arguments = new StackEntry[] + { + new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(newType, true), eeTypeDesc), + new Int32ConstantEntry(paramCnt), + new AddressExpressionEntry(StackValueKind.ValueType, "newobj_array_pdims", dimensions) + }; + MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime.CompilerHelpers", "ArrayHelpers"); + MethodDesc helperMethod = helperType.GetKnownMethod("NewObjArray", null); + PushNonNull(HandleCall(helperMethod, helperMethod.Signature, arguments, forcedReturnType: newType)); + return; + } + else if (newType.IsString) { // String constructors actually look like regular method calls IMethodNode node = _compilation.NodeFactory.StringAllocator(callee); @@ -927,12 +1272,6 @@ namespace Internal.IL } } - // we don't really have virtual call support, but we'll treat it as direct for now - if (opcode != ILOpcode.call && opcode != ILOpcode.callvirt && opcode != ILOpcode.newobj) - { - throw new NotImplementedException(); - } - if (opcode == ILOpcode.newobj && callee.OwningType.IsDelegate) { FunctionPointerEntry functionPointer = ((FunctionPointerEntry)_stack.Peek()); @@ -940,16 +1279,26 @@ namespace Internal.IL callee = delegateInfo.Constructor.Method; if (callee.Signature.Length == 3) { - PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString())); + PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString(), delegateInfo.Thunk.Method.Signature)); } } - HandleCall(callee, callee.Signature, opcode); + TypeDesc localConstrainedType = _constrainedType; + _constrainedType = null; + HandleCall(callee, callee.Signature, opcode, localConstrainedType); } - private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPointer, bool isCallVirt) + private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPointer, bool isCallVirt, TypeDesc constrainedType) { string calleeName = _compilation.NameMangler.GetMangledMethodName(callee).ToString(); + + // Sealed methods must not be called virtually due to sealed vTables, so call them directly + if(callee.IsFinal || callee.OwningType.IsSealed()) + { + AddMethodReference(callee); + return GetOrCreateLLVMFunction(calleeName, callee.Signature); + } + if (thisPointer != null && callee.IsVirtual && isCallVirt) { // TODO: Full resolution of virtual methods @@ -959,44 +1308,125 @@ namespace Internal.IL if (!_compilation.HasFixedSlotVTable(callee.OwningType)) AddVirtualMethodReference(callee); - //TODO: needs runtime support for DispatchByInterface - if (callee.OwningType.IsInterface) - throw new NotImplementedException("Interface call"); + bool isValueTypeCall = false; + TypeDesc thisType = thisPointer.Type; + TypeFlags category = thisType.Category; + MethodDesc targetMethod = null; + TypeDesc parameterType = null; + + if (category == TypeFlags.ByRef) + { + parameterType = ((ByRefType)thisType).ParameterType; + if (parameterType.IsValueType) + { + isValueTypeCall = true; + } + } + + if(constrainedType != null && constrainedType.IsValueType) + { + isValueTypeCall = true; + } + + if (isValueTypeCall) + { + if (constrainedType != null) + { + targetMethod = constrainedType.TryResolveConstraintMethodApprox(callee.OwningType, callee, out _); + } + else if (callee.OwningType.IsInterface) + { + targetMethod = parameterType.ResolveInterfaceMethodTarget(callee); + } + else + { + targetMethod = parameterType.FindVirtualFunctionTargetMethodOnObjectType(callee); + } + } + + if (targetMethod != null) + { + AddMethodReference(targetMethod); + return GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(targetMethod).ToString(), callee.Signature); + } + + return GetCallableVirtualMethod(thisPointer, callee); - return GetCallableVirtualMethod(thisPointer.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), callee); } else { - return GetOrCreateLLVMFunction(calleeName); + return GetOrCreateLLVMFunction(calleeName, callee.Signature); } } - + private LLVMValueRef GetOrCreateMethodSlot(MethodDesc method) { var vtableSlotSymbol = _compilation.NodeFactory.VTableSlot(method); _dependencies.Add(vtableSlotSymbol); LLVMValueRef slot = LoadAddressOfSymbolNode(vtableSlotSymbol); - return LLVM.BuildLoad(_builder, slot, string.Empty); + return LLVM.BuildLoad(_builder, slot, $"{method.Name}_slot"); } - private LLVMValueRef GetCallableVirtualMethod(LLVMValueRef objectPtr, MethodDesc method) + private LLVMValueRef GetCallableVirtualMethod(StackEntry objectPtr, MethodDesc method) { Debug.Assert(method.IsVirtual); + LLVMValueRef slot = GetOrCreateMethodSlot(method); + var pointerSize = method.Context.Target.PointerSize; + LLVMTypeRef llvmSignature = GetLLVMSignatureForMethod(method.Signature); + LLVMValueRef functionPtr; if (method.OwningType.IsInterface) { - throw new NotImplementedException(); + 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", objectPtr.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), 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); } else { - LLVMValueRef slot = GetOrCreateMethodSlot(method); - var pointerSize = method.Context.Target.PointerSize; - LLVMTypeRef universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); - var rawObjectPtr = CastIfNecessary(objectPtr, LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(universalSignature, 0), 0), 0)); + var rawObjectPtr = CastIfNecessary(objectPtr.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(llvmSignature, 0), 0), 0), objectPtr.Name()); var eeType = LLVM.BuildLoad(_builder, rawObjectPtr, "ldEEType"); var slotPtr = LLVM.BuildGEP(_builder, eeType, new LLVMValueRef[] { slot }, "__getslot__"); - var functionPtr = LLVM.BuildLoad(_builder, slotPtr, "ld__getslot__"); - return functionPtr; + functionPtr = LLVM.BuildLoad(_builder, slotPtr, "ld__getslot__"); + } + + return functionPtr; + } + + private LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature) + { + TypeDesc returnType = signature.ReturnType; + LLVMTypeRef llvmReturnType; + bool returnOnStack = false; + if (!NeedsReturnStackSlot(signature)) + { + returnOnStack = true; + llvmReturnType = GetLLVMTypeForTypeDesc(returnType); + } + else + { + llvmReturnType = LLVM.VoidType(); + } + + List<LLVMTypeRef> signatureTypes = new List<LLVMTypeRef>(); + signatureTypes.Add(LLVM.PointerType(LLVM.Int8Type(), 0)); // Shadow stack pointer + + if (!returnOnStack && returnType != GetWellKnownType(WellKnownType.Void)) + { + signatureTypes.Add(LLVM.PointerType(LLVM.Int8Type(), 0)); } + + // 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) + { + if (CanStoreTypeOnStack(type)) + { + signatureTypes.Add(GetLLVMTypeForTypeDesc(type)); + } + } + + return LLVM.FunctionType(llvmReturnType, signatureTypes.ToArray(), false); } private ExpressionEntry AllocateObject(TypeDesc type) @@ -1019,11 +1449,21 @@ namespace Internal.IL return LLVM.ConstInt(LLVM.Int8Type(), number, LLVMMisc.False); } + private static LLVMValueRef BuildConstInt16(byte number) + { + return LLVM.ConstInt(LLVM.Int16Type(), number, LLVMMisc.False); + } + private static LLVMValueRef BuildConstInt32(int number) { return LLVM.ConstInt(LLVM.Int32Type(), (ulong)number, LLVMMisc.False); } + private static LLVMValueRef BuildConstInt64(long number) + { + return LLVM.ConstInt(LLVM.Int64Type(), (ulong)number, LLVMMisc.False); + } + private LLVMValueRef GetEETypeForTypeDesc(TypeDesc target, bool constructed) { var eeTypePointer = GetEETypePointerForTypeDesc(target, constructed); @@ -1129,12 +1569,29 @@ namespace Internal.IL return true; } break; + case ".ctor": + if (metadataType.IsByReferenceOfT) + { + StackEntry byRefValueParamHolder = _stack.Pop(); + + // Allocate a slot on the shadow stack for the ByReference type + int spillIndex = _spilledExpressions.Count; + SpilledExpressionEntry spillEntry = new SpilledExpressionEntry(StackValueKind.ByRef, "byref" + _currentOffset, metadataType, spillIndex, this); + _spilledExpressions.Add(spillEntry); + LLVMValueRef addrOfValueType = LoadVarAddress(spillIndex, LocalVarKind.Temp, out TypeDesc unused); + var typedAddress = CastIfNecessary(_builder, addrOfValueType, LLVM.PointerType(LLVM.Int32Type(), 0)); + LLVM.BuildStore(_builder, byRefValueParamHolder.ValueForStackKind(StackValueKind.ByRef, _builder, false), typedAddress); + + _stack.Push(spillEntry); + return true; + } + break; } return false; } - private void HandleCall(MethodDesc callee, MethodSignature signature, ILOpcode opcode = ILOpcode.call, LLVMValueRef calliTarget = default(LLVMValueRef)) + private void HandleCall(MethodDesc callee, MethodSignature signature, ILOpcode opcode = ILOpcode.call, TypeDesc constrainedType = null, LLVMValueRef calliTarget = default(LLVMValueRef)) { 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 @@ -1143,10 +1600,34 @@ namespace Internal.IL { argumentValues[argumentValues.Length - i - 1] = _stack.Pop(); } - PushNonNull(HandleCall(callee, signature, argumentValues, opcode, calliTarget)); + + if (constrainedType != null) + { + if (signature.IsStatic) + { + // Constrained call on static method + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, _method); + } + StackEntry thisByRef = argumentValues[0]; + if (thisByRef.Kind != StackValueKind.ByRef) + { + // Constrained call without byref + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, _method); + } + + // If this is a constrained call and the 'this' pointer is a reference type, it's a byref, + // dereference it before calling. + if (!constrainedType.IsValueType) + { + TypeDesc objectType = thisByRef.Type.GetParameterType(); + argumentValues[0] = new LoadExpressionEntry(StackValueKind.ObjRef, "thisPtr", thisByRef.ValueAsType(objectType, _builder), objectType); + } + } + + PushNonNull(HandleCall(callee, signature, argumentValues, opcode, constrainedType, calliTarget)); } - private ExpressionEntry HandleCall(MethodDesc callee, MethodSignature signature, StackEntry[] argumentValues, ILOpcode opcode = ILOpcode.call, LLVMValueRef calliTarget = default(LLVMValueRef), TypeDesc forcedReturnType = null) + private ExpressionEntry HandleCall(MethodDesc callee, MethodSignature signature, StackEntry[] argumentValues, ILOpcode opcode = ILOpcode.call, TypeDesc constrainedType = null, LLVMValueRef calliTarget = default(LLVMValueRef), TypeDesc forcedReturnType = null) { if (opcode == ILOpcode.callvirt && callee.IsVirtual) { @@ -1159,10 +1640,12 @@ namespace Internal.IL var pointerSize = _compilation.NodeFactory.Target.PointerSize; LLVMValueRef returnAddress; - LLVMValueRef castReturnAddress; + LLVMValueRef castReturnAddress = default; TypeDesc returnType = signature.ReturnType; + + bool needsReturnSlot = NeedsReturnStackSlot(signature); SpilledExpressionEntry returnSlot = null; - if (!returnType.IsVoid) + if (needsReturnSlot) { int returnIndex = _spilledExpressions.Count; returnSlot = new SpilledExpressionEntry(GetStackValueKind(returnType), callee?.Name + "_return", returnType, returnIndex, this); @@ -1170,11 +1653,6 @@ namespace Internal.IL returnAddress = LoadVarAddress(returnIndex, LocalVarKind.Temp, out TypeDesc unused); castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), callee?.Name + "_castreturn"); } - else - { - returnAddress = LLVM.ConstNull(LLVM.PointerType(LLVM.Int8Type(), 0)); - castReturnAddress = returnAddress; - } int offset = GetTotalParameterOffset() + GetTotalLocalOffset(); LLVMValueRef shadowStack = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), @@ -1182,16 +1660,25 @@ namespace Internal.IL String.Empty); var castShadowStack = LLVM.BuildPointerCast(_builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack"); - // argument offset + List<LLVMValueRef> llvmArgs = new List<LLVMValueRef>(); + llvmArgs.Add(castShadowStack); + if (needsReturnSlot) + { + llvmArgs.Add(castReturnAddress); + } + + // argument offset on the shadow stack int argOffset = 0; var instanceAdjustment = signature.IsStatic ? 0 : 1; for (int index = 0; index < argumentValues.Length; index++) { StackEntry toStore = argumentValues[index]; + bool isThisParameter = false; TypeDesc argType; if (index == 0 && !signature.IsStatic) { + isThisParameter = true; if (opcode == ILOpcode.calli) argType = toStore.Type; else if (callee.OwningType.IsValueType) @@ -1205,10 +1692,23 @@ namespace Internal.IL } LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType); + LLVMValueRef argValue = toStore.ValueAsType(valueType, _builder); + + // Pass arguments as parameters if possible + if (!isThisParameter && CanStoreTypeOnStack(argType)) + { + llvmArgs.Add(argValue); + } + // Otherwise store them on the shadow stack + else + { + // The previous argument might have left this type unaligned, so pad if necessary + argOffset = PadOffset(argType, argOffset); - ImportStoreHelper(toStore.ValueAsType(valueType, _builder), valueType, castShadowStack, (uint)argOffset); + ImportStoreHelper(argValue, valueType, castShadowStack, (uint)argOffset); - argOffset = PadNextOffset(argType, argOffset); + argOffset += argType.GetElementSize().AsInt; + } } LLVMValueRef fn; @@ -1218,17 +1718,21 @@ namespace Internal.IL } else { - fn = LLVMFunctionForMethod(callee, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt); + fn = LLVMFunctionForMethod(callee, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt, constrainedType); } - LLVM.BuildCall(_builder, fn, new LLVMValueRef[] { - castShadowStack, - castReturnAddress}, string.Empty); - + LLVMValueRef llvmReturn = LLVM.BuildCall(_builder, fn, llvmArgs.ToArray(), string.Empty); if (!returnType.IsVoid) { - return returnSlot; + if (needsReturnSlot) + { + return returnSlot; + } + else + { + return new ExpressionEntry(GetStackValueKind(returnType), callee?.Name + "_return", llvmReturn, returnType); + } } else { @@ -1268,7 +1772,15 @@ namespace Internal.IL string realMethodName = method.Name; - if (!method.IsPInvoke && method is TypeSystem.Ecma.EcmaMethod) + if (method.IsPInvoke) + { + string entrypointName = method.GetPInvokeMethodMetadata().Name; + if(!String.IsNullOrEmpty(entrypointName)) + { + realMethodName = entrypointName; + } + } + else if (!method.IsPInvoke && method is TypeSystem.Ecma.EcmaMethod) { realMethodName = ((TypeSystem.Ecma.EcmaMethod)method).GetRuntimeImportName() ?? method.Name; } @@ -1382,25 +1894,88 @@ namespace Internal.IL LLVMTypeRef thunkSig = LLVM.FunctionType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), llvmParams, false); LLVMValueRef thunkFunc = LLVM.AddFunction(compilation.Module, nativeName, thunkSig); - LLVMBasicBlockRef block = LLVM.AppendBasicBlock(thunkFunc, "Block0"); + LLVMBasicBlockRef shadowStackSetupBlock = LLVM.AppendBasicBlock(thunkFunc, "ShadowStackSetupBlock"); + LLVMBasicBlockRef allocateShadowStackBlock = LLVM.AppendBasicBlock(thunkFunc, "allocateShadowStackBlock"); + LLVMBasicBlockRef managedCallBlock = LLVM.AppendBasicBlock(thunkFunc, "ManagedCallBlock"); + LLVMBuilderRef builder = LLVM.CreateBuilder(); - LLVM.PositionBuilderAtEnd(builder, block); + LLVM.PositionBuilderAtEnd(builder, shadowStackSetupBlock); + + // Allocate shadow stack if it's null + LLVMValueRef shadowStackPtr = LLVM.BuildAlloca(builder, LLVM.PointerType(LLVM.Int8Type(), 0), "ShadowStackPtr"); + LLVMValueRef savedShadowStack = LLVM.BuildLoad(builder, ShadowStackTop, "SavedShadowStack"); + LLVM.BuildStore(builder, savedShadowStack, shadowStackPtr); + LLVMValueRef shadowStackNull = LLVM.BuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, savedShadowStack, LLVM.ConstPointerNull(LLVM.PointerType(LLVM.Int8Type(), 0)), "ShadowStackNull"); + LLVM.BuildCondBr(builder, shadowStackNull, allocateShadowStackBlock, managedCallBlock); + + LLVM.PositionBuilderAtEnd(builder, allocateShadowStackBlock); - LLVMValueRef shadowStack = LLVM.BuildLoad(builder, ShadowStackTop, ""); + LLVMValueRef newShadowStack = LLVM.BuildArrayMalloc(builder, LLVM.Int8Type(), BuildConstInt32(1000000), "NewShadowStack"); + LLVM.BuildStore(builder, newShadowStack, shadowStackPtr); + LLVM.BuildBr(builder, managedCallBlock); + LLVM.PositionBuilderAtEnd(builder, managedCallBlock); + LLVMTypeRef reversePInvokeFrameType = LLVM.StructType(new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); + LLVMValueRef reversePInvokeFrame = default(LLVMValueRef); + LLVMTypeRef reversePInvokeFunctionType = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(reversePInvokeFrameType, 0) }, false); + if (method.IsNativeCallable) + { + reversePInvokeFrame = LLVM.BuildAlloca(builder, reversePInvokeFrameType, "ReversePInvokeFrame"); + LLVMValueRef RhpReversePInvoke2 = GetOrCreateLLVMFunction("RhpReversePInvoke2", reversePInvokeFunctionType); + LLVM.BuildCall(builder, RhpReversePInvoke2, new LLVMValueRef[] { reversePInvokeFrame }, ""); + } + + LLVMValueRef shadowStack = LLVM.BuildLoad(builder, shadowStackPtr, "ShadowStack"); int curOffset = 0; + curOffset = PadNextOffset(method.Signature.ReturnType, curOffset); + LLVMValueRef calleeFrame = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { BuildConstInt32(curOffset) }, "calleeFrame"); + + List<LLVMValueRef> llvmArgs = new List<LLVMValueRef>(); + llvmArgs.Add(calleeFrame); + + bool needsReturnSlot = NeedsReturnStackSlot(method.Signature); + + if (needsReturnSlot) + { + // Slot for return value if necessary + llvmArgs.Add(shadowStack); + } + for (int i = 0; i < llvmParams.Length; i++) { - LLVMValueRef argAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "arg" + i); - LLVM.BuildStore(builder, LLVM.GetParam(thunkFunc, (uint)i), CastIfNecessary(builder, argAddr, LLVM.PointerType(llvmParams[i], 0))); - curOffset = PadNextOffset(method.Signature[i], curOffset); + LLVMValueRef argValue = LLVM.GetParam(thunkFunc, (uint)i); + + if (CanStoreTypeOnStack(method.Signature[i])) + { + llvmArgs.Add(argValue); + } + else + { + curOffset = PadOffset(method.Signature[i], curOffset); + LLVMValueRef argAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "arg" + i); + LLVM.BuildStore(builder, argValue, CastIfNecessary(builder, argAddr, LLVM.PointerType(llvmParams[i], 0), $"parameter{i}_")); + curOffset = PadNextOffset(method.Signature[i], curOffset); + } + } + + LLVMValueRef llvmReturnValue = LLVM.BuildCall(builder, managedFunction, llvmArgs.ToArray(), ""); + + if (method.IsNativeCallable) + { + LLVMValueRef RhpReversePInvokeReturn2 = GetOrCreateLLVMFunction("RhpReversePInvokeReturn2", reversePInvokeFunctionType); + LLVM.BuildCall(builder, RhpReversePInvokeReturn2, new LLVMValueRef[] { reversePInvokeFrame }, ""); } - LLVMValueRef retAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "retAddr"); - LLVM.BuildCall(builder, managedFunction, new LLVMValueRef[] { shadowStack, retAddr }, ""); - if (method.Signature.ReturnType != compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Void)) + if (!method.Signature.ReturnType.IsVoid) { - LLVM.BuildRet(builder, LLVM.BuildLoad(builder, CastIfNecessary(builder, retAddr, LLVM.PointerType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), 0)), "")); + if (needsReturnSlot) + { + LLVM.BuildRet(builder, LLVM.BuildLoad(builder, CastIfNecessary(builder, shadowStack, LLVM.PointerType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), 0)), "returnValue")); + } + else + { + LLVM.BuildRet(builder, llvmReturnValue); + } } else { @@ -1411,7 +1986,7 @@ 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)); + HandleCall(null, methodSignature, ILOpcode.calli, calliTarget: ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVM.PointerType(GetLLVMSignatureForMethod(methodSignature), 0), _builder)); } private void ImportLdFtn(int token, ILOpcode opCode) @@ -1423,7 +1998,7 @@ namespace Internal.IL StackEntry thisPointer = _stack.Pop(); if (method.IsVirtual) { - targetLLVMFunction = LLVMFunctionForMethod(method, thisPointer, true); + targetLLVMFunction = LLVMFunctionForMethod(method, thisPointer, true, null); AddVirtualMethodReference(method); } } @@ -1434,7 +2009,7 @@ namespace Internal.IL if (targetLLVMFunction.Pointer.Equals(IntPtr.Zero)) { - targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(method).ToString()); + targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(method).ToString(), method.Signature); } var entry = new FunctionPointerEntry("ldftn", method, targetLLVMFunction, GetWellKnownType(WellKnownType.IntPtr), opCode == ILOpcode.ldvirtftn); @@ -1470,9 +2045,6 @@ namespace Internal.IL if (opcode == ILOpcode.br) { ImportFallthrough(target); - //TODO: why does this illegal branch happen in System.Reflection.MemberFilter.ctor - if (target.StartOffset == 0) - throw new InvalidProgramException(); LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(target)); } else @@ -1593,9 +2165,6 @@ namespace Internal.IL } } } - //TODO: why did this happen only during an optimized build of [System.Private.CoreLib]System.Threading.Lock.ReleaseContended - if (target.StartOffset == 0) - throw new NotImplementedException("cant branch to entry basic block"); ImportFallthrough(target); ImportFallthrough(fallthrough); @@ -1630,11 +2199,11 @@ namespace Internal.IL var expressionPointer = pointer as ExpressionEntry; if(type == null) { - type = GetWellKnownType(WellKnownType.Object).MakeByRefType(); + type = GetWellKnownType(WellKnownType.Object); } LLVMValueRef pointerElementType = pointer.ValueAsType(type.MakePointerType(), _builder); - _stack.Push(new LoadExpressionEntry(type != null ? GetStackValueKind(type) : StackValueKind.ByRef, "ldind", + _stack.Push(new LoadExpressionEntry(type != null ? GetStackValueKind(type) : StackValueKind.ByRef, $"Indirect{pointer.Name()}", pointerElementType, type)); } @@ -1827,6 +2396,7 @@ namespace Internal.IL if(enumCleanTargetType != null && targetType.IsPrimitive) { if(enumCleanTargetType.IsWellKnownType(WellKnownType.Byte) || + enumCleanTargetType.IsWellKnownType(WellKnownType.Char) || enumCleanTargetType.IsWellKnownType(WellKnownType.UInt16) || enumCleanTargetType.IsWellKnownType(WellKnownType.UInt32) || enumCleanTargetType.IsWellKnownType(WellKnownType.UInt64) || @@ -1915,26 +2485,12 @@ namespace Internal.IL private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) { StackEntry value = _stack.Pop(); - LLVMValueRef convertedValue; TypeDesc destType = GetWellKnownType(wellKnownType); - //conv.u for a pointer should change to a int8* - if (wellKnownType == WellKnownType.UIntPtr) - { - if (value.Kind == StackValueKind.Int32) - { - convertedValue = LLVM.BuildIntToPtr(_builder, value.ValueAsInt32(_builder, false), LLVM.PointerType(LLVM.Int8Type(), 0), "conv.u"); - } - else - { - convertedValue = value.ValueAsType(destType, _builder); - } - } - else - { - convertedValue = value.ValueAsType(destType, _builder); - } - PushExpression(GetStackValueKind(destType), "conv", convertedValue, destType); + // Load the value and then convert it instead of using ValueAsType to avoid loading the incorrect size + LLVMValueRef loadedValue = value.ValueAsType(value.Type, _builder); + LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name()); + PushExpression(GetStackValueKind(destType), "conv", converted, destType); } private void ImportUnaryOperation(ILOpcode opCode) @@ -2053,7 +2609,8 @@ namespace Internal.IL else if (ldtokenValue is FieldDesc) { ldtokenKind = WellKnownType.RuntimeFieldHandle; - value = new LdTokenEntry<FieldDesc>(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, LLVM.ConstInt(LLVM.Int32Type(), 0, LLVMMisc.False), GetWellKnownType(ldtokenKind)); + LLVMValueRef fieldHandle = LLVM.ConstStruct(new LLVMValueRef[] { BuildConstInt32(0) }, true); + value = new LdTokenEntry<FieldDesc>(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, fieldHandle, GetWellKnownType(ldtokenKind)); _stack.Push(value); } else if (ldtokenValue is MethodDesc) @@ -2126,6 +2683,7 @@ namespace Internal.IL private void ImportConstrainedPrefix(int token) { + _constrainedType = (TypeDesc)_methodIL.GetObject(token); } private void ImportNoPrefix(byte mask) @@ -2186,7 +2744,49 @@ namespace Internal.IL if (!isStatic) _stack.Pop(); - return WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler); + ISymbolNode node; + MetadataType owningType = (MetadataType)field.OwningType; + LLVMValueRef staticBase; + int fieldOffset = field.Offset.AsInt; + + // TODO: We need the right thread static per thread + if (field.IsThreadStatic) + { + node = _compilation.NodeFactory.TypeThreadStaticsSymbol(owningType); + staticBase = LoadAddressOfSymbolNode(node); + } + + else if (field.HasGCStaticBase) + { + node = _compilation.NodeFactory.TypeGCStaticsSymbol(owningType); + + // We can't use GCStatics in the data section until we can successfully call + // InitializeModules on startup, so stick with globals for now + //LLVMValueRef basePtrPtr = LoadAddressOfSymbolNode(node); + //staticBase = LLVM.BuildLoad(_builder, LLVM.BuildLoad(_builder, LLVM.BuildPointerCast(_builder, basePtrPtr, LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(LLVM.Int8Type(), 0), 0), 0), "castBasePtrPtr"), "basePtr"), "base"); + staticBase = WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler); + fieldOffset = 0; + } + else + { + node = _compilation.NodeFactory.TypeNonGCStaticsSymbol(owningType); + staticBase = LoadAddressOfSymbolNode(node); + } + + _dependencies.Add(node); + + // Run static constructor if necessary + // If the type is non-BeforeFieldInit, this is handled before calling any methods on it + if (owningType.IsBeforeFieldInit || (!owningType.IsBeforeFieldInit && owningType != _thisType)) + { + TriggerCctor(owningType); + } + + LLVMValueRef castStaticBase = LLVM.BuildPointerCast(_builder, staticBase, LLVM.PointerType(LLVM.Int8Type(), 0), owningType.Name + "_statics"); + LLVMValueRef fieldAddr = LLVM.BuildGEP(_builder, castStaticBase, new LLVMValueRef[] { BuildConstInt32(fieldOffset) }, field.Name + "_addr"); + + + return fieldAddr; } else { @@ -2194,18 +2794,39 @@ namespace Internal.IL } } + /// <summary> + /// Triggers a static constructor check and call for types that have them + /// </summary> + private void TriggerCctor(MetadataType type) + { + if (_compilation.TypeSystemContext.HasLazyStaticConstructor(type)) + { + ISymbolNode classConstructionContextSymbol = _compilation.NodeFactory.TypeNonGCStaticsSymbol(type); + _dependencies.Add(classConstructionContextSymbol); + LLVMValueRef firstNonGcStatic = LoadAddressOfSymbolNode(classConstructionContextSymbol); + + // TODO: Codegen could check whether it has already run rather than calling into EnsureClassConstructorRun + // but we'd have to figure out how to manage the additional basic blocks + LLVMValueRef classConstructionContextPtr = LLVM.BuildGEP(_builder, firstNonGcStatic, new LLVMValueRef[] { BuildConstInt32(-2) }, "classConstructionContext"); + StackEntry classConstructionContext = new AddressExpressionEntry(StackValueKind.NativeInt, "classConstructionContext", classConstructionContextPtr, GetWellKnownType(WellKnownType.IntPtr)); + MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System.Runtime.CompilerServices", "ClassConstructorRunner"); + MethodDesc helperMethod = helperType.GetKnownMethod("EnsureClassConstructorRun", null); + HandleCall(helperMethod, helperMethod.Signature, new StackEntry[] { classConstructionContext }); + } + } + private void ImportLoadField(int token, bool isStatic) { FieldDesc field = (FieldDesc)_methodIL.GetObject(token); LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); - PushLoadExpression(GetStackValueKind(field.FieldType), "ldfld_" + field.Name, fieldAddress, field.FieldType); + 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); - _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldflda", fieldAddress, field.FieldType.MakePointerType())); + _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, $"FieldAddress_{field.Name}", fieldAddress, field.FieldType.MakeByRefType())); } private void ImportStoreField(int token, bool isStatic) @@ -2240,8 +2861,10 @@ namespace Internal.IL TypeDesc type = ResolveTypeToken(token); var valueEntry = _stack.Pop(); var llvmType = GetLLVMTypeForTypeDesc(type); - if (llvmType.TypeKind == LLVMTypeKind.LLVMArrayTypeKind) + if (llvmType.TypeKind == LLVMTypeKind.LLVMStructTypeKind) + { ImportCallMemset(valueEntry.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), 0, type.GetElementSize().AsInt); + } else if (llvmType.TypeKind == LLVMTypeKind.LLVMIntegerTypeKind) LLVM.BuildStore(_builder, LLVM.ConstInt(llvmType, 0, LLVMMisc.False), valueEntry.ValueAsType(LLVM.PointerType(llvmType, 0), _builder)); else if (llvmType.TypeKind == LLVMTypeKind.LLVMPointerTypeKind) @@ -2319,7 +2942,7 @@ namespace Internal.IL StackEntry index = _stack.Pop(); StackEntry arrayReference = _stack.Pop(); var nullSafeElementType = elementType ?? GetWellKnownType(WellKnownType.Object); - PushLoadExpression(GetStackValueKind(nullSafeElementType), "ldelem", GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), nullSafeElementType), nullSafeElementType); + PushLoadExpression(GetStackValueKind(nullSafeElementType), $"{arrayReference.Name()}Element", GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), nullSafeElementType), nullSafeElementType); } private void ImportStoreElement(int token) @@ -2352,7 +2975,7 @@ namespace Internal.IL StackEntry index = _stack.Pop(); StackEntry arrayReference = _stack.Pop(); - PushExpression(GetStackValueKind(byRefElement), "ldelema", GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), elementType), byRefElement); + PushExpression(GetStackValueKind(byRefElement), $"{arrayReference.Name()}ElementAddress", GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), elementType), byRefElement); } private LLVMValueRef GetElementAddress(LLVMValueRef elementPosition, LLVMValueRef arrayReference, TypeDesc arrayElementType) @@ -2372,6 +2995,9 @@ namespace Internal.IL private void ImportEndFinally() { + // These are currently unreachable since we can't get into finally blocks. + // We'll need to change this once we have other finally block handling. + LLVM.BuildUnreachable(_builder); } private void ImportFallthrough(BasicBlock next) @@ -2418,9 +3044,6 @@ namespace Internal.IL if (entry == null) throw new InvalidProgramException(); - if (currentEntry is SpilledExpressionEntry) - continue; //this is already a sharable value - StoreTemp(entry.LocalIndex, currentEntry.ValueAsType(entry.Type, _builder)); } } @@ -2433,6 +3056,7 @@ namespace Internal.IL private const string RuntimeImport = "RuntimeImports"; private const string InternalCalls = "InternalCalls"; private const string TypeCast = "TypeCast"; + private const string DispatchResolve = "DispatchResolve"; private ExpressionEntry CallRuntime(TypeSystemContext context, string className, string methodName, StackEntry[] arguments, TypeDesc forcedReturnType = null) { MetadataType helperType = context.SystemModule.GetKnownType("System.Runtime", className); @@ -2453,16 +3077,11 @@ namespace Internal.IL private StackEntry NewSpillSlot(StackEntry entry) { - if (entry is SpilledExpressionEntry) - return entry; - else - { - var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal - var entryIndex = _spilledExpressions.Count; - var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "spilled" + entryIndex, entryType, entryIndex, this); - _spilledExpressions.Add(newEntry); - return newEntry; - } + var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal + var entryIndex = _spilledExpressions.Count; + var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "spilled" + entryIndex, entryType, entryIndex, this); + _spilledExpressions.Add(newEntry); + return newEntry; } private StackEntry TakeAddressOf(StackEntry entry) @@ -2535,6 +3154,7 @@ namespace Internal.IL TrapFunction = LLVM.AddFunction(Module, "llvm.trap", LLVM.FunctionType(LLVM.VoidType(), Array.Empty<LLVMTypeRef>(), false)); } LLVM.BuildCall(_builder, TrapFunction, Array.Empty<LLVMValueRef>(), string.Empty); + LLVM.BuildUnreachable(_builder); } private void EmitDoNothingCall() |