From 1651e0f536d59c6e00e860e74c4bf191e64b0424 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 6 Jan 2025 07:57:24 -0800 Subject: [PATCH 1/3] Early work to make it possible to call the interpreter from ReadyToRun code --- src/coreclr/inc/readytorun.h | 3 + .../Common/Internal/Runtime/ModuleHeaders.cs | 1 + .../Internal/Runtime/ReadyToRunConstants.cs | 3 + .../ReadyToRun/DelayLoadMethodImport.cs | 10 +++ .../ReadyToRun/InterpreterImport.cs | 63 ++++++++++++++++ .../ReadyToRun/InterpreterMapNode.cs | 49 ++++++++++++ .../ReadyToRun/InterpreterStub.cs | 68 +++++++++++++++++ .../ReadyToRunCodegenNodeFactory.cs | 18 +++++ .../Compiler/ReadyToRunCodegenCompilation.cs | 3 +- .../ILCompiler.ReadyToRun.csproj | 3 + .../JitInterface/CorInfoImpl.ReadyToRun.cs | 9 +++ src/coreclr/vm/interpreter.cpp | 9 ++- src/coreclr/vm/interpreter.h | 2 + src/coreclr/vm/jitinterface.cpp | 4 + src/coreclr/vm/method.cpp | 14 +++- src/coreclr/vm/prestub.cpp | 11 +++ src/coreclr/vm/readytoruninfo.cpp | 11 +++ src/coreclr/vm/readytoruninfo.h | 20 +++++ .../src/System/AppContext.cs | 74 +++++++++++++++++++ 19 files changed, 370 insertions(+), 5 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterImport.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterMapNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index f5737ab0de97..9ff6d3bc7dbe 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -102,6 +102,7 @@ enum class ReadyToRunSectionType : uint32_t MethodIsGenericMap = 121, // Added in V9.0 EnclosingTypeMap = 122, // Added in V9.0 TypeGenericInfoMap = 123, // Added in V9.0 + InterpreterMap = 124, // Prototype // If you add a new section consider whether it is a breaking or non-breaking change. // Usually it is non-breaking, but if it is preferable to have older runtimes fail @@ -447,6 +448,8 @@ enum ReadyToRunHelper READYTORUN_HELPER_StackProbe = 0x111, READYTORUN_HELPER_GetCurrentManagedThreadId = 0x112, + + READYTORUN_HELPER_InterpreterRoutine = 0x113, }; #include "readytoruninstructionset.h" diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 51860d1341c7..d1cca6517add 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -79,6 +79,7 @@ enum ReadyToRunSectionType MethodIsGenericMap = 121, // Added in V9.0 EnclosingTypeMap = 122, // Added in V9.0 TypeGenericInfoMap = 123, // Added in V9.0 + InterpreterMap = 124, // Added for prototyping only // // NativeAOT ReadyToRun sections diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index e5c46bcd1b96..2d72c522d6eb 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -348,6 +348,9 @@ public enum ReadyToRunHelper GetCurrentManagedThreadId = 0x112, + //Interpreter + InterpreterRoutine = 0x113, + // ********************************************************************************************** // // These are not actually part of the R2R file format. We have them here because it's convenient. diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs index dd0d49c8db19..04b6a825d5ed 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs @@ -46,6 +46,16 @@ public override IEnumerable GetStaticDependencies(NodeFacto { yield return entry; } + + // It is possible that an instance of DelayLoadMethodImport is constructed by never get into the graph + // So we must delay the construction and rooting of the InterpreterImport + + InterpreterImport _interpreterImport = new InterpreterImport(); + InterpreterStub _interpreterStub = new InterpreterStub(_interpreterImport); + factory.AddInterpreterMapping(this, _interpreterImport, _interpreterStub); + yield return new DependencyListEntry(_interpreterImport, "Unused reason 1"); + yield return new DependencyListEntry(_interpreterStub, "Unused reason 2"); + yield return new DependencyListEntry(factory.InterpreterMap, "Unused reason 3"); if (_localMethod != null) yield return new DependencyListEntry(_localMethod, "Local method import"); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterImport.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterImport.cs new file mode 100644 index 000000000000..ee674a07e60c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterImport.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.JitInterface; +using Internal.Text; +using Internal.TypeSystem; +using Internal.ReadyToRunConstants; +using ILCompiler.DependencyAnalysisFramework; +using System.Collections.Generic; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + // An InterpreterImport simply a pointer sized variable that the + // interpreter stub can use to reference the InterpreterMethodInfo + public class InterpreterImport : ObjectNode, ISymbolDefinitionNode + { + public int _id; + public static int id = 1; + + public InterpreterImport() + { + // TODO, andrewau, we need a mechanism to identify the call sites + // using a ID is not going to be deterministic + _id = id++; + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return this._id - ((InterpreterImport)(other))._id; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + builder.EmitZeroPointer(); + return builder.ToObjectData(); + } + + public int Offset { get; set; } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + => sb.Append(nameMangler.CompilationUnitPrefix).Append("InterpreterImport").Append(_id.ToString()); + + public override ObjectNodeSection GetSection(NodeFactory factory) + { + return ObjectNodeSection.DataSection; + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) + { + yield break; + } + + protected override string GetName(NodeFactory context) => "InterpreterImport"; + + public override int ClassCode => 46709394; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterMapNode.cs new file mode 100644 index 000000000000..78f666fee8ad --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterMapNode.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using System.Collections.Generic; +using System.Diagnostics; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + public class InterpreterMapNode : HeaderTableNode + { + public override int ClassCode => 25687179; + + private List leftItems = new List(); + private List lastItems = new List(); + private List rightItems = new List(); + + public void AddMapping(DelayLoadMethodImport left, InterpreterImport right, InterpreterStub last) + { + // TODO, andrewau, this require proper locking and sorting for multithreaded compilation. + // correctness and determinism + leftItems.Add(left); + rightItems.Add(right); + lastItems.Add(last); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append("__InterpreterMap"u8); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + for (int i = 0; i < leftItems.Count; i++) + { + // This will emit the RVAs for these + builder.EmitReloc(leftItems[i], RelocType.IMAGE_REL_FILE_ABSOLUTE); + builder.EmitReloc(rightItems[i], RelocType.IMAGE_REL_FILE_ABSOLUTE); + builder.EmitReloc(lastItems[i], RelocType.IMAGE_REL_FILE_ABSOLUTE); + } + return builder.ToObjectData(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs new file mode 100644 index 000000000000..ff03a1ca2a1b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.JitInterface; +using Internal.Text; +using Internal.TypeSystem; +using Internal.ReadyToRunConstants; +using ILCompiler.DependencyAnalysisFramework; +using System.Collections.Generic; + +// TODO, andrewau, other architectures +using ILCompiler.DependencyAnalysis.X64; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + // An InterpreterStub is a small trampoline that + // 1.) Push the InterpreterMethodInfo to the stack, and + // 2.) Jump to the InterpreterMethod + public class InterpreterStub : ObjectNode, ISymbolDefinitionNode + { + public InterpreterImport _interpreterImport; + + public InterpreterStub(InterpreterImport interpreterImport) + { + _interpreterImport = interpreterImport; + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _interpreterImport._id - ((InterpreterStub)(other))._interpreterImport._id; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + X64Emitter x64Emitter = new X64Emitter(factory, relocsOnly); + x64Emitter.Builder.AddSymbol(this); + x64Emitter.EmitMOV(Register.RCX, _interpreterImport); + // TODO, andrewau, shouldn't have to do this if we can make node represent redirection cell + AddrMode addr = new AddrMode(Register.RCX, null, 0, 0, AddrModeSize.Int64); + x64Emitter.EmitMOV(Register.RCX, ref addr); + x64Emitter.EmitJMP(factory.InterpreterRoutineImport); + return x64Emitter.Builder.ToObjectData(); + } + + public int Offset { get; set; } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + => sb.Append(nameMangler.CompilationUnitPrefix).Append("InterpreterStub").Append(_interpreterImport._id.ToString()); + + public override ObjectNodeSection GetSection(NodeFactory factory) + { + return ObjectNodeSection.TextSection; + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) + { + yield break; + } + + protected override string GetName(NodeFactory context) => "InterpreterStub"; + + public override int ClassCode => 95566084; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 507c34b6f899..1988c16174c8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -97,6 +97,16 @@ public void GenerateHotColdMap(DependencyAnalyzerBase dependencyGra dependencyGraph.AddRoot(HotColdMap, "HotColdMap is generated because there is cold code"); } } + + public void AddInterpreterMapping(DelayLoadMethodImport left, InterpreterImport right, InterpreterStub last) + { + if (InterpreterMap == null) + { + InterpreterMap = new InterpreterMapNode(); + Header.Add(Internal.Runtime.ReadyToRunSectionType.InterpreterMap, InterpreterMap, InterpreterMap); + } + InterpreterMap.AddMapping(left, right, last); + } public void SetMarkingComplete() { @@ -372,6 +382,8 @@ private void CreateNodeCaches() public HotColdMapNode HotColdMap; + public InterpreterMapNode InterpreterMap; + public RuntimeFunctionsGCInfoNode RuntimeFunctionsGCInfo; public DelayLoadMethodCallThunkNodeRange DelayLoadMethodCallThunks; @@ -387,6 +399,8 @@ private void CreateNodeCaches() public Import ModuleImport; + public Import InterpreterRoutineImport; + public ISymbolNode PersonalityRoutine; public ISymbolNode FilterFuncletPersonalityRoutine; @@ -803,6 +817,10 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I ReadyToRunHelper.Module)); graph.AddRoot(ModuleImport, "Module import is required by the R2R format spec"); + InterpreterRoutineImport = new Import(EagerImports, new ReadyToRunHelperSignature( + ReadyToRunHelper.InterpreterRoutine)); + graph.AddRoot(InterpreterRoutineImport, "This allow ready to run code to bail to interpreter"); + if (Target.Architecture != TargetArchitecture.X86) { Import personalityRoutineImport = new Import(EagerImports, new ReadyToRunHelperSignature( diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 66c7767dab8a..5368d01846be 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -348,7 +348,8 @@ internal ReadyToRunCodegenCompilation( { _computedFixedLayoutTypesUncached = IsLayoutFixedInCurrentVersionBubbleInternal; _resilient = resilient; - _parallelism = parallelism; + // TODO, andrewau, until we fix the problem in InterpreterMapNode, this is required + _parallelism = 1; _corInfoImpls = new CorInfoImpl[_parallelism]; _generateMapFile = generateMapFile; _generateMapCsvFile = generateMapCsvFile; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index da8f861c4dba..1478dc4f5eb8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -193,6 +193,9 @@ + + + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index eebbbaceb7c2..eabc79a9baf2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -511,6 +511,15 @@ private static mdToken FindGenericMethodArgTypeSpec(EcmaModule module) public static bool ShouldSkipCompilation(InstructionSetSupport instructionSetSupport, MethodDesc methodNeedingCode) { + // TODO, andrewau, this will restrict the compilation to only a handful of methods for simplicity only + if ( + !string.Equals(methodNeedingCode.Name, "Setup") + && + !string.Equals(methodNeedingCode.Name, "Collatz") + ) + { + return true; + } if (methodNeedingCode.IsAggressiveOptimization) { return true; diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index 4c1bfc57dd1c..9016f1d3206f 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -12902,7 +12902,7 @@ void Interpreter::PrintILProfile(Interpreter::InstrExecRecord *recs, unsigned in } #endif // INTERP_ILINSTR_PROFILE -void InterpretCallTarget(PCODE pCallTarget, const ARG_SLOT* pArguments, ARG_SLOT* pReturnValue, int cbReturnValue) +InterpreterMethodInfo* GetInterpreterMethodInfo(PCODE pCallTarget) { _ASSERTE(InterpreterPrecode::IsInstance(pCallTarget)); @@ -12925,6 +12925,13 @@ void InterpretCallTarget(PCODE pCallTarget, const ARG_SLOT* pArguments, ARG_SLOT delete jitInfo; } + return methInfo; +} + +void InterpretCallTarget(PCODE pCallTarget, const ARG_SLOT* pArguments, ARG_SLOT* pReturnValue, int cbReturnValue) +{ + InterpreterMethodInfo* methInfo = GetInterpreterMethodInfo(pCallTarget); + ARG_SLOT retVal = Interpreter::InterpretMethodBody(methInfo, true, reinterpret_cast(const_cast(pArguments)), NULL); if (pReturnValue != NULL) *pReturnValue = retVal; diff --git a/src/coreclr/vm/interpreter.h b/src/coreclr/vm/interpreter.h index b45dd5e6dd1d..e82b1133686a 100644 --- a/src/coreclr/vm/interpreter.h +++ b/src/coreclr/vm/interpreter.h @@ -2098,4 +2098,6 @@ unsigned short Interpreter::NumberOfIntegerRegArgs() { return 8; } #error Unsupported architecture. #endif +InterpreterMethodInfo* GetInterpreterMethodInfo(PCODE pCallTarget); + #endif // INTERPRETER_H_DEFINED diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 336b217e627b..218aa86e5809 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -13788,6 +13788,10 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, case READYTORUN_HELPER_DelayLoad_MethodCall: result = (size_t)GetEEFuncEntryPoint(DelayLoad_MethodCall); break; + + case READYTORUN_HELPER_InterpreterRoutine: + result = (size_t)GetEEFuncEntryPoint(InterpretMethod); + break; case READYTORUN_HELPER_DelayLoad_Helper: result = (size_t)GetEEFuncEntryPoint(DelayLoad_Helper); diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 2e4ffd36c1eb..978a3d256ef0 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -3888,8 +3888,17 @@ PrecodeType MethodDesc::GetPrecodeType() PrecodeType precodeType = PRECODE_INVALID; #ifdef FEATURE_INTERPRETER - precodeType = PRECODE_INTERPRETER; -#else // !FEATURE_INTERPRETER + if ( + !m_pszDebugClassName + || + !m_pszDebugClassName + || + (strcmp("System.AppContext", m_pszDebugClassName) != 0) + || + (strcmp("Collatz", m_pszDebugMethodName) != 0) + ) + return PRECODE_INTERPRETER; +#endif // FEATURE_INTERPRETER #ifdef HAS_FIXUP_PRECODE if (!RequiresMethodDescCallingConvention()) { @@ -3901,7 +3910,6 @@ PrecodeType MethodDesc::GetPrecodeType() { precodeType = PRECODE_STUB; } -#endif // FEATURE_INTERPRETER return precodeType; } diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 6528c0103e29..3cab0fe8389d 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3359,6 +3359,17 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl pCode = pMD->GetMethodEntryPoint(); + if (InterpreterPrecode::IsInstance(pCode)) + { + InterpreterMethodInfo* pInterpreterMethodInfo = GetInterpreterMethodInfo(pCode); + RVA interpreterCellRva; + RVA interpreterStubRva; + pModule->GetReadyToRunInfo()->GetInterpreterStub(rva, &interpreterCellRva, &interpreterStubRva); + pCode = pNativeImage->GetRvaData(interpreterStubRva); + InterpreterMethodInfo** pCell = (InterpreterMethodInfo**)pNativeImage->GetRvaData(interpreterCellRva); + *pCell = pInterpreterMethodInfo; + } + #if _DEBUG if (pEMFrame->GetGCRefMap() != NULL) { diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 34cd10559aa1..67fea6712df4 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -831,6 +831,17 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat m_nHotColdMap = 0; } + IMAGE_DATA_DIRECTORY * pInterpreterMapDir = m_pComposite->FindSection(ReadyToRunSectionType::InterpreterMap); + if (pInterpreterMapDir != NULL) + { + m_pInterpreterMap = (PTR_ULONG)m_pComposite->GetLayout()->GetDirectoryData(pInterpreterMapDir); + m_nInterpreterMap = pInterpreterMapDir->Size / sizeof(ULONG); + } + else + { + m_nInterpreterMap = 0; + } + IMAGE_DATA_DIRECTORY * pImportSectionsDir = m_pComposite->FindSection(ReadyToRunSectionType::ImportSections); if (pImportSectionsDir != NULL) { diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index a58bbb41b900..784216339dd9 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -118,6 +118,26 @@ class ReadyToRunInfo PTR_ULONG m_pHotColdMap; DWORD m_nHotColdMap; + PTR_ULONG m_pInterpreterMap; + DWORD m_nInterpreterMap; + +public: + void GetInterpreterStub(RVA rva, RVA* pCell, RVA* pStub) + { + // TODO, move file + // TODO, assert + // TODO, andrewau, can we make this a more efficient sort? + for (uint i = 0; i < m_nInterpreterMap; i += 3) + { + if (rva == m_pInterpreterMap[i]) + { + *pCell = m_pInterpreterMap[i + 1]; + *pStub = m_pInterpreterMap[i + 2]; + } + } + } +private: + PTR_IMAGE_DATA_DIRECTORY m_pSectionDelayLoadMethodCallThunks; PTR_READYTORUN_IMPORT_SECTION m_pImportSections; diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs index d0ab3305bfd4..affa6e3b856e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs @@ -154,6 +154,8 @@ internal static unsafe void Setup(char** pNames, uint* pNameLengths, char** pVal #elif !NATIVEAOT internal static unsafe void Setup(char** pNames, char** pValues, int count) { + Base obj = Collatz(); + obj.Collatz1(); Debug.Assert(s_dataStore == null, "s_dataStore is not expected to be inited before Setup is called"); s_dataStore = new Dictionary(count); for (int i = 0; i < count; i++) @@ -161,6 +163,78 @@ internal static unsafe void Setup(char** pNames, char** pValues, int count) s_dataStore.Add(new string(pNames[i]), new string(pValues[i])); } } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + private static void ReadyToCall() + { + // Do nothing really + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + private static Base Collatz() + { +#pragma warning disable IDE0054 + ReadyToCall(); // Should fail now? + int a = 10086; + while (a > 1) + { + if (a % 2 == 0) + { + a = a / 2; + } + else + { + a = 3 * a + 1; + } + } + if (a == 0) + { + return new Base(); + } + else + { + return new Derived(); + } + } + + internal class Base + { + public virtual void Collatz1() + { + int a = 12580; + while (a > 1) + { + if (a % 2 == 0) + { + a = a / 2; + } + else + { + a = 3 * a + 1; + } + } + } + } + + internal sealed class Derived : Base + { + public override void Collatz1() + { + int a = 28963035; + while (a > 1) + { + if (a % 2 == 0) + { + a = a / 2; + } + else + { + a = 3 * a + 1; + } + } + } + } +#pragma warning restore IDE0054 #endif } } From ea18e643f4383bd91691b51228c53747a4e0ba76 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Wed, 8 Jan 2025 08:14:33 -0800 Subject: [PATCH 2/3] Experiment with signature --- .../Target_X64/X64Emitter.cs | 30 +++++++++- .../ReadyToRun/InterpreterStub.cs | 10 ++-- src/coreclr/vm/CMakeLists.txt | 2 + src/coreclr/vm/amd64/interpreter.asm | 60 +++++++++++++++++++ src/coreclr/vm/interpreter.h | 25 +++++++- src/coreclr/vm/jitinterface.cpp | 7 ++- .../src/System/AppContext.cs | 11 ++-- 7 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 src/coreclr/vm/amd64/interpreter.asm diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs index 321da23b8566..b4e7b46896bb 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs @@ -99,10 +99,25 @@ public void EmitJMP(ISymbolNode symbol) { Builder.EmitByte(0xE9); Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); - } } + public void EmitCALL(ISymbolNode symbol) + { + if (symbol.RepresentsIndirectionCell) + { + // CALL instruction with indirection + Builder.EmitByte(0xFF); + Builder.EmitByte(0x15); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + } + else + { + // Regular CALL instruction + Builder.EmitByte(0xE8); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + } + } public void EmitJE(ISymbolNode symbol) { if (symbol.RepresentsIndirectionCell) @@ -141,6 +156,14 @@ public void EmitJmpToAddrMode(ref AddrMode addrMode) EmitIndirInstruction(0xFF, 0x4, ref addrMode); } + public void EmitPUSH(Register reg) + { + if (reg >= Register.RAX && reg <= Register.R15) + { + Builder.EmitByte((byte)((byte)0x50 + (byte)((byte)reg & (byte)0x0F))); + } + } + public void EmitPUSH(sbyte imm8) { Builder.EmitByte(0x6A); @@ -173,6 +196,11 @@ public void EmitPUSH(ISymbolNode node) } } + public void EmitPOP() + { + Builder.EmitByte(0x58); + } + public void EmitRET() { Builder.EmitByte(0xC3); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs index ff03a1ca2a1b..7da2ed7c2a4b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs @@ -14,8 +14,9 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { // An InterpreterStub is a small trampoline that - // 1.) Push the InterpreterMethodInfo to the stack, and - // 2.) Jump to the InterpreterMethod + // 1.) Copy the pointer to the pointer to the InterpreterMethodInfo into RAX + // 2.) Jump to the InterpreterRoutine + // public class InterpreterStub : ObjectNode, ISymbolDefinitionNode { public InterpreterImport _interpreterImport; @@ -34,10 +35,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { X64Emitter x64Emitter = new X64Emitter(factory, relocsOnly); x64Emitter.Builder.AddSymbol(this); - x64Emitter.EmitMOV(Register.RCX, _interpreterImport); - // TODO, andrewau, shouldn't have to do this if we can make node represent redirection cell - AddrMode addr = new AddrMode(Register.RCX, null, 0, 0, AddrModeSize.Int64); - x64Emitter.EmitMOV(Register.RCX, ref addr); + x64Emitter.EmitMOV(Register.RAX, _interpreterImport); x64Emitter.EmitJMP(factory.InterpreterRoutineImport); return x64Emitter.Builder.ToObjectData(); } diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 334faa6f1676..f241f0235d89 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -618,6 +618,7 @@ if(CLR_CMAKE_TARGET_ARCH_AMD64) ${ARCH_SOURCES_DIR}/GenericComCallStubs.asm ${ARCH_SOURCES_DIR}/GenericCLRToCOMCallStubs.asm ${ARCH_SOURCES_DIR}/getstate.asm + ${ARCH_SOURCES_DIR}/interpreter.asm ${ARCH_SOURCES_DIR}/JitHelpers_Fast.asm ${ARCH_SOURCES_DIR}/JitHelpers_FastWriteBarriers.asm ${ARCH_SOURCES_DIR}/JitHelpers_SingleAppDomain.asm @@ -653,6 +654,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64) set(VM_SOURCES_WKS_ARCH_ASM ${ARCH_SOURCES_DIR}/AsmHelpers.asm ${ARCH_SOURCES_DIR}/CallDescrWorkerARM64.asm + ${ARCH_SOURCES_DIR}/interpreter.asm ${ARCH_SOURCES_DIR}/patchedcode.asm ${ARCH_SOURCES_DIR}/PInvokeStubs.asm ${ARCH_SOURCES_DIR}/thunktemplates.asm diff --git a/src/coreclr/vm/amd64/interpreter.asm b/src/coreclr/vm/amd64/interpreter.asm new file mode 100644 index 000000000000..8e8b6ef21389 --- /dev/null +++ b/src/coreclr/vm/amd64/interpreter.asm @@ -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. + +; *********************************************************************** +; File: JitHelpers_Fast.asm +; +; Notes: routinues which we believe to be on the hot path for managed +; code in most scenarios. +; *********************************************************************** + + +include AsmMacros.inc +include asmconstants.inc + +extern ?InterpretMethod@@YA_JPEAUInterpreterMethodInfo@@PEAEPEAX@Z:proc + +; +; This routine rearrange the stack so that it will be the way coreclr!InterpretMethod wants. +; +; This routine should only be called by the InterpreterStub from ReadyToRun code, where it puts a pointer to a pointer to the InterpreterMethodInfo in rax and leave the argument untouched +; +; In the sequel, stack grows to the left (i.e. left hand side is lower virtual address), rsp always point to the leftmost (i.e. topmost) of the stack +; +; The state of right now +; +; rax -> &&InterpreterMethodInfo +; +; [ret] [s0] [s1] [s2] [s3] [spill args] +; + +; +; Desired state before call +; +; rcx -> &InterpreterMethodInfo +; rdx -> ilArgs = rsp + 20h +; TODO: r8 should be stubContext, necessary for IL Stub +; +; +0h +8h +10h +18h +20h +28h +30h +38h +; [s0] [s1] [s2] [s3] [rcx] [rdx] [r8] [r9] [na] [ret] [s0] [s1] [s2] [s3] [spill args] +; <- shadow slots -> <- shadow slots -> +; +; The na slot is meant to make sure the stack is aligned +; + +NESTED_ENTRY InterpreterRoutine, _TEXT + alloc_stack 48h + +END_PROLOGUE + mov [rsp+38h], r9 + mov [rsp+30h], r8 + mov [rsp+28h], rdx + mov [rsp+20h], rcx + mov rcx, [rax] + lea rdx, [rsp + 20h] + call ?InterpretMethod@@YA_JPEAUInterpreterMethodInfo@@PEAEPEAX@Z + add rsp, 48h + ret +NESTED_END InterpreterRoutine, _TEXT + + end \ No newline at end of file diff --git a/src/coreclr/vm/interpreter.h b/src/coreclr/vm/interpreter.h index e82b1133686a..d2e958425d7a 100644 --- a/src/coreclr/vm/interpreter.h +++ b/src/coreclr/vm/interpreter.h @@ -1125,7 +1125,30 @@ class Interpreter return *reinterpret_cast(&m_ilArgs[m_methInfo->m_argDescs[argNum].m_nativeOffset]); } #endif - return &m_ilArgs[m_methInfo->m_argDescs[argNum].m_nativeOffset]; + BYTE* argAddr = &m_ilArgs[m_methInfo->m_argDescs[argNum].m_nativeOffset]; + if (argAddr >= m_ilArgs + 0x20) + { + // + // This is to adjust for the fact that the assembly stub does not know + // the signature of the method, and therefore it has no idea how many spill arg to copy + // so we are referencing the spill args where they were + // + // The stack look like this: + // + // top (low address) <- bottom (high address) + // [s0] [s1] [s2] [s3] [rcx] [rdx] [r8] [r9] [na] [ret] [s0] [s1] [s2] [s3] [spill args] + // ^ + // m_ilArgs + // ^ + // m_ilArgs + 0x20 + // ^ + // m_ilArgs + 0x30 + // + // The end effect of the shift will make accessing [na] become accessing spill args, which is what we wanted + // + argAddr += 0x30; + } + return argAddr; } else { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 218aa86e5809..53cd5c911ed4 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -55,6 +55,11 @@ #include "interpreter.h" #endif // FEATURE_INTERPRETER +extern "C" +{ + void InterpreterRoutine(); +} + #ifdef FEATURE_PERFMAP #include "perfmap.h" #endif @@ -13790,7 +13795,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, break; case READYTORUN_HELPER_InterpreterRoutine: - result = (size_t)GetEEFuncEntryPoint(InterpretMethod); + result = (size_t)GetEEFuncEntryPoint(InterpreterRoutine); break; case READYTORUN_HELPER_DelayLoad_Helper: diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs index affa6e3b856e..e4eecf3eae94 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs @@ -165,17 +165,20 @@ internal static unsafe void Setup(char** pNames, char** pValues, int count) } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - private static void ReadyToCall() + private static int ReadyToCall(int a, int b, int c, int d, int e, int f) { - // Do nothing really + return a * 1048576 + b * 65536 + c * 4096 + d * 256 + e * 16 + f; } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static Base Collatz() { #pragma warning disable IDE0054 - ReadyToCall(); // Should fail now? - int a = 10086; + int a = ReadyToCall(1, 2, 3, 4, 5, 6); + if (a > 0) + { + throw new Exception(a.ToString()); + } while (a > 1) { if (a % 2 == 0) From 310951f394a7b5515b53d4339ccbc52d601edbd8 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 13 Jan 2025 13:25:07 -0800 Subject: [PATCH 3/3] Experiment with virtual --- src/coreclr/inc/readytorun.h | 1 + .../Internal/Runtime/ReadyToRunConstants.cs | 1 + .../ReadyToRun/DelayLoadHelperMethodImport.cs | 9 +++++ .../ReadyToRun/DelayLoadMethodImport.cs | 3 +- .../ReadyToRun/InterpreterMapNode.cs | 4 +-- .../ReadyToRun/InterpreterStub.cs | 13 +++++-- .../ReadyToRunCodegenNodeFactory.cs | 8 ++++- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 ++ src/coreclr/vm/amd64/interpreter.asm | 35 +++++++++++++++++++ src/coreclr/vm/interpreter.cpp | 7 ++++ src/coreclr/vm/jitinterface.cpp | 5 +++ src/coreclr/vm/method.cpp | 2 +- src/coreclr/vm/prestub.cpp | 8 +++-- .../src/System/AppContext.cs | 14 ++++---- 14 files changed, 97 insertions(+), 15 deletions(-) diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 9ff6d3bc7dbe..5647880294db 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -450,6 +450,7 @@ enum ReadyToRunHelper READYTORUN_HELPER_GetCurrentManagedThreadId = 0x112, READYTORUN_HELPER_InterpreterRoutine = 0x113, + READYTORUN_HELPER_InterpreterVirtualRoutine = 0x114, }; #include "readytoruninstructionset.h" diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index 2d72c522d6eb..2974c2c4fab7 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -350,6 +350,7 @@ public enum ReadyToRunHelper //Interpreter InterpreterRoutine = 0x113, + InterpreterVirtualRoutine = 0x114, // ********************************************************************************************** // diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadHelperMethodImport.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadHelperMethodImport.cs index eb996de2ac2b..184b106d4d76 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadHelperMethodImport.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadHelperMethodImport.cs @@ -42,6 +42,15 @@ public override IEnumerable GetStaticDependencies(NodeFacto { yield return baseEntry; } + + // This is for virtual calls + InterpreterImport _interpreterImport = new InterpreterImport(); + InterpreterStub _interpreterStub = new InterpreterStub(_interpreterImport, /* virtual = */true); + factory.AddInterpreterMapping(this, _interpreterImport, _interpreterStub); + yield return new DependencyListEntry(_interpreterImport, "Unused reason 1"); + yield return new DependencyListEntry(_interpreterStub, "Unused reason 2"); + yield return new DependencyListEntry(factory.InterpreterMap, "Unused reason 3"); + if (_useInstantiatingStub) { // Require compilation of the canonical version for instantiating stubs diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs index 04b6a825d5ed..ecb5fc03c0ca 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs @@ -50,8 +50,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto // It is possible that an instance of DelayLoadMethodImport is constructed by never get into the graph // So we must delay the construction and rooting of the InterpreterImport + // This is for static calls InterpreterImport _interpreterImport = new InterpreterImport(); - InterpreterStub _interpreterStub = new InterpreterStub(_interpreterImport); + InterpreterStub _interpreterStub = new InterpreterStub(_interpreterImport, /* virtual = */false); factory.AddInterpreterMapping(this, _interpreterImport, _interpreterStub); yield return new DependencyListEntry(_interpreterImport, "Unused reason 1"); yield return new DependencyListEntry(_interpreterStub, "Unused reason 2"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterMapNode.cs index 78f666fee8ad..b610a52b40f2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterMapNode.cs @@ -13,11 +13,11 @@ public class InterpreterMapNode : HeaderTableNode { public override int ClassCode => 25687179; - private List leftItems = new List(); + private List leftItems = new List(); private List lastItems = new List(); private List rightItems = new List(); - public void AddMapping(DelayLoadMethodImport left, InterpreterImport right, InterpreterStub last) + public void AddMapping(ISymbolNode left, InterpreterImport right, InterpreterStub last) { // TODO, andrewau, this require proper locking and sorting for multithreaded compilation. // correctness and determinism diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs index 7da2ed7c2a4b..2b7819c353bd 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InterpreterStub.cs @@ -20,10 +20,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun public class InterpreterStub : ObjectNode, ISymbolDefinitionNode { public InterpreterImport _interpreterImport; + public bool _virtual; - public InterpreterStub(InterpreterImport interpreterImport) + public InterpreterStub(InterpreterImport interpreterImport, bool isVirtual) { _interpreterImport = interpreterImport; + _virtual = isVirtual; } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) @@ -36,7 +38,14 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) X64Emitter x64Emitter = new X64Emitter(factory, relocsOnly); x64Emitter.Builder.AddSymbol(this); x64Emitter.EmitMOV(Register.RAX, _interpreterImport); - x64Emitter.EmitJMP(factory.InterpreterRoutineImport); + if (_virtual) + { + x64Emitter.EmitJMP(factory.InterpreterVirtualRoutineImport); + } + else + { + x64Emitter.EmitJMP(factory.InterpreterRoutineImport); + } return x64Emitter.Builder.ToObjectData(); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 1988c16174c8..79503724b6f8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -98,7 +98,7 @@ public void GenerateHotColdMap(DependencyAnalyzerBase dependencyGra } } - public void AddInterpreterMapping(DelayLoadMethodImport left, InterpreterImport right, InterpreterStub last) + public void AddInterpreterMapping(ISymbolNode left, InterpreterImport right, InterpreterStub last) { if (InterpreterMap == null) { @@ -401,6 +401,8 @@ private void CreateNodeCaches() public Import InterpreterRoutineImport; + public Import InterpreterVirtualRoutineImport; + public ISymbolNode PersonalityRoutine; public ISymbolNode FilterFuncletPersonalityRoutine; @@ -821,6 +823,10 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I ReadyToRunHelper.InterpreterRoutine)); graph.AddRoot(InterpreterRoutineImport, "This allow ready to run code to bail to interpreter"); + InterpreterVirtualRoutineImport = new Import(EagerImports, new ReadyToRunHelperSignature( + ReadyToRunHelper.InterpreterVirtualRoutine)); + graph.AddRoot(InterpreterVirtualRoutineImport, "This allow ready to run code to bail to interpreter"); + if (Target.Architecture != TargetArchitecture.X86) { Import personalityRoutineImport = new Import(EagerImports, new ReadyToRunHelperSignature( diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index eabc79a9baf2..9a1801743b25 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -516,6 +516,8 @@ public static bool ShouldSkipCompilation(InstructionSetSupport instructionSetSup !string.Equals(methodNeedingCode.Name, "Setup") && !string.Equals(methodNeedingCode.Name, "Collatz") + && + !string.Equals(methodNeedingCode.Name, "ReadyToRun") ) { return true; diff --git a/src/coreclr/vm/amd64/interpreter.asm b/src/coreclr/vm/amd64/interpreter.asm index 8e8b6ef21389..bec2cf2fe2a3 100644 --- a/src/coreclr/vm/amd64/interpreter.asm +++ b/src/coreclr/vm/amd64/interpreter.asm @@ -57,4 +57,39 @@ END_PROLOGUE ret NESTED_END InterpreterRoutine, _TEXT +NESTED_ENTRY InterpreterVirtualRoutine, _TEXT + alloc_stack 48h + +END_PROLOGUE + ; At this point, I have rax pointing to the slot number, and rcx pointing to this + ; I am making two assumptions + ; 1. virtual slot < 8 (i.e. we are always calling in the 1st chunk), and + ; 2. we are always going into the InterpreterMethodInfo + + ; rcx was pointing to the object + mov r8, [rcx] + ; r8 is pointing to the method table + add r8, 48h + ; r8 is pointing to the pointer to the vtable + mov r8, [r8] + ; r8 is pointing to the vtable + + ; rax was pointing to the slot number + mov rax, [rax] + ; rax is the slot number + shl rax, 3 + ; rax is the offset from the v-table + add r8, rax + ; r8 is pointing to the interpreter-precode + mov [rsp+38h], r9 + mov [rsp+30h], r8 + mov [rsp+28h], rdx + mov [rsp+20h], rcx + mov rcx, [r8] ; rcx is the interpreter-precode + lea rdx, [rsp + 20h] + call ?InterpretMethod@@YA_JPEAUInterpreterMethodInfo@@PEAEPEAX@Z + add rsp, 48h + ret +NESTED_END InterpreterVirtualRoutine, _TEXT + end \ No newline at end of file diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index 9016f1d3206f..55056043691e 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -1932,6 +1932,13 @@ HCIMPL3(INT64, InterpretMethod, struct InterpreterMethodInfo* interpMethInfo, BY ARG_SLOT retVal = 0; HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); + + PCODE addr = (PCODE)interpMethInfo; + if (InterpreterPrecode::IsInstance(addr)) + { + interpMethInfo = GetInterpreterMethodInfo(addr); + } + retVal = Interpreter::InterpretMethodBody(interpMethInfo, false, ilArgs, stubContext); HELPER_METHOD_FRAME_END(); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 53cd5c911ed4..8262d33bb3db 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -58,6 +58,7 @@ extern "C" { void InterpreterRoutine(); + void InterpreterVirtualRoutine(); } #ifdef FEATURE_PERFMAP @@ -13798,6 +13799,10 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, result = (size_t)GetEEFuncEntryPoint(InterpreterRoutine); break; + case READYTORUN_HELPER_InterpreterVirtualRoutine: + result = (size_t)GetEEFuncEntryPoint(InterpreterVirtualRoutine); + break; + case READYTORUN_HELPER_DelayLoad_Helper: result = (size_t)GetEEFuncEntryPoint(DelayLoad_Helper); break; diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 978a3d256ef0..0f5d74c4eebf 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -3895,7 +3895,7 @@ PrecodeType MethodDesc::GetPrecodeType() || (strcmp("System.AppContext", m_pszDebugClassName) != 0) || - (strcmp("Collatz", m_pszDebugMethodName) != 0) + (strcmp("ReadyToRun", m_pszDebugMethodName) != 0) ) return PRECODE_INTERPRETER; #endif // FEATURE_INTERPRETER diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 3cab0fe8389d..10b7ee4b1f71 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3342,8 +3342,12 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl } else { - pCode = pMgr->GetVTableCallStub(slot); - *(TADDR *)pIndirection = pCode; + RVA interpreterCellRva; + RVA interpreterStubRva; + pModule->GetReadyToRunInfo()->GetInterpreterStub(rva, &interpreterCellRva, &interpreterStubRva); + pCode = pNativeImage->GetRvaData(interpreterStubRva); + DWORD* pCell = (DWORD*)pNativeImage->GetRvaData(interpreterCellRva); + *pCell = slot; } _ASSERTE(pCode != (PCODE)NULL); } diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs index e4eecf3eae94..632f113b1a46 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs @@ -154,8 +154,7 @@ internal static unsafe void Setup(char** pNames, uint* pNameLengths, char** pVal #elif !NATIVEAOT internal static unsafe void Setup(char** pNames, char** pValues, int count) { - Base obj = Collatz(); - obj.Collatz1(); + ReadyToRun(); Debug.Assert(s_dataStore == null, "s_dataStore is not expected to be inited before Setup is called"); s_dataStore = new Dictionary(count); for (int i = 0; i < count; i++) @@ -164,6 +163,13 @@ internal static unsafe void Setup(char** pNames, char** pValues, int count) } } + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + private static void ReadyToRun() + { + Base obj = Collatz(); + obj.Collatz1(); + } + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static int ReadyToCall(int a, int b, int c, int d, int e, int f) { @@ -175,10 +181,6 @@ private static Base Collatz() { #pragma warning disable IDE0054 int a = ReadyToCall(1, 2, 3, 4, 5, 6); - if (a > 0) - { - throw new Exception(a.ToString()); - } while (a > 1) { if (a % 2 == 0)