Skip to content

Commit 106a1e1

Browse files
Optimize PI and RPI transitions
This changes the managed ABI in the following ways: 1) The PI transition frames becomes simply the shadow stack top. This "frame" is zero-sized - we don't store the current thread in it, since the WASM TLS model allows us to elide it. The obvious benefit from this is that the PI path is now almost 100% optimal: two stores and one load. We can get rid of the load in an ST build as well, but that's left for a future change. 2) The RPI transition frame is now always allocated at a zero offset and returned directly from the RPI helper. We thus elide the intermediate state where we already have the shadow stack, but haven't yet attached the thread. This brings us in line with other targets. 3) The sparse virtual unwind frame is now allocated right after the RPI frame, and "combined" RPI helpers introduced that both effect the transition and push the EH frame. The RPI changes reduce the number of helper calls that any RPI method needs to make from 3 to 1 (for epilogs - 2 to 1) in the sparse virtual unwinding model, and reduce the number of intructions on the critical path. Benchmarks: Node base: Bench_PInvoke took : 86 ms (8.64 ns / op) Bench_ReversePInvoke_Empty took : 113 ms (11.30 ns / op) Bench_ReversePInvoke_WithEH took : 172 ms (17.24 ns / op) Node diff: Bench_PInvoke took : 81 ms (8.06 ns / op) Bench_ReversePInvoke_Empty took : 58 ms (5.81 ns / op) Bench_ReversePInvoke_WithEH took : 108 ms (10.79 ns / op) Wasmtime base: Bench_PInvoke took : 99 ms (9.86 ns / op) Bench_ReversePInvoke_Empty took : 73 ms (7.28 ns / op) Bench_ReversePInvoke_WithEH took : 77 ms (7.71 ns / op) Wasmtime diff: Bench_PInvoke took : 82 ms (8.16 ns / op) Bench_ReversePInvoke_Empty took : 31 ms (3.06 ns / op) Bench_ReversePInvoke_WithEH took : 50 ms (4.98 ns / op)
1 parent a27a1be commit 106a1e1

File tree

22 files changed

+316
-166
lines changed

22 files changed

+316
-166
lines changed

src/coreclr/inc/corinfo.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,14 +595,14 @@ enum CorInfoHelpFunc
595595
CORINFO_HELP_VALIDATE_INDIRECT_CALL, // CFG: Validate function pointer
596596
CORINFO_HELP_DISPATCH_INDIRECT_CALL, // CFG: Validate and dispatch to pointer
597597

598-
CORINFO_HELP_LLVM_GET_OR_INIT_SHADOW_STACK_TOP,
599598
CORINFO_HELP_LLVM_EH_CATCH,
600599
CORINFO_HELP_LLVM_EH_POP_UNWOUND_VIRTUAL_FRAMES,
601600
CORINFO_HELP_LLVM_EH_PUSH_VIRTUAL_UNWIND_FRAME,
601+
CORINFO_HELP_LLVM_EH_REVERSE_PINVOKE_ENTER_AND_PUSH_VIRTUAL_UNWIND_FRAME,
602602
CORINFO_HELP_LLVM_EH_POP_VIRTUAL_UNWIND_FRAME,
603+
CORINFO_HELP_LLVM_EH_REVERSE_PINVOKE_EXIT_AND_POP_VIRTUAL_UNWIND_FRAME,
603604
CORINFO_HELP_LLVM_EH_UNHANDLED_EXCEPTION,
604605
CORINFO_HELP_LLVM_RESOLVE_INTERFACE_CALL_TARGET,
605-
CORINFO_HELP_LLVM_GET_EXTERNAL_CALL_TARGET,
606606
CORINFO_HELP_LLVM_STRESS_GC,
607607

608608
CORINFO_HELP_COUNT,

src/coreclr/inc/jithelpers.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,14 +343,14 @@
343343
JITHELPER(CORINFO_HELP_DISPATCH_INDIRECT_CALL, NULL, METHOD__NIL)
344344
#endif
345345

346-
JITHELPER(CORINFO_HELP_LLVM_GET_OR_INIT_SHADOW_STACK_TOP, NULL, METHOD__NIL)
347346
JITHELPER(CORINFO_HELP_LLVM_EH_CATCH, NULL, METHOD__NIL)
348347
JITHELPER(CORINFO_HELP_LLVM_EH_POP_UNWOUND_VIRTUAL_FRAMES, NULL, METHOD__NIL)
349348
JITHELPER(CORINFO_HELP_LLVM_EH_PUSH_VIRTUAL_UNWIND_FRAME, NULL, METHOD__NIL)
349+
JITHELPER(CORINFO_HELP_LLVM_EH_REVERSE_PINVOKE_ENTER_AND_PUSH_VIRTUAL_UNWIND_FRAME, NULL, METHOD__NIL)
350350
JITHELPER(CORINFO_HELP_LLVM_EH_POP_VIRTUAL_UNWIND_FRAME, NULL, METHOD__NIL)
351+
JITHELPER(CORINFO_HELP_LLVM_EH_REVERSE_PINVOKE_EXIT_AND_POP_VIRTUAL_UNWIND_FRAME, NULL, METHOD__NIL)
351352
JITHELPER(CORINFO_HELP_LLVM_EH_UNHANDLED_EXCEPTION, NULL, METHOD__NIL)
352353
JITHELPER(CORINFO_HELP_LLVM_RESOLVE_INTERFACE_CALL_TARGET, NULL, METHOD__NIL)
353-
JITHELPER(CORINFO_HELP_LLVM_GET_EXTERNAL_CALL_TARGET, NULL, METHOD__NIL)
354354
JITHELPER(CORINFO_HELP_LLVM_STRESS_GC, JIT_StressGC, METHOD__NIL)
355355
#undef JITHELPER
356356
#undef DYNAMICJITHELPER

src/coreclr/jit/flowgraph.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,6 +1740,7 @@ void Compiler::fgAddReversePInvokeEnterExit()
17401740
LclVarDsc* varDsc = lvaGetDesc(lvaReversePInvokeFrameVar);
17411741
lvaSetStruct(lvaReversePInvokeFrameVar, typGetBlkLayout(eeGetEEInfo()->sizeOfReversePInvokeFrame), false);
17421742

1743+
#ifndef TARGET_WASM // WASM RPI helpers have special ABI and are inserted in lowering.
17431744
// Add enter pinvoke exit callout at the start of prolog
17441745

17451746
GenTree* pInvokeFrameVar = gtNewLclVarAddrNode(lvaReversePInvokeFrameVar);
@@ -1804,6 +1805,7 @@ void Compiler::fgAddReversePInvokeEnterExit()
18041805
printf("\n");
18051806
}
18061807
#endif
1808+
#endif // !TARGET_WASM
18071809
}
18081810

18091811
/*****************************************************************************

src/coreclr/jit/llvm.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -517,11 +517,11 @@ bool Llvm::helperCallMayVirtuallyUnwind(CorInfoHelpFunc helperFunc) const
517517
{ FUNC(CORINFO_HELP_THROW_ENTRYPOINT_NOT_FOUND_EXCEPTION) },
518518

519519
// [R]PI helpers, implemented in "Runtime\thread.cpp".
520-
{ FUNC(CORINFO_HELP_JIT_PINVOKE_BEGIN) CORINFO_TYPE_VOID, { CORINFO_TYPE_PTR }, HFIF_SS_ARG | HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
521-
{ FUNC(CORINFO_HELP_JIT_PINVOKE_END) CORINFO_TYPE_VOID, { CORINFO_TYPE_PTR }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
522-
{ FUNC(CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER) CORINFO_TYPE_VOID, { CORINFO_TYPE_PTR }, HFIF_SS_ARG },
520+
{ FUNC(CORINFO_HELP_JIT_PINVOKE_BEGIN) CORINFO_TYPE_VOID, { }, HFIF_SS_ARG | HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
521+
{ FUNC(CORINFO_HELP_JIT_PINVOKE_END) CORINFO_TYPE_VOID, { }, HFIF_SS_ARG | HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
522+
{ FUNC(CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER) CORINFO_TYPE_PTR, { }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
523523
{ FUNC(CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER_TRACK_TRANSITIONS) },
524-
{ FUNC(CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT) CORINFO_TYPE_VOID, { CORINFO_TYPE_PTR, CORINFO_TYPE_PTR }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
524+
{ FUNC(CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT) CORINFO_TYPE_VOID, { CORINFO_TYPE_PTR }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
525525
{ FUNC(CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT_TRACK_TRANSITIONS) },
526526

527527
// Implemented in "CoreLib\src\System\Runtime\TypeLoaderExports.cs".
@@ -544,14 +544,15 @@ bool Llvm::helperCallMayVirtuallyUnwind(CorInfoHelpFunc helperFunc) const
544544
{ FUNC(CORINFO_HELP_VALIDATE_INDIRECT_CALL) },
545545
{ FUNC(CORINFO_HELP_DISPATCH_INDIRECT_CALL) },
546546

547-
{ FUNC(CORINFO_HELP_LLVM_GET_OR_INIT_SHADOW_STACK_TOP) CORINFO_TYPE_PTR, { }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
547+
// TODO-LLVM-QPI: incorrect SS top args for RPI return helper.
548548
{ FUNC(CORINFO_HELP_LLVM_EH_CATCH) CORINFO_TYPE_CLASS, { CORINFO_TYPE_NATIVEUINT }, HFIF_SS_ARG },
549549
{ FUNC(CORINFO_HELP_LLVM_EH_POP_UNWOUND_VIRTUAL_FRAMES) CORINFO_TYPE_VOID, { }, HFIF_SS_ARG },
550-
{ FUNC(CORINFO_HELP_LLVM_EH_PUSH_VIRTUAL_UNWIND_FRAME) CORINFO_TYPE_VOID, { CORINFO_TYPE_PTR, CORINFO_TYPE_PTR, CORINFO_TYPE_NATIVEUINT }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND},
551-
{ FUNC(CORINFO_HELP_LLVM_EH_POP_VIRTUAL_UNWIND_FRAME) CORINFO_TYPE_VOID, { }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND},
550+
{ FUNC(CORINFO_HELP_LLVM_EH_PUSH_VIRTUAL_UNWIND_FRAME) CORINFO_TYPE_VOID, { CORINFO_TYPE_PTR, CORINFO_TYPE_PTR, CORINFO_TYPE_NATIVEUINT }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
551+
{ FUNC(CORINFO_HELP_LLVM_EH_REVERSE_PINVOKE_ENTER_AND_PUSH_VIRTUAL_UNWIND_FRAME) CORINFO_TYPE_PTR, { CORINFO_TYPE_PTR, CORINFO_TYPE_NATIVEUINT }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
552+
{ FUNC(CORINFO_HELP_LLVM_EH_POP_VIRTUAL_UNWIND_FRAME) CORINFO_TYPE_VOID, { }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
553+
{ FUNC(CORINFO_HELP_LLVM_EH_REVERSE_PINVOKE_EXIT_AND_POP_VIRTUAL_UNWIND_FRAME) CORINFO_TYPE_VOID, { CORINFO_TYPE_PTR }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
552554
{ FUNC(CORINFO_HELP_LLVM_EH_UNHANDLED_EXCEPTION) CORINFO_TYPE_VOID, { CORINFO_TYPE_CLASS }, HFIF_SS_ARG },
553555
{ FUNC(CORINFO_HELP_LLVM_RESOLVE_INTERFACE_CALL_TARGET) CORINFO_TYPE_PTR, { CORINFO_TYPE_CLASS, CORINFO_TYPE_PTR }, HFIF_SS_ARG },
554-
{ FUNC(CORINFO_HELP_LLVM_GET_EXTERNAL_CALL_TARGET) CORINFO_TYPE_PTR, { }, HFIF_NO_RPI_OR_GC | HFIF_NO_VIRTUAL_UNWIND },
555556
{ FUNC(CORINFO_HELP_LLVM_STRESS_GC) CORINFO_TYPE_BYREF, { CORINFO_TYPE_BYREF, CORINFO_TYPE_PTR }, HFIF_SS_ARG },
556557
};
557558
// clang-format on

src/coreclr/jit/llvm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ class Llvm
308308

309309
// Shared between virtual unwind frame insertion and LSSA.
310310
unsigned m_initialUnwindIndex = UNWIND_INDEX_NONE;
311+
CORINFO_GENERIC_HANDLE m_ehInfoSymbol = nullptr;
311312

312313
// Shared between unwind index insertion and EH codegen.
313314
ArrayStack<unsigned>* m_unwindIndexMap = nullptr;

src/coreclr/jit/llvmlower.cpp

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ void Llvm::lowerBlock(BasicBlock* block)
243243

244244
void Llvm::lowerRange(BasicBlock* block, LIR::Range& range)
245245
{
246+
BasicBlock* savedBlock = m_currentBlock;
247+
LIR::Range* savedRange = m_currentRange;
246248
m_currentBlock = block;
247249
m_currentRange = &range;
248250

@@ -253,8 +255,8 @@ void Llvm::lowerRange(BasicBlock* block, LIR::Range& range)
253255

254256
INDEBUG(range.CheckLIR(_compiler, /* checkUnusedValues */ true));
255257

256-
m_currentBlock = nullptr;
257-
m_currentRange = nullptr;
258+
m_currentBlock = savedBlock;
259+
m_currentRange = savedRange;
258260
}
259261

260262
void Llvm::lowerNode(GenTree* node)
@@ -795,21 +797,19 @@ void Llvm::lowerUnmanagedCall(GenTreeCall* callNode)
795797
// two or more consecutive PI calls.
796798
if (!callNode->IsSuppressGCTransition())
797799
{
800+
// TODO-LLVM-Upstream: don't allocate lvaInlinedPInvokeFrameVar (its size is zero).
798801
assert(_compiler->opts.ShouldUsePInvokeHelpers()); // No inline transition support yet.
799802
assert(_compiler->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM);
800803

801804
// Insert CORINFO_HELP_JIT_PINVOKE_BEGIN.
802-
GenTreeLclFld* frameAddr = _compiler->gtNewLclVarAddrNode(_compiler->lvaInlinedPInvokeFrameVar);
803-
GenTreeCall* helperCall = _compiler->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_BEGIN, TYP_VOID, frameAddr);
804-
CurrentRange().InsertBefore(callNode, frameAddr, helperCall);
805-
lowerNode(frameAddr);
805+
GenTreeCall* helperCall = _compiler->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_BEGIN, TYP_VOID);
806+
CurrentRange().InsertBefore(callNode, helperCall);
806807
lowerNode(helperCall);
807808

808809
// Insert CORINFO_HELP_JIT_PINVOKE_END. No need to explicitly lower the call/local address as the
809810
// normal lowering loop will pick them up.
810-
frameAddr = _compiler->gtNewLclVarAddrNode(_compiler->lvaInlinedPInvokeFrameVar);
811-
helperCall = _compiler->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_END, TYP_VOID, frameAddr);
812-
CurrentRange().InsertAfter(callNode, frameAddr, helperCall);
811+
helperCall = _compiler->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_END, TYP_VOID);
812+
CurrentRange().InsertAfter(callNode, helperCall);
813813
}
814814

815815
if (callNode->gtCallType != CT_INDIRECT)
@@ -1598,20 +1598,28 @@ bool Llvm::addVirtualUnwindFrameForExceptionHandling()
15981598
CORINFO_GENERIC_HANDLE ehInfoSymbol =
15991599
m_llvm->GetSparseVirtualUnwindInfo(&clauses.BottomRef(), clauses.Height());
16001600

1601-
GenTree* ehInfoNode =
1602-
m_compiler->gtNewIconHandleNode(reinterpret_cast<size_t>(ehInfoSymbol), GTF_ICON_CONST_PTR);
1603-
GenTree* unwindFrameLclAddr = m_compiler->gtNewLclVarAddrNode(unwindFrameLclNum);
1604-
GenTreeIntCon* initialUnwindIndexNode = m_compiler->gtNewIconNode(m_initialIndexValue, TYP_I_IMPL);
1605-
GenTreeCall* initializeCall =
1606-
m_compiler->gtNewHelperCallNode(CORINFO_HELP_LLVM_EH_PUSH_VIRTUAL_UNWIND_FRAME, TYP_VOID,
1607-
unwindFrameLclAddr, ehInfoNode, initialUnwindIndexNode);
1608-
1609-
LIR::Range initRange;
1610-
initRange.InsertAtEnd(unwindFrameLclAddr);
1611-
initRange.InsertAtEnd(ehInfoNode);
1612-
initRange.InsertAtEnd(initialUnwindIndexNode);
1613-
initRange.InsertAtEnd(initializeCall);
1614-
m_llvm->lowerAndInsertIntoFirstBlock(std::move(initRange));
1601+
// For frames with an RPI transition, we will use RPI helpers that combine the transitions with unwind
1602+
// frame linking. This is not just an optimization - the helpers hardcode where the RPI frame is located
1603+
// and the presence of an unwind frame, itself hard-set to be at a zero offset, affects that.
1604+
if (!m_compiler->opts.IsReversePInvoke())
1605+
{
1606+
GenTree* ehInfoNode =
1607+
m_compiler->gtNewIconHandleNode(reinterpret_cast<size_t>(ehInfoSymbol), GTF_ICON_CONST_PTR);
1608+
GenTree* unwindFrameLclAddr = m_compiler->gtNewLclVarAddrNode(unwindFrameLclNum);
1609+
GenTreeIntCon* initialUnwindIndexNode = m_compiler->gtNewIconNode(m_initialIndexValue, TYP_I_IMPL);
1610+
GenTreeCall* initializeCall =
1611+
m_compiler->gtNewHelperCallNode(CORINFO_HELP_LLVM_EH_PUSH_VIRTUAL_UNWIND_FRAME, TYP_VOID,
1612+
unwindFrameLclAddr, ehInfoNode, initialUnwindIndexNode);
1613+
1614+
LIR::Range initRange;
1615+
initRange.InsertAtEnd(unwindFrameLclAddr);
1616+
initRange.InsertAtEnd(ehInfoNode);
1617+
initRange.InsertAtEnd(initialUnwindIndexNode);
1618+
initRange.InsertAtEnd(initializeCall);
1619+
m_llvm->lowerAndInsertIntoFirstBlock(std::move(initRange));
1620+
}
1621+
1622+
m_llvm->m_ehInfoSymbol = ehInfoSymbol;
16151623
m_llvm->m_sparseVirtualUnwindFrameLclNum = unwindFrameLclNum;
16161624
}
16171625

@@ -1630,7 +1638,7 @@ bool Llvm::addVirtualUnwindFrameForExceptionHandling()
16301638
}
16311639

16321640
// Explicit pops are only needed for explicitly linked (via TLS) sparse frames.
1633-
if (m_llvm->m_sparseVirtualUnwindFrameLclNum != BAD_VAR_NUM)
1641+
if ((m_llvm->m_sparseVirtualUnwindFrameLclNum != BAD_VAR_NUM) && !m_compiler->opts.IsReversePInvoke())
16341642
{
16351643
for (BasicBlock* block : m_compiler->Blocks())
16361644
{

0 commit comments

Comments
 (0)