2828using namespace clang ;
2929using namespace clang ::interp;
3030
31- static bool RetValue (InterpState &S, CodePtr &Pt) {
31+ #if __has_cpp_attribute(clang::musttail)
32+ #define MUSTTAIL [[clang::musttail]]
33+ #elif __has_cpp_attribute(msvc::musttail)
34+ #define MUSTTAIL [[msvc::musttail]]
35+ #elif __has_attribute(musttail)
36+ #define MUSTTAIL __attribute__ ((musttail))
37+ #endif
38+
39+ // On MSVC, musttail does not guarantee tail calls in debug mode.
40+ // We disable it on MSVC generally since it doesn't seem to be able
41+ // to handle the way we use tailcalls.
42+ // PPC can't tail-call external calls, which is a problem for InterpNext.
43+ #if defined(_MSC_VER) || defined(__powerpc__) || !defined(MUSTTAIL)
44+ #undef MUSTTAIL
45+ #define MUSTTAIL
46+ #define USE_TAILCALLS 0
47+ #else
48+ #define USE_TAILCALLS 1
49+ #endif
50+
51+ PRESERVE_NONE static bool RetValue (InterpState &S, CodePtr &Ptr) {
3252 llvm::report_fatal_error (" Interpreter cannot return values" );
3353}
3454
@@ -55,76 +75,6 @@ static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
5575 return S.noteStep (PC);
5676}
5777
58- // https://github.com/llvm/llvm-project/issues/102513
59- #if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
60- #pragma optimize("", off)
61- #endif
62- // FIXME: We have the large switch over all opcodes here again, and in
63- // Interpret().
64- static bool BCP (InterpState &S, CodePtr &RealPC, int32_t Offset, PrimType PT) {
65- [[maybe_unused]] CodePtr PCBefore = RealPC;
66- size_t StackSizeBefore = S.Stk .size ();
67-
68- auto SpeculativeInterp = [&S, RealPC]() -> bool {
69- const InterpFrame *StartFrame = S.Current ;
70- CodePtr PC = RealPC;
71-
72- for (;;) {
73- auto Op = PC.read <Opcode>();
74- if (Op == OP_EndSpeculation)
75- return true ;
76- CodePtr OpPC = PC;
77-
78- switch (Op) {
79- #define GET_INTERP
80- #include " Opcodes.inc"
81- #undef GET_INTERP
82- }
83- }
84- llvm_unreachable (" We didn't see an EndSpeculation op?" );
85- };
86-
87- if (SpeculativeInterp ()) {
88- if (PT == PT_Ptr) {
89- const auto &Ptr = S.Stk .pop <Pointer>();
90- assert (S.Stk .size () == StackSizeBefore);
91- S.Stk .push <Integral<32 , true >>(
92- Integral<32 , true >::from (CheckBCPResult (S, Ptr)));
93- } else {
94- // Pop the result from the stack and return success.
95- TYPE_SWITCH (PT, S.Stk .pop <T>(););
96- assert (S.Stk .size () == StackSizeBefore);
97- S.Stk .push <Integral<32 , true >>(Integral<32 , true >::from (1 ));
98- }
99- } else {
100- if (!S.inConstantContext ())
101- return Invalid (S, RealPC);
102-
103- S.Stk .clearTo (StackSizeBefore);
104- S.Stk .push <Integral<32 , true >>(Integral<32 , true >::from (0 ));
105- }
106-
107- // RealPC should not have been modified.
108- assert (*RealPC == *PCBefore);
109-
110- // Jump to end label. This is a little tricker than just RealPC += Offset
111- // because our usual jump instructions don't have any arguments, to the offset
112- // we get is a little too much and we need to subtract the size of the
113- // bool and PrimType arguments again.
114- int32_t ParamSize = align (sizeof (PrimType));
115- assert (Offset >= ParamSize);
116- RealPC += Offset - ParamSize;
117-
118- [[maybe_unused]] CodePtr PCCopy = RealPC;
119- assert (PCCopy.read <Opcode>() == OP_EndSpeculation);
120-
121- return true ;
122- }
123- // https://github.com/llvm/llvm-project/issues/102513
124- #if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
125- #pragma optimize("", on)
126- #endif
127-
12878static void diagnoseMissingInitializer (InterpState &S, CodePtr OpPC,
12979 const ValueDecl *VD) {
13080 const SourceInfo &E = S.Current ->getSource (OpPC);
@@ -258,6 +208,9 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
258208
259209namespace clang {
260210namespace interp {
211+ PRESERVE_NONE static bool BCP (InterpState &S, CodePtr &RealPC, int32_t Offset,
212+ PrimType PT);
213+
261214static void popArg (InterpState &S, const Expr *Arg) {
262215 PrimType Ty = S.getContext ().classify (Arg).value_or (PT_Ptr);
263216 TYPE_SWITCH (Ty, S.Stk .discard <T>());
@@ -2582,38 +2535,156 @@ bool CastMemberPtrDerivedPop(InterpState &S, CodePtr OpPC, int32_t Off,
25822535 return castBackMemberPointer (S, Ptr, Off, BaseDecl);
25832536}
25842537
2585- // https://github.com/llvm/llvm-project/issues/102513
2586- #if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
2587- #pragma optimize("", off)
2538+ // FIXME: Would be nice to generate this instead of hardcoding it here.
2539+ constexpr bool OpReturns (Opcode Op) {
2540+ return Op == OP_RetVoid || Op == OP_RetValue || Op == OP_NoRet ||
2541+ Op == OP_RetSint8 || Op == OP_RetUint8 || Op == OP_RetSint16 ||
2542+ Op == OP_RetUint16 || Op == OP_RetSint32 || Op == OP_RetUint32 ||
2543+ Op == OP_RetSint64 || Op == OP_RetUint64 || Op == OP_RetIntAP ||
2544+ Op == OP_RetIntAPS || Op == OP_RetBool || Op == OP_RetFixedPoint ||
2545+ Op == OP_RetPtr || Op == OP_RetMemberPtr || Op == OP_RetFloat ||
2546+ Op == OP_EndSpeculation;
2547+ }
2548+
2549+ #if USE_TAILCALLS
2550+ PRESERVE_NONE static bool InterpNext (InterpState &S, CodePtr &PC);
25882551#endif
2552+
2553+ // The dispatcher functions read the opcode arguments from the
2554+ // bytecode and call the implementation function.
2555+ #define GET_INTERPFN_DISPATCHERS
2556+ #include " Opcodes.inc"
2557+ #undef GET_INTERPFN_DISPATCHERS
2558+
2559+ using InterpFn = bool (*)(InterpState &, CodePtr &PC) PRESERVE_NONE;
2560+ // Array of the dispatcher functions defined above.
2561+ const InterpFn InterpFunctions[] = {
2562+ #define GET_INTERPFN_LIST
2563+ #include " Opcodes.inc"
2564+ #undef GET_INTERPFN_LIST
2565+ };
2566+
2567+ #if USE_TAILCALLS
2568+ // Read the next opcode and call the dispatcher function.
2569+ PRESERVE_NONE static bool InterpNext (InterpState &S, CodePtr &PC) {
2570+ auto Op = PC.read <Opcode>();
2571+ auto Fn = InterpFunctions[Op];
2572+ MUSTTAIL return Fn (S, PC);
2573+ }
2574+ #endif
2575+
25892576bool Interpret (InterpState &S) {
25902577 // The current stack frame when we started Interpret().
25912578 // This is being used by the ops to determine wheter
25922579 // to return from this function and thus terminate
25932580 // interpretation.
2594- const InterpFrame *StartFrame = S.Current ;
25952581 assert (!S.Current ->isRoot ());
25962582 CodePtr PC = S.Current ->getPC ();
25972583
2598- // Empty program.
2599- if (!PC)
2600- return true ;
2584+ #if USE_TAILCALLS
2585+ return InterpNext (S, PC);
2586+ #else
2587+ while (true ) {
2588+ auto Op = PC.read <Opcode>();
2589+ auto Fn = InterpFunctions[Op];
2590+
2591+ if (!Fn (S, PC))
2592+ return false ;
2593+ if (OpReturns (Op))
2594+ break ;
2595+ }
2596+ return true ;
2597+ #endif
2598+ }
2599+
2600+ // / This is used to implement speculative execution via __builtin_constant_p
2601+ // / when we generate bytecode.
2602+ // /
2603+ // / The setup here is that we use the same tailcall mechanism for speculative
2604+ // / evaluation that we use for the regular one.
2605+ // / Since each speculative execution ends with an EndSpeculation opcode,
2606+ // / that one does NOT call InterpNext() but simply returns true.
2607+ // / This way, we return back to this function when we see an EndSpeculation,
2608+ // / OR (of course), when we encounter an error and one of the opcodes
2609+ // / returns false.
2610+ PRESERVE_NONE static bool BCP (InterpState &S, CodePtr &RealPC, int32_t Offset,
2611+ PrimType PT) {
2612+ [[maybe_unused]] CodePtr PCBefore = RealPC;
2613+ size_t StackSizeBefore = S.Stk .size ();
26012614
2602- for (;;) {
2615+ // Speculation depth must be at least 1 here, since we must have
2616+ // passed a StartSpeculation op before.
2617+ #ifndef NDEBUG
2618+ [[maybe_unused]] unsigned DepthBefore = S.SpeculationDepth ;
2619+ assert (DepthBefore >= 1 );
2620+ #endif
2621+
2622+ CodePtr PC = RealPC;
2623+ auto SpeculativeInterp = [&S, &PC]() -> bool {
2624+ // Ignore diagnostics during speculative execution.
2625+ PushIgnoreDiags (S, PC);
2626+ auto _ = llvm::scope_exit ([&]() { PopIgnoreDiags (S, PC); });
2627+
2628+ #if USE_TAILCALLS
26032629 auto Op = PC.read <Opcode>();
2604- CodePtr OpPC = PC;
2630+ auto Fn = InterpFunctions[Op];
2631+ return Fn (S, PC);
2632+ #else
2633+ while (true ) {
2634+ auto Op = PC.read <Opcode>();
2635+ auto Fn = InterpFunctions[Op];
26052636
2606- switch (Op) {
2607- # define GET_INTERP
2608- # include " Opcodes.inc "
2609- # undef GET_INTERP
2637+ if (! Fn (S, PC))
2638+ return false ;
2639+ if ( OpReturns (Op))
2640+ break ;
26102641 }
2642+ return true ;
2643+ #endif
2644+ };
2645+
2646+ if (SpeculativeInterp ()) {
2647+ // Speculation must've ended naturally via a EndSpeculation opcode.
2648+ assert (S.SpeculationDepth == DepthBefore - 1 );
2649+ if (PT == PT_Ptr) {
2650+ const auto &Ptr = S.Stk .pop <Pointer>();
2651+ assert (S.Stk .size () == StackSizeBefore);
2652+ S.Stk .push <Integral<32 , true >>(
2653+ Integral<32 , true >::from (CheckBCPResult (S, Ptr)));
2654+ } else {
2655+ // Pop the result from the stack and return success.
2656+ TYPE_SWITCH (PT, S.Stk .discard <T>(););
2657+ assert (S.Stk .size () == StackSizeBefore);
2658+ S.Stk .push <Integral<32 , true >>(Integral<32 , true >::from (1 ));
2659+ }
2660+ } else {
2661+ // End the speculation manually since we didn't call EndSpeculation
2662+ // naturally.
2663+ EndSpeculation (S, RealPC);
2664+
2665+ if (!S.inConstantContext ())
2666+ return Invalid (S, RealPC);
2667+
2668+ S.Stk .clearTo (StackSizeBefore);
2669+ S.Stk .push <Integral<32 , true >>(Integral<32 , true >::from (0 ));
26112670 }
2671+
2672+ // RealPC should not have been modified.
2673+ assert (*RealPC == *PCBefore);
2674+
2675+ // We have already evaluated this speculation's EndSpeculation opcode.
2676+ assert (S.SpeculationDepth == DepthBefore - 1 );
2677+
2678+ // Jump to end label. This is a little tricker than just RealPC += Offset
2679+ // because our usual jump instructions don't have any arguments, to the offset
2680+ // we get is a little too much and we need to subtract the size of the
2681+ // bool and PrimType arguments again.
2682+ int32_t ParamSize = align (sizeof (PrimType));
2683+ assert (Offset >= ParamSize);
2684+ RealPC += Offset - ParamSize;
2685+
2686+ return true ;
26122687}
2613- // https://github.com/llvm/llvm-project/issues/102513
2614- #if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
2615- #pragma optimize("", on)
2616- #endif
26172688
26182689} // namespace interp
26192690} // namespace clang
0 commit comments