From c214863ad3e4da15fb3b56fa9c82ef359fee55fe Mon Sep 17 00:00:00 2001 From: Toni Solarin-Sodara Date: Tue, 14 Aug 2018 17:50:42 +0100 Subject: Preliminary Interpreter Support (#6182) --- .../Microsoft.NETCore.Native.targets | 15 +- .../System/Collections/Generic/LowLevelStack.cs | 161 ++++++++ .../NativeFormat/NativeFormatMetadataUnit.cs | 2 +- .../src/System.Private.Interop.Experimental.csproj | 9 + .../src/System.Private.Interop.csproj | 5 +- .../Runtime/CompilerHelpers/LibraryInitializer.cs | 17 + .../Runtime/Interpreter/ILImporter.Interpreter.cs | 447 +++++++++++++++++++++ .../Internal/Runtime/Interpreter/ILInterpreter.cs | 58 +++ .../Interpreter/InterpreterCallInterceptor.cs | 72 ++++ .../Interpreter/InterpreterExecutionStrategy.cs | 22 + .../src/Internal/Runtime/Interpreter/StackItem.cs | 60 +++ .../src/System.Private.Interpreter.csproj | 35 ++ .../Runtime/CompilerHelpers/LibraryInitializer.cs | 17 + .../src/System.Private.Jit.csproj | 7 +- ...tem.Private.Reflection.Core.Experimental.csproj | 2 +- .../src/System.Private.Reflection.Core.csproj | 3 +- .../Reflection/Runtime/General/Dispensers.cs | 2 +- .../Reflection/Execution/ReflectionExecution.cs | 4 - ...rivate.Reflection.Execution.Experimental.csproj | 2 +- .../src/System.Private.Reflection.Execution.csproj | 9 +- .../src/Internal/Reflection/Core/AssemblyBinder.cs | 2 +- .../Execution/AssemblyBinderImplementation.Ecma.cs | 8 +- .../Execution/AssemblyBinderImplementation.cs | 6 +- .../TypeLoader/JitSupport.MethodEntrypointStubs.cs | 308 -------------- .../Runtime/TypeLoader/MethodEntrypointStubs.cs | 308 ++++++++++++++ .../TypeLoaderEnvironment.LdTokenResultLookup.cs | 2 +- .../TypeLoader/TypeLoaderEnvironment.Metadata.cs | 2 +- .../TypeLoaderEnvironment.MethodAddress.cs | 2 +- .../TypeLoader/TypeLoaderTypeSystemContext.cs | 4 +- .../System.Private.TypeLoader.Experimental.csproj | 2 +- .../src/System.Private.TypeLoader.csproj | 8 +- 31 files changed, 1253 insertions(+), 348 deletions(-) create mode 100644 src/Common/src/System/Collections/Generic/LowLevelStack.cs create mode 100644 src/System.Private.Interop/src/System.Private.Interop.Experimental.csproj create mode 100644 src/System.Private.Interpreter/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs create mode 100644 src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/ILImporter.Interpreter.cs create mode 100644 src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/ILInterpreter.cs create mode 100644 src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/InterpreterCallInterceptor.cs create mode 100644 src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/InterpreterExecutionStrategy.cs create mode 100644 src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/StackItem.cs create mode 100644 src/System.Private.Interpreter/src/System.Private.Interpreter.csproj create mode 100644 src/System.Private.Jit/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs delete mode 100644 src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/JitSupport.MethodEntrypointStubs.cs create mode 100644 src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MethodEntrypointStubs.cs diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.targets b/src/BuildIntegration/Microsoft.NETCore.Native.targets index 0318a9d61..79f3a61b7 100644 --- a/src/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/BuildIntegration/Microsoft.NETCore.Native.targets @@ -80,11 +80,21 @@ See the LICENSE file in the project root for more information. + + + + + - - + + + + + + + @@ -170,6 +180,7 @@ See the LICENSE file in the project root for more information. + diff --git a/src/Common/src/System/Collections/Generic/LowLevelStack.cs b/src/Common/src/System/Collections/Generic/LowLevelStack.cs new file mode 100644 index 000000000..b193036a2 --- /dev/null +++ b/src/Common/src/System/Collections/Generic/LowLevelStack.cs @@ -0,0 +1,161 @@ +// 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. + +/*============================================================ +** +** +** Private version of Stack for internal System.Private.CoreLib use. This +** permits sharing more source between BCL and System.Private.CoreLib (as well as the +** fact that Stack is just a useful class in general.) +** +** This does not strive to implement the full api surface area +** (but any portion it does implement should match the real Stack's +** behavior.) +** +===========================================================*/ + +namespace System.Collections.Generic +{ + // Implements a variable-size Stack that uses an array of objects to store the + // elements. A Stack has a capacity, which is the allocated length + // of the internal array. As elements are added to a Stack, the capacity + // of the Stack is automatically increased as required by reallocating the + // internal array. + // + /// + /// LowLevelStack with no interface implementation to minimize both code and data size + /// Data size is smaller because there will be minimal virtual function table. + /// Code size is smaller because only functions called will be in the binary. + /// + internal class LowLevelStack + { + protected T[] _items; + protected int _size; + protected int _version; + + private static readonly T[] s_emptyArray = new T[0]; + + public LowLevelStack() + { + _items = s_emptyArray; + } + + public LowLevelStack(int capacity) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException(nameof(capacity)); + + if (capacity == 0) + _items = s_emptyArray; + else + { + _size = capacity; + _items = new T[capacity]; + } + } + + public LowLevelStack(IEnumerable collection) + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + ICollection c = collection as ICollection; + if (c != null) + { + int count = c.Count; + if (count == 0) + { + _items = s_emptyArray; + } + else + { + _items = new T[count]; + c.CopyTo(_items, 0); + _size = count; + } + } + else + { + _size = 0; + _items = s_emptyArray; + + using (IEnumerator en = collection.GetEnumerator()) + { + while (en.MoveNext()) + { + Push(en.Current); + } + } + } + } + + public int Count + { + get + { + return _items.Length; + } + } + + public void Push(T item) + { + _size = _size + 1; + Array.Resize(ref _items, _size); + _items[_size - 1] = item; + _version++; + } + + public T Pop() + { + ThrowIfEmptyStack(); + + _size = _size - 1; + T item = _items[_size]; + Array.Resize(ref _items, _size); + _version++; + return item; + } + + public bool TryPop(out T result) + { + if (_size == 0) + { + result = default; + return false; + } + + _size = _size - 1; + result = _items[_size]; + Array.Resize(ref _items, _size); + _version++; + + return true; + } + + public T Peek() + { + ThrowIfEmptyStack(); + return _items[_size - 1]; + } + + public bool TryPeek(out T result) + { + if (_size == 0) + { + result = default; + return false; + } + + result = _items[_size - 1]; + _version++; + return true; + } + + private void ThrowIfEmptyStack() + { + if (_size == 0) + throw new InvalidOperationException(); + } + } +} diff --git a/src/Common/src/TypeSystem/NativeFormat/NativeFormatMetadataUnit.cs b/src/Common/src/TypeSystem/NativeFormat/NativeFormatMetadataUnit.cs index 298e8431e..d4e9718ee 100644 --- a/src/Common/src/TypeSystem/NativeFormat/NativeFormatMetadataUnit.cs +++ b/src/Common/src/TypeSystem/NativeFormat/NativeFormatMetadataUnit.cs @@ -502,7 +502,7 @@ namespace Internal.TypeSystem.NativeFormat AssemblyBindResult bindResult; RuntimeAssemblyName assemblyName = AssemblyNameParser.Parse(assemblyNameString); Exception failureException; - if (!AssemblyBinderImplementation.Instance.Bind(assemblyName, out bindResult, out failureException)) + if (!AssemblyBinderImplementation.Instance.Bind(assemblyName, cacheMissedLookups: true, out bindResult, out failureException)) { throw failureException; } diff --git a/src/System.Private.Interop/src/System.Private.Interop.Experimental.csproj b/src/System.Private.Interop/src/System.Private.Interop.Experimental.csproj new file mode 100644 index 000000000..3fcaee81c --- /dev/null +++ b/src/System.Private.Interop/src/System.Private.Interop.Experimental.csproj @@ -0,0 +1,9 @@ + + + System.Private.Interop.Experimental + true + true + + + + diff --git a/src/System.Private.Interop/src/System.Private.Interop.csproj b/src/System.Private.Interop/src/System.Private.Interop.csproj index 73411b210..f3561abda 100644 --- a/src/System.Private.Interop/src/System.Private.Interop.csproj +++ b/src/System.Private.Interop/src/System.Private.Interop.csproj @@ -1,7 +1,7 @@ - System.Private.Interop + System.Private.Interop Library true $(DefineConstants);TARGET_CORE_API_SET @@ -21,7 +21,8 @@ true - + + diff --git a/src/System.Private.Interpreter/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.Interpreter/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 000000000..82c5b1299 --- /dev/null +++ b/src/System.Private.Interpreter/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.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. + +using Internal.Runtime.Interpreter; +using Internal.Runtime.TypeLoader; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + MethodExecutionStrategy.GlobalExecutionStrategy = new InterpreterExecutionStrategy(); + } + } +} diff --git a/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/ILImporter.Interpreter.cs b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/ILImporter.Interpreter.cs new file mode 100644 index 000000000..a86ef2d67 --- /dev/null +++ b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/ILImporter.Interpreter.cs @@ -0,0 +1,447 @@ +// 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 Internal.Runtime.Interpreter; +using Internal.TypeSystem; + +namespace Internal.IL +{ + partial class ILImporter + { + private class BasicBlock + { + // Common fields + public enum ImportState : byte + { + Unmarked, + IsPending + } + + public BasicBlock Next; + + public int StartOffset; + public ImportState State = ImportState.Unmarked; + + public bool TryStart; + public bool FilterStart; + public bool HandlerStart; + } + + private class ExceptionRegion + { + public ILExceptionRegion ILRegion; + } + + private readonly byte[] _ilBytes; + private readonly MethodDesc _method; + private readonly MethodIL _methodIL; + private readonly ILInterpreter _interpreter; + private ExceptionRegion[] _exceptionRegions; + + public ILImporter(ILInterpreter interpreter, MethodDesc method, MethodIL methodIL) + { + _ilBytes = methodIL.GetILBytes(); + _method = method; + _methodIL = methodIL; + _interpreter = interpreter; + + var ilExceptionRegions = methodIL.GetExceptionRegions(); + _exceptionRegions = new ExceptionRegion[methodIL.GetExceptionRegions().Length]; + for (int i = 0; i < ilExceptionRegions.Length; i++) + { + _exceptionRegions[i] = new ExceptionRegion() { ILRegion = ilExceptionRegions[i] }; + } + } + + public void Interpret() + { + FindBasicBlocks(); + ImportBasicBlocks(); + } + + private void MarkInstructionBoundary() { } + + private void StartImportingInstruction() { } + + private void EndImportingInstruction() { } + + private void StartImportingBasicBlock(BasicBlock basicBlock) { } + + private void EndImportingBasicBlock(BasicBlock basicBlock) { } + + private void ReportInvalidBranchTarget(int targetOffset) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportFallthroughAtEndOfMethod() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportMethodEndInsideInstruction() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportInvalidInstruction(ILOpcode opcode) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private TypeDesc ResolveTypeToken(int token) + { + return (TypeDesc)_methodIL.GetObject(token); + } + + private TypeDesc GetWellKnownType(WellKnownType wellKnownType) + { + return _interpreter.TypeSystemContext.GetWellKnownType(wellKnownType); + } + + private void ImportNop() + { + // Do nothing! + } + + private void ImportBreak() + { + throw new NotImplementedException(); + } + + private void ImportLoadVar(int index, bool argument) + { + throw new NotImplementedException(); + } + + private void ImportStoreVar(int index, bool argument) + { + throw new NotImplementedException(); + } + + private void ImportAddressOfVar(int index, bool argument) + { + throw new NotImplementedException(); + } + + private void ImportDup() + { + throw new NotImplementedException(); + } + + private void ImportPop() + { + throw new NotImplementedException(); + } + + private void ImportCalli(int token) + { + throw new NotImplementedException(); + } + + private void ImportLoadNull() + { + throw new NotImplementedException(); + } + + private void ImportReturn() + { + bool hasReturnValue = _interpreter.EvaluationStack.TryPop(out StackItem stackItem); + if (!hasReturnValue) + return; + + switch (stackItem.Kind) + { + case StackValueKind.Int32: + _interpreter.SetReturnValue(((Int32StackItem)stackItem).Value); + break; + case StackValueKind.Int64: + _interpreter.SetReturnValue(((Int64StackItem)stackItem).Value); + break; + case StackValueKind.Unknown: + case StackValueKind.NativeInt: + case StackValueKind.Float: + case StackValueKind.ByRef: + case StackValueKind.ObjRef: + case StackValueKind.ValueType: + default: + break; + } + } + + private void ImportLoadInt(long value, StackValueKind kind) + { + if (kind == StackValueKind.Int32) + _interpreter.EvaluationStack.Push(new Int32StackItem((int)value)); + else if (kind == StackValueKind.Int64) + _interpreter.EvaluationStack.Push(new Int64StackItem(value)); + } + + private void ImportLoadFloat(double value) + { + throw new NotImplementedException(); + } + + private void ImportShiftOperation(ILOpcode opcode) + { + throw new NotImplementedException(); + } + + private void ImportCompareOperation(ILOpcode opcode) + { + throw new NotImplementedException(); + } + + private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) + { + throw new NotImplementedException(); + } + + private void ImportUnaryOperation(ILOpcode opCode) + { + throw new NotImplementedException(); + } + + private void ImportCpOpj(int token) + { + throw new NotImplementedException(); + } + + private void ImportCkFinite() + { + throw new NotImplementedException(); + } + + private void ImportLocalAlloc() + { + throw new NotImplementedException(); + } + + private void ImportEndFilter() + { + throw new NotImplementedException(); + } + + private void ImportCpBlk() + { + throw new NotImplementedException(); + } + + private void ImportInitBlk() + { + throw new NotImplementedException(); + } + + private void ImportRethrow() + { + throw new NotImplementedException(); + } + + private void ImportSizeOf(int token) + { + throw new NotImplementedException(); + } + + private void ImportUnalignedPrefix(byte alignment) + { + throw new NotImplementedException(); + } + + private void ImportVolatilePrefix() + { + throw new NotImplementedException(); + } + + private void ImportTailPrefix() + { + throw new NotImplementedException(); + } + + private void ImportNoPrefix(byte mask) + { + throw new NotImplementedException(); + } + + private void ImportThrow() + { + throw new NotImplementedException(); + } + + private void ImportInitObj(int token) + { + throw new NotImplementedException(); + } + + private void ImportLoadLength() + { + throw new NotImplementedException(); + } + + private void ImportEndFinally() + { + throw new NotImplementedException(); + } + + private void ImportFallthrough(BasicBlock nextBasicBlock) + { + throw new NotImplementedException(); + } + + private void ImportReadOnlyPrefix() + { + throw new NotImplementedException(); + } + + private void ImportRefAnyType() + { + throw new NotImplementedException(); + } + + private void ImportConstrainedPrefix(int v) + { + throw new NotImplementedException(); + } + + private void ImportLdFtn(int v, ILOpcode opCode) + { + throw new NotImplementedException(); + } + + private void ImportArgList() + { + throw new NotImplementedException(); + } + + private void ImportLeave(BasicBlock basicBlock) + { + throw new NotImplementedException(); + } + + private void ImportLdToken(int v) + { + throw new NotImplementedException(); + } + + private void ImportMkRefAny(int v) + { + throw new NotImplementedException(); + } + + private void ImportRefAnyVal(int v) + { + throw new NotImplementedException(); + } + + private void ImportAddressOfElement(int v) + { + throw new NotImplementedException(); + } + + private void ImportNewArray(int v) + { + throw new NotImplementedException(); + } + + private void ImportBox(int v) + { + throw new NotImplementedException(); + } + + private void ImportStoreField(int v1, bool v2) + { + throw new NotImplementedException(); + } + + private void ImportAddressOfField(int v1, bool v2) + { + throw new NotImplementedException(); + } + + private void ImportLoadField(int v1, bool v2) + { + throw new NotImplementedException(); + } + + private void ImportUnbox(int v, ILOpcode opCode) + { + throw new NotImplementedException(); + } + + private void ImportCasting(ILOpcode opCode, int v) + { + throw new NotImplementedException(); + } + + private void ImportLoadString(int v) + { + throw new NotImplementedException(); + } + + private void ImportBinaryOperation(ILOpcode opCode) + { + throw new NotImplementedException(); + } + + private void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock basicBlock) + { + throw new NotImplementedException(); + } + + private void ImportBranch(ILOpcode iLOpcode, BasicBlock basicBlock1, BasicBlock basicBlock2) + { + throw new NotImplementedException(); + } + + private void ImportCall(ILOpcode opCode, int v) + { + throw new NotImplementedException(); + } + + private void ImportJmp(int v) + { + throw new NotImplementedException(); + } + + private void ImportLoadIndirect(int token) + { + ImportLoadIndirect(ResolveTypeToken(token)); + } + + private void ImportLoadIndirect(TypeDesc type) + { + throw new NotImplementedException(); + } + + private void ImportStoreIndirect(int token) + { + ImportStoreIndirect(ResolveTypeToken(token)); + } + + private void ImportStoreIndirect(TypeDesc type) + { + throw new NotImplementedException(); + } + + private void ImportLoadElement(int token) + { + ImportLoadElement(ResolveTypeToken(token)); + } + + private void ImportLoadElement(TypeDesc elementType) + { + throw new NotImplementedException(); + } + + private void ImportStoreElement(int token) + { + ImportStoreElement(ResolveTypeToken(token)); + } + + private void ImportStoreElement(TypeDesc elementType) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/ILInterpreter.cs b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/ILInterpreter.cs new file mode 100644 index 000000000..d78611ec9 --- /dev/null +++ b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/ILInterpreter.cs @@ -0,0 +1,58 @@ +// 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.Collections.Generic; + +using Internal.IL; +using Internal.Runtime.CallInterceptor; +using Internal.TypeSystem; + +namespace Internal.Runtime.Interpreter +{ + internal unsafe class ILInterpreter + { + private readonly MethodDesc _method; + private readonly MethodIL _methodIL; + private readonly TypeSystemContext _context; + private readonly LowLevelStack _stack; + + private CallInterceptorArgs _callInterceptorArgs; + + public LowLevelStack EvaluationStack + { + get + { + return _stack; + } + } + + public TypeSystemContext TypeSystemContext + { + get + { + return _context; + } + } + + public ILInterpreter(TypeSystemContext context, MethodDesc method, MethodIL methodIL) + { + _context = context; + _method = method; + _methodIL = methodIL; + _stack = new LowLevelStack(); + } + + public void InterpretMethod(ref CallInterceptorArgs callInterceptorArgs) + { + _callInterceptorArgs = callInterceptorArgs; + ILImporter importer = new ILImporter(this, _method, _methodIL); + importer.Interpret(); + } + + public void SetReturnValue(T value) + { + _callInterceptorArgs.ArgumentsAndReturnValue.SetVar(0, value); + } + } +} diff --git a/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/InterpreterCallInterceptor.cs b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/InterpreterCallInterceptor.cs new file mode 100644 index 000000000..3703d213b --- /dev/null +++ b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/InterpreterCallInterceptor.cs @@ -0,0 +1,72 @@ +// 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.IL; +using Internal.Runtime.CallConverter; +using Internal.Runtime.CallInterceptor; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace Internal.Runtime.Interpreter +{ + public class InterpreterCallInterceptor : CallInterceptor.CallInterceptor + { + private readonly MethodDesc _method; + private readonly MethodIL _methodIL; + private readonly TypeSystemContext _context; + + public InterpreterCallInterceptor(TypeSystemContext context, MethodDesc method) : base(false) + { + _context = context; + _method = method; + _methodIL = EcmaMethodIL.Create(method as EcmaMethod); + } + + public override LocalVariableType[] ArgumentAndReturnTypes + { + get + { + LocalVariableType[] localVariableTypes = new LocalVariableType[_method.Signature.Length + 1]; + localVariableTypes[0] = new LocalVariableType(_method.Signature.ReturnType.RuntimeTypeHandle, false, _method.Signature.ReturnType.IsByRef); + for (int i = 0; i < _method.Signature.Length; i++) + { + var argument = _method.Signature[i]; + localVariableTypes[i + 1] = new LocalVariableType(argument.RuntimeTypeHandle, false, argument.IsByRef); + } + + return localVariableTypes; + } + } + + public override CallingConvention CallingConvention + { + get + { + return _method.Signature.IsStatic ? CallingConvention.ManagedStatic : CallingConvention.ManagedInstance; + } + } + + public override LocalVariableType[] LocalVariableTypes + { + get + { + LocalVariableDefinition[] locals = _methodIL.GetLocals(); + LocalVariableType[] localVariableTypes = new LocalVariableType[locals.Length]; + for (int i = 0; i < locals.Length; i++) + { + var variable = locals[i]; + localVariableTypes[i] = new LocalVariableType(variable.Type.RuntimeTypeHandle, variable.IsPinned, variable.Type.IsByRef); + } + + return localVariableTypes; + } + } + + public override void ThunkExecute(ref CallInterceptorArgs callInterceptorArgs) + { + ILInterpreter interpreter = new ILInterpreter(_context, _method, _methodIL); + interpreter.InterpretMethod(ref callInterceptorArgs); + } + } +} diff --git a/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/InterpreterExecutionStrategy.cs b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/InterpreterExecutionStrategy.cs new file mode 100644 index 000000000..7fa3a5742 --- /dev/null +++ b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/InterpreterExecutionStrategy.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 System; + +using Internal.Runtime.TypeLoader; +using Internal.TypeSystem; + +namespace Internal.Runtime.Interpreter +{ + public class InterpreterExecutionStrategy : MethodExecutionStrategy + { + public override IntPtr OnEntryPoint(MethodEntrypointPtr methodEntrypointPtr, IntPtr callerArgumentsInfo) + { + var context = TypeSystemContextFactory.Create(); + MethodDesc method = methodEntrypointPtr.MethodIdentifier.ToMethodDesc(context); + InterpreterCallInterceptor callInterceptor = new InterpreterCallInterceptor(context, method); + return callInterceptor.GetThunkAddress(); + } + } +} diff --git a/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/StackItem.cs b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/StackItem.cs new file mode 100644 index 000000000..b35b8bb05 --- /dev/null +++ b/src/System.Private.Interpreter/src/Internal/Runtime/Interpreter/StackItem.cs @@ -0,0 +1,60 @@ +// 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 Internal.IL; + +namespace Internal.Runtime.Interpreter +{ + internal abstract class StackItem + { + public StackValueKind Kind { get; set; } + } + + internal class StackItem : StackItem + { + public T Value { get; } + + public StackItem(T value, StackValueKind kind) + { + Value = value; + Kind = kind; + } + } + + internal class Int32StackItem : StackItem + { + public Int32StackItem(int value) : base(value, StackValueKind.Int32) + { + } + } + + internal class Int64StackItem : StackItem + { + public Int64StackItem(long value) : base(value, StackValueKind.Int64) + { + } + } + + internal class FloatStackItem : StackItem + { + public FloatStackItem(double value) : base(value, StackValueKind.Float) + { + } + } + + internal class ValueTypeStackItem : StackItem + { + public ValueTypeStackItem(ValueType value) : base(value, StackValueKind.ValueType) + { + } + } + + internal class ObjectRefStackItem : StackItem + { + public ObjectRefStackItem(Object value) : base(value, StackValueKind.ObjRef) + { + } + } +} diff --git a/src/System.Private.Interpreter/src/System.Private.Interpreter.csproj b/src/System.Private.Interpreter/src/System.Private.Interpreter.csproj new file mode 100644 index 000000000..3808516a4 --- /dev/null +++ b/src/System.Private.Interpreter/src/System.Private.Interpreter.csproj @@ -0,0 +1,35 @@ + + + + System.Private.Interpreter + 4.0.0.0 + Library + true + + + + false + true + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/System.Private.Jit/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.Jit/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 000000000..ff2abd9e8 --- /dev/null +++ b/src/System.Private.Jit/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.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. + +using Internal.Runtime.JitSupport; +using Internal.Runtime.TypeLoader; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + MethodExecutionStrategy.GlobalExecutionStrategy = new RyuJitExecutionStrategy(); + } + } +} diff --git a/src/System.Private.Jit/src/System.Private.Jit.csproj b/src/System.Private.Jit/src/System.Private.Jit.csproj index f49426928..942a0b74b 100644 --- a/src/System.Private.Jit/src/System.Private.Jit.csproj +++ b/src/System.Private.Jit/src/System.Private.Jit.csproj @@ -1,4 +1,4 @@ - + System.Private.Jit @@ -31,7 +31,7 @@ - + ..\..\Common\src\Internal\NativeFormat @@ -43,6 +43,7 @@ ..\..\JitInterface\src + @@ -155,4 +156,4 @@ - + \ No newline at end of file diff --git a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.Experimental.csproj b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.Experimental.csproj index 75f14057a..2cc7b4b44 100644 --- a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.Experimental.csproj +++ b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.Experimental.csproj @@ -2,7 +2,7 @@ System.Private.Reflection.Core.Experimental true - true + true diff --git a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj index 696f74096..014a80f7f 100644 --- a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj +++ b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj @@ -24,14 +24,15 @@ true - + + diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs index 79eca8868..90d5f4803 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs @@ -97,7 +97,7 @@ namespace System.Reflection.Runtime.Assemblies AssemblyBinder binder = ReflectionCoreExecution.ExecutionDomain.ReflectionDomainSetup.AssemblyBinder; AssemblyBindResult bindResult; Exception exception; - if (!binder.Bind(assemblyRefName, out bindResult, out exception)) + if (!binder.Bind(assemblyRefName, cacheMissedLookups: true, out bindResult, out exception)) return exception; return GetRuntimeAssembly(bindResult); diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs index f0261574f..c8b4a019e 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs @@ -64,10 +64,6 @@ namespace Internal.Reflection.Execution }; ExecutionEnvironment = executionEnvironment; - -#if SUPPORT_JIT - Internal.Runtime.TypeLoader.MethodExecutionStrategy.GlobalExecutionStrategy = new Internal.Runtime.JitSupport.RyuJitExecutionStrategy(); -#endif } // diff --git a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.Experimental.csproj b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.Experimental.csproj index 069aa60a9..d70b8fc7a 100644 --- a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.Experimental.csproj +++ b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.Experimental.csproj @@ -2,7 +2,7 @@ System.Private.Reflection.Execution.Experimental true - true + true diff --git a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj index 5b935ef32..ed737d170 100644 --- a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj +++ b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj @@ -13,9 +13,6 @@ ECMA_METADATA_SUPPORT;$(DefineConstants) - - SUPPORT_JIT;$(DefineConstants) - @@ -28,17 +25,18 @@ global,System_Private_CoreLib - - + + + @@ -95,7 +93,6 @@ - diff --git a/src/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs b/src/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs index 75cb21e54..e346f5f6b 100644 --- a/src/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs +++ b/src/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs @@ -30,7 +30,7 @@ namespace Internal.Reflection.Core { public const String DefaultAssemblyNameForGetType = "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; - public abstract bool Bind(RuntimeAssemblyName refName, out AssemblyBindResult result, out Exception exception); + public abstract bool Bind(RuntimeAssemblyName refName, bool cacheMissedLookups, out AssemblyBindResult result, out Exception exception); public abstract bool Bind(byte[] rawAssembly, byte[] rawSymbolStore, out AssemblyBindResult result, out Exception exception); diff --git a/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs b/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs index c6342da01..444538f55 100644 --- a/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs +++ b/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs @@ -59,7 +59,7 @@ namespace Internal.Reflection.Execution lock(s_ecmaLoadedAssemblies) { // 3. Attempt to bind to already loaded assembly - if (Bind(runtimeAssemblyName, out bindResult, out exception)) + if (Bind(runtimeAssemblyName, cacheMissedLookups: false, out bindResult, out exception)) { result = true; return; @@ -75,7 +75,7 @@ namespace Internal.Reflection.Execution moduleList.RegisterModule(newModuleInfo); // 5. Then try to load by name again. This load should always succeed - if (Bind(runtimeAssemblyName, out bindResult, out exception)) + if (Bind(runtimeAssemblyName, cacheMissedLookups: true, out bindResult, out exception)) { result = true; return; @@ -86,7 +86,7 @@ namespace Internal.Reflection.Execution } } - partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool foundMatch) + partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, bool cacheMissedLookups, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool foundMatch) { lock(s_ecmaLoadedAssemblies) { @@ -186,7 +186,7 @@ namespace Internal.Reflection.Execution } // Cache missed lookups - if (!foundMatch) + if (cacheMissedLookups && !foundMatch) { PEInfo peinfo = new PEInfo(refName, null, null); s_ecmaLoadedAssemblies.Add(peinfo); diff --git a/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs b/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs index 701160534..e2fcc6a57 100644 --- a/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs +++ b/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs @@ -35,7 +35,7 @@ namespace Internal.Reflection.Execution public static AssemblyBinderImplementation Instance { get; } = new AssemblyBinderImplementation(); partial void BindEcmaByteArray(byte[] rawAssembly, byte[] rawSymbolStore, ref AssemblyBindResult bindResult, ref Exception exception, ref bool? result); - partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool resultBoolean); + partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, bool cacheMissedLookups, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool resultBoolean); partial void InsertEcmaLoadedAssemblies(List loadedAssemblies); public sealed override bool Bind(byte[] rawAssembly, byte[] rawSymbolStore, out AssemblyBindResult bindResult, out Exception exception) @@ -53,7 +53,7 @@ namespace Internal.Reflection.Execution return result.Value; } - public sealed override bool Bind(RuntimeAssemblyName refName, out AssemblyBindResult result, out Exception exception) + public sealed override bool Bind(RuntimeAssemblyName refName, bool cacheMissedLookups, out AssemblyBindResult result, out Exception exception) { bool foundMatch = false; result = default(AssemblyBindResult); @@ -102,7 +102,7 @@ namespace Internal.Reflection.Execution } } - BindEcmaAssemblyName(refName, ref result, ref exception, ref preferredException, ref foundMatch); + BindEcmaAssemblyName(refName, cacheMissedLookups, ref result, ref exception, ref preferredException, ref foundMatch); if (exception != null) return false; diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/JitSupport.MethodEntrypointStubs.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/JitSupport.MethodEntrypointStubs.cs deleted file mode 100644 index 6f0c2927c..000000000 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/JitSupport.MethodEntrypointStubs.cs +++ /dev/null @@ -1,308 +0,0 @@ -// 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.Runtime; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; - -using Internal.Runtime; -using Internal.Runtime.Augments; -using Internal.Runtime.CompilerServices; - -using Internal.NativeFormat; -using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; -using Internal.TypeSystem.NativeFormat; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.Runtime.General; - -namespace Internal.Runtime.TypeLoader -{ - // Extensibility api to allow a method execution strategy to be defined by a module that depends - // on this module. The GlobalExecutionStrategy static variable is expected to be assigned once per process - // with whatever the execution strategy is. - public abstract class MethodExecutionStrategy - { - public static MethodExecutionStrategy GlobalExecutionStrategy; - public abstract IntPtr OnEntryPoint(MethodEntrypointPtr entrypointInfo, IntPtr callerArgumentsInfo); - } - - internal struct MethodEntrypointData - { - public MethodEntrypointData(RuntimeMethodHandle methodIdentifier, IntPtr methodEntrypointThunk) - { - MethodIdentifier = methodIdentifier; - MethodCode = IntPtr.Zero; - MethodEntrypointThunk = methodEntrypointThunk; - } - - public readonly RuntimeMethodHandle MethodIdentifier; - public IntPtr MethodCode; - public readonly IntPtr MethodEntrypointThunk; - } - - public unsafe struct MethodEntrypointPtr - { - private static object s_thunkPoolHeap; - internal static void SetThunkPool(object thunkPoolHeap) { s_thunkPoolHeap = thunkPoolHeap; } - - internal MethodEntrypointPtr(MethodEntrypointData *data) - { - _data = data; - } - - internal MethodEntrypointPtr(IntPtr ptr) - { - _data = (MethodEntrypointData*)ptr.ToPointer(); - } - - private MethodEntrypointData *_data; - public RuntimeMethodHandle MethodIdentifier { get { return _data->MethodIdentifier; } } - public IntPtr MethodCode - { - get - { - return _data->MethodCode; - } - set - { - _data->MethodCode = value; - RuntimeAugments.SetThunkData(s_thunkPoolHeap, _data->MethodEntrypointThunk, value, new IntPtr(_data)); - } - } - - public IntPtr MethodEntrypointThunk { get { return _data->MethodEntrypointThunk; } } - - public IntPtr ToIntPtr() - { - return new IntPtr(_data); - } - } - - public static class MethodEntrypointStubs - { - private static unsafe IntPtr RuntimeMethodHandleToIntPtr(RuntimeMethodHandle rmh) - { - return *(IntPtr*)&rmh; - } - private static unsafe RuntimeMethodHandle IntPtrToRuntimeMethodHandle(IntPtr rmh) - { - RuntimeMethodHandle handle = default(RuntimeMethodHandle); - RuntimeMethodHandle* pRMH = &handle; - *((IntPtr*)pRMH) = rmh; - - return handle; - } - - private static unsafe RuntimeTypeHandle IntPtrToRuntimeTypeHandle(IntPtr rtth) - { - RuntimeTypeHandle handle = default(RuntimeTypeHandle); - RuntimeTypeHandle* pRTTH = &handle; - *((IntPtr*)pRTTH) = rtth; - - return handle; - } - - private struct MethodEntrypointLookup - { - public MethodEntrypointLookup(MethodDesc method) - { - _method = method; - - RuntimeTypeHandle declaringTypeHandle = method.OwningType.GetRuntimeTypeHandle(); - RuntimeSignature methodSignature; - if (!RuntimeSignatureHelper.TryCreate(method, out methodSignature)) - { - Environment.FailFast("Unable to create method signature"); - } - RuntimeTypeHandle[] genericMethodArgs = null; - if (method.Instantiation.Length != 0) - { - genericMethodArgs = new RuntimeTypeHandle[method.Instantiation.Length]; - for (int i = 0; i < genericMethodArgs.Length; i++) - { - genericMethodArgs[i] = method.Instantiation[i].GetRuntimeTypeHandle(); - } - } - - _rmh = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(declaringTypeHandle, - IntPtr.Zero, - methodSignature, - genericMethodArgs); - } - - public MethodDesc Method => _method; - public RuntimeMethodHandle MethodHandle => _rmh; - - private RuntimeMethodHandle _rmh; - MethodDesc _method; - } - - private unsafe class MethodEntrypointHash : LockFreeReaderHashtableOfPointers - { - TypeLoaderEnvironment _tle = TypeLoaderEnvironment.Instance; - - /// - /// Given a key, compute a hash code. This function must be thread safe. - /// - protected override int GetKeyHashCode(MethodEntrypointLookup key) - { - return key.MethodHandle.GetHashCode(); - } - - /// - /// Given a value, compute a hash code which would be identical to the hash code - /// for a key which should look up this value. This function must be thread safe. - /// - protected override unsafe int GetValueHashCode(MethodEntrypointPtr value) - { - return value.MethodIdentifier.GetHashCode(); - } - - /// - /// Compare a key and value. If the key refers to this value, return true. - /// This function must be thread safe. - /// - protected override bool CompareKeyToValue(MethodEntrypointLookup key, MethodEntrypointPtr value) - { - return value.MethodIdentifier.Equals(key.MethodHandle); - } - - /// - /// Compare a value with another value. Return true if values are equal. - /// This function must be thread safe. - /// - protected override bool CompareValueToValue(MethodEntrypointPtr value1, MethodEntrypointPtr value2) - { - return value1.MethodIdentifier.Equals(value2.MethodIdentifier); - } - - [DllImport("*", ExactSpelling = true, EntryPoint = "MethodEntrypointStubs_SetupPointers")] - private unsafe extern static IntPtr MethodEntrypointStubs_SetupPointers(IntPtr universalTransitionThunk, IntPtr methodEntrypoint); - - private static object s_thunkPoolHeap; - private static IntPtr s_entryPointStub = SetupMethodEntrypoints(); - - private static IntPtr SetupMethodEntrypoints() - { - return MethodEntrypointStubs_SetupPointers(RuntimeAugments.GetUniversalTransitionThunk(), - Intrinsics.AddrOf>(EntrypointThunk)); - } - - unsafe private static IntPtr EntrypointThunk(IntPtr callerTransitionBlockParam, IntPtr entrypointData) - { - MethodEntrypointPtr entryPointPointer = new MethodEntrypointPtr(entrypointData); - return MethodExecutionStrategy.GlobalExecutionStrategy.OnEntryPoint(entryPointPointer, callerTransitionBlockParam); - } - - /// - /// Create a new value from a key. Must be threadsafe. Value may or may not be added - /// to collection. Return value must not be null. - /// - protected override unsafe MethodEntrypointPtr CreateValueFromKey(MethodEntrypointLookup key) - { - lock (this) - { - IntPtr thunk = IntPtr.Zero; - if (s_thunkPoolHeap == null) - { - s_thunkPoolHeap = RuntimeAugments.CreateThunksHeap(s_entryPointStub); - MethodEntrypointPtr.SetThunkPool(s_thunkPoolHeap); - Debug.Assert(s_thunkPoolHeap != null); - } - - thunk = RuntimeAugments.AllocateThunk(s_thunkPoolHeap); - Debug.Assert(thunk != IntPtr.Zero); - MethodEntrypointData *methodEntrypointData = (MethodEntrypointData*)MemoryHelpers.AllocateMemory(sizeof(MethodEntrypointData)); - - *methodEntrypointData = new MethodEntrypointData(key.MethodHandle, thunk); - - RuntimeAugments.SetThunkData(s_thunkPoolHeap, thunk, IntPtr.Zero, new IntPtr(methodEntrypointData)); - - SerializedDebugData.RegisterTailCallThunk(thunk); - - return new MethodEntrypointPtr(methodEntrypointData); - } - } - - /// - /// Convert a value to an IntPtr for storage into the hashtable - /// - protected override IntPtr ConvertValueToIntPtr(MethodEntrypointPtr value) - { - return value.ToIntPtr(); - } - - /// - /// Convert an IntPtr into a value for comparisions, or for returning. - /// - protected override MethodEntrypointPtr ConvertIntPtrToValue(IntPtr pointer) - { - return new MethodEntrypointPtr(pointer); - } - } - - private static MethodEntrypointHash s_methodEntrypointHash = new MethodEntrypointHash(); - - public static bool TryGetMethodEntrypoint(MethodDesc methodOnType, out IntPtr entryPoint, out IntPtr unboxingStubAddress, out TypeLoaderEnvironment.MethodAddressType foundAddressType) - { - MethodDesc typicalMethod = methodOnType.GetTypicalMethodDefinition(); - - if (!(typicalMethod is EcmaMethod)) - { - foundAddressType = TypeLoaderEnvironment.MethodAddressType.None; - entryPoint = IntPtr.Zero; - unboxingStubAddress = IntPtr.Zero; - return false; - } - - // OK, this is a method entrypoint for an ecma method - EcmaMethod ecmaTypicalMethod = (EcmaMethod)typicalMethod; - - // Canonicalize - MethodDesc canonMethod = methodOnType.GetCanonMethodTarget(CanonicalFormKind.Specific); - if (canonMethod != methodOnType) - foundAddressType = TypeLoaderEnvironment.MethodAddressType.Canonical; - else - foundAddressType = TypeLoaderEnvironment.MethodAddressType.Exact; - - - // Check to see if we should produce an unboxing stub entrypoint - - unboxingStubAddress = IntPtr.Zero; // Optimistically choose not to - if (ecmaTypicalMethod.OwningType.IsValueType) - { - MethodSignature methodSig = ecmaTypicalMethod.Signature; - if (!methodSig.IsStatic) - unboxingStubAddress = new IntPtr(5); // TODO Actually implement the unboxing stub logic - } - - // Ensure RuntimeTypeHandles for owningType, and for instantiation types - // They should be there, as the paths to this function should ensure it, but its a bit sketchy - // as we don't have an opportunity to easily compute them now - if (!canonMethod.OwningType.RetrieveRuntimeTypeHandleIfPossible()) - Environment.FailFast("Did not pre-allocate owning type typehandle"); - - foreach (TypeDesc type in canonMethod.Instantiation) - { - if (!type.RetrieveRuntimeTypeHandleIfPossible()) - Environment.FailFast("Did not pre-allocate instantiation type typehandle"); - } - - // We need to create a RuntimeMethodHandle for this method - MethodEntrypointPtr entrypoint = s_methodEntrypointHash.GetOrCreateValue(new MethodEntrypointLookup(canonMethod)); - if (entrypoint.MethodCode != IntPtr.Zero) - entryPoint = entrypoint.MethodCode; - else - entryPoint = entrypoint.MethodEntrypointThunk; - - return true; - } - } -} \ No newline at end of file diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MethodEntrypointStubs.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MethodEntrypointStubs.cs new file mode 100644 index 000000000..6f0c2927c --- /dev/null +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MethodEntrypointStubs.cs @@ -0,0 +1,308 @@ +// 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.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem.NativeFormat; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.Runtime.General; + +namespace Internal.Runtime.TypeLoader +{ + // Extensibility api to allow a method execution strategy to be defined by a module that depends + // on this module. The GlobalExecutionStrategy static variable is expected to be assigned once per process + // with whatever the execution strategy is. + public abstract class MethodExecutionStrategy + { + public static MethodExecutionStrategy GlobalExecutionStrategy; + public abstract IntPtr OnEntryPoint(MethodEntrypointPtr entrypointInfo, IntPtr callerArgumentsInfo); + } + + internal struct MethodEntrypointData + { + public MethodEntrypointData(RuntimeMethodHandle methodIdentifier, IntPtr methodEntrypointThunk) + { + MethodIdentifier = methodIdentifier; + MethodCode = IntPtr.Zero; + MethodEntrypointThunk = methodEntrypointThunk; + } + + public readonly RuntimeMethodHandle MethodIdentifier; + public IntPtr MethodCode; + public readonly IntPtr MethodEntrypointThunk; + } + + public unsafe struct MethodEntrypointPtr + { + private static object s_thunkPoolHeap; + internal static void SetThunkPool(object thunkPoolHeap) { s_thunkPoolHeap = thunkPoolHeap; } + + internal MethodEntrypointPtr(MethodEntrypointData *data) + { + _data = data; + } + + internal MethodEntrypointPtr(IntPtr ptr) + { + _data = (MethodEntrypointData*)ptr.ToPointer(); + } + + private MethodEntrypointData *_data; + public RuntimeMethodHandle MethodIdentifier { get { return _data->MethodIdentifier; } } + public IntPtr MethodCode + { + get + { + return _data->MethodCode; + } + set + { + _data->MethodCode = value; + RuntimeAugments.SetThunkData(s_thunkPoolHeap, _data->MethodEntrypointThunk, value, new IntPtr(_data)); + } + } + + public IntPtr MethodEntrypointThunk { get { return _data->MethodEntrypointThunk; } } + + public IntPtr ToIntPtr() + { + return new IntPtr(_data); + } + } + + public static class MethodEntrypointStubs + { + private static unsafe IntPtr RuntimeMethodHandleToIntPtr(RuntimeMethodHandle rmh) + { + return *(IntPtr*)&rmh; + } + private static unsafe RuntimeMethodHandle IntPtrToRuntimeMethodHandle(IntPtr rmh) + { + RuntimeMethodHandle handle = default(RuntimeMethodHandle); + RuntimeMethodHandle* pRMH = &handle; + *((IntPtr*)pRMH) = rmh; + + return handle; + } + + private static unsafe RuntimeTypeHandle IntPtrToRuntimeTypeHandle(IntPtr rtth) + { + RuntimeTypeHandle handle = default(RuntimeTypeHandle); + RuntimeTypeHandle* pRTTH = &handle; + *((IntPtr*)pRTTH) = rtth; + + return handle; + } + + private struct MethodEntrypointLookup + { + public MethodEntrypointLookup(MethodDesc method) + { + _method = method; + + RuntimeTypeHandle declaringTypeHandle = method.OwningType.GetRuntimeTypeHandle(); + RuntimeSignature methodSignature; + if (!RuntimeSignatureHelper.TryCreate(method, out methodSignature)) + { + Environment.FailFast("Unable to create method signature"); + } + RuntimeTypeHandle[] genericMethodArgs = null; + if (method.Instantiation.Length != 0) + { + genericMethodArgs = new RuntimeTypeHandle[method.Instantiation.Length]; + for (int i = 0; i < genericMethodArgs.Length; i++) + { + genericMethodArgs[i] = method.Instantiation[i].GetRuntimeTypeHandle(); + } + } + + _rmh = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(declaringTypeHandle, + IntPtr.Zero, + methodSignature, + genericMethodArgs); + } + + public MethodDesc Method => _method; + public RuntimeMethodHandle MethodHandle => _rmh; + + private RuntimeMethodHandle _rmh; + MethodDesc _method; + } + + private unsafe class MethodEntrypointHash : LockFreeReaderHashtableOfPointers + { + TypeLoaderEnvironment _tle = TypeLoaderEnvironment.Instance; + + /// + /// Given a key, compute a hash code. This function must be thread safe. + /// + protected override int GetKeyHashCode(MethodEntrypointLookup key) + { + return key.MethodHandle.GetHashCode(); + } + + /// + /// Given a value, compute a hash code which would be identical to the hash code + /// for a key which should look up this value. This function must be thread safe. + /// + protected override unsafe int GetValueHashCode(MethodEntrypointPtr value) + { + return value.MethodIdentifier.GetHashCode(); + } + + /// + /// Compare a key and value. If the key refers to this value, return true. + /// This function must be thread safe. + /// + protected override bool CompareKeyToValue(MethodEntrypointLookup key, MethodEntrypointPtr value) + { + return value.MethodIdentifier.Equals(key.MethodHandle); + } + + /// + /// Compare a value with another value. Return true if values are equal. + /// This function must be thread safe. + /// + protected override bool CompareValueToValue(MethodEntrypointPtr value1, MethodEntrypointPtr value2) + { + return value1.MethodIdentifier.Equals(value2.MethodIdentifier); + } + + [DllImport("*", ExactSpelling = true, EntryPoint = "MethodEntrypointStubs_SetupPointers")] + private unsafe extern static IntPtr MethodEntrypointStubs_SetupPointers(IntPtr universalTransitionThunk, IntPtr methodEntrypoint); + + private static object s_thunkPoolHeap; + private static IntPtr s_entryPointStub = SetupMethodEntrypoints(); + + private static IntPtr SetupMethodEntrypoints() + { + return MethodEntrypointStubs_SetupPointers(RuntimeAugments.GetUniversalTransitionThunk(), + Intrinsics.AddrOf>(EntrypointThunk)); + } + + unsafe private static IntPtr EntrypointThunk(IntPtr callerTransitionBlockParam, IntPtr entrypointData) + { + MethodEntrypointPtr entryPointPointer = new MethodEntrypointPtr(entrypointData); + return MethodExecutionStrategy.GlobalExecutionStrategy.OnEntryPoint(entryPointPointer, callerTransitionBlockParam); + } + + /// + /// Create a new value from a key. Must be threadsafe. Value may or may not be added + /// to collection. Return value must not be null. + /// + protected override unsafe MethodEntrypointPtr CreateValueFromKey(MethodEntrypointLookup key) + { + lock (this) + { + IntPtr thunk = IntPtr.Zero; + if (s_thunkPoolHeap == null) + { + s_thunkPoolHeap = RuntimeAugments.CreateThunksHeap(s_entryPointStub); + MethodEntrypointPtr.SetThunkPool(s_thunkPoolHeap); + Debug.Assert(s_thunkPoolHeap != null); + } + + thunk = RuntimeAugments.AllocateThunk(s_thunkPoolHeap); + Debug.Assert(thunk != IntPtr.Zero); + MethodEntrypointData *methodEntrypointData = (MethodEntrypointData*)MemoryHelpers.AllocateMemory(sizeof(MethodEntrypointData)); + + *methodEntrypointData = new MethodEntrypointData(key.MethodHandle, thunk); + + RuntimeAugments.SetThunkData(s_thunkPoolHeap, thunk, IntPtr.Zero, new IntPtr(methodEntrypointData)); + + SerializedDebugData.RegisterTailCallThunk(thunk); + + return new MethodEntrypointPtr(methodEntrypointData); + } + } + + /// + /// Convert a value to an IntPtr for storage into the hashtable + /// + protected override IntPtr ConvertValueToIntPtr(MethodEntrypointPtr value) + { + return value.ToIntPtr(); + } + + /// + /// Convert an IntPtr into a value for comparisions, or for returning. + /// + protected override MethodEntrypointPtr ConvertIntPtrToValue(IntPtr pointer) + { + return new MethodEntrypointPtr(pointer); + } + } + + private static MethodEntrypointHash s_methodEntrypointHash = new MethodEntrypointHash(); + + public static bool TryGetMethodEntrypoint(MethodDesc methodOnType, out IntPtr entryPoint, out IntPtr unboxingStubAddress, out TypeLoaderEnvironment.MethodAddressType foundAddressType) + { + MethodDesc typicalMethod = methodOnType.GetTypicalMethodDefinition(); + + if (!(typicalMethod is EcmaMethod)) + { + foundAddressType = TypeLoaderEnvironment.MethodAddressType.None; + entryPoint = IntPtr.Zero; + unboxingStubAddress = IntPtr.Zero; + return false; + } + + // OK, this is a method entrypoint for an ecma method + EcmaMethod ecmaTypicalMethod = (EcmaMethod)typicalMethod; + + // Canonicalize + MethodDesc canonMethod = methodOnType.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (canonMethod != methodOnType) + foundAddressType = TypeLoaderEnvironment.MethodAddressType.Canonical; + else + foundAddressType = TypeLoaderEnvironment.MethodAddressType.Exact; + + + // Check to see if we should produce an unboxing stub entrypoint + + unboxingStubAddress = IntPtr.Zero; // Optimistically choose not to + if (ecmaTypicalMethod.OwningType.IsValueType) + { + MethodSignature methodSig = ecmaTypicalMethod.Signature; + if (!methodSig.IsStatic) + unboxingStubAddress = new IntPtr(5); // TODO Actually implement the unboxing stub logic + } + + // Ensure RuntimeTypeHandles for owningType, and for instantiation types + // They should be there, as the paths to this function should ensure it, but its a bit sketchy + // as we don't have an opportunity to easily compute them now + if (!canonMethod.OwningType.RetrieveRuntimeTypeHandleIfPossible()) + Environment.FailFast("Did not pre-allocate owning type typehandle"); + + foreach (TypeDesc type in canonMethod.Instantiation) + { + if (!type.RetrieveRuntimeTypeHandleIfPossible()) + Environment.FailFast("Did not pre-allocate instantiation type typehandle"); + } + + // We need to create a RuntimeMethodHandle for this method + MethodEntrypointPtr entrypoint = s_methodEntrypointHash.GetOrCreateValue(new MethodEntrypointLookup(canonMethod)); + if (entrypoint.MethodCode != IntPtr.Zero) + entryPoint = entrypoint.MethodCode; + else + entryPoint = entrypoint.MethodEntrypointThunk; + + return true; + } + } +} \ No newline at end of file diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs index 3c6ecf107..b8682b894 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs @@ -363,7 +363,7 @@ namespace Internal.Runtime.TypeLoader DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)runtimeMethodHandleValue.ToPointer(); declaringTypeHandle = *(RuntimeTypeHandle*)&(methodData->DeclaringType); - genericMethodArgs = null; + genericMethodArgs = Array.Empty(); if (methodData->NumGenericArgs > 0) { diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs index bde93220d..67a0d3da6 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs @@ -1340,7 +1340,7 @@ namespace Internal.Runtime.TypeLoader #if SUPPORTS_R2R_LOADING TryGetCodeTableEntry(methodOnType, out entryPoint, out unboxingStubAddress, out foundAddressType); #endif -#if SUPPORT_JIT +#if SUPPORT_DYNAMIC_CODE if (foundAddressType == MethodAddressType.None) MethodEntrypointStubs.TryGetMethodEntrypoint(methodOnType, out entryPoint, out unboxingStubAddress, out foundAddressType); #endif diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MethodAddress.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MethodAddress.cs index 1f33974f8..dbc95140f 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MethodAddress.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MethodAddress.cs @@ -52,7 +52,7 @@ namespace Internal.Runtime.TypeLoader #if SUPPORTS_R2R_LOADING TryGetCodeTableEntry(method, out methodAddress, out unboxingStubAddress, out foundAddressType); #endif -#if SUPPORT_JIT +#if SUPPORT_DYNAMIC_CODE if (foundAddressType == MethodAddressType.None) MethodEntrypointStubs.TryGetMethodEntrypoint(method, out methodAddress, out unboxingStubAddress, out foundAddressType); #endif diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs index 3b9b0bcd1..ccbf4aff9 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs @@ -45,7 +45,7 @@ namespace Internal.Runtime.TypeLoader public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) { if ((type == UniversalCanonType) -#if SUPPORT_JIT +#if SUPPORT_DYNAMIC_CODE || (type.IsRuntimeDeterminedType && (((RuntimeDeterminedType)type).CanonicalType == UniversalCanonType))) #else ) @@ -202,7 +202,7 @@ namespace Internal.Runtime.TypeLoader #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING AssemblyBindResult bindResult; Exception failureException; - if (!AssemblyBinderImplementation.Instance.Bind(name.ToRuntimeAssemblyName(), out bindResult, out failureException)) + if (!AssemblyBinderImplementation.Instance.Bind(name.ToRuntimeAssemblyName(), cacheMissedLookups: true, out bindResult, out failureException)) { if (throwErrorIfNotFound) throw failureException; diff --git a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.Experimental.csproj b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.Experimental.csproj index 530409f48..1625467d0 100644 --- a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.Experimental.csproj +++ b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.Experimental.csproj @@ -2,7 +2,7 @@ System.Private.TypeLoader.Experimental true - true + true diff --git a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index 11afdf633..0a9048622 100644 --- a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -33,8 +33,8 @@ EETYPE_TYPE_MANAGER;$(DefineConstants) - - SUPPORT_JIT;$(DefineConstants) + + SUPPORT_DYNAMIC_CODE;$(DefineConstants) FEATURE_INTERPRETER;$(DefineConstants) @@ -56,7 +56,7 @@ ..\..\Common\src\Internal\NativeFormat - + @@ -93,7 +93,7 @@ - + -- cgit v1.2.3