From 9296b80614d770603bdea83e7aa6242a7514b2a4 Mon Sep 17 00:00:00 2001 From: "John F. Carr" Date: Fri, 23 Aug 2024 15:22:33 -0400 Subject: [PATCH 1/4] New attribute IntrReadInaccessibleMemOnly; use IntrInaccessibleMemOnly for runtime start and stop --- llvm/include/llvm/IR/Intrinsics.td | 11 +++++++---- llvm/utils/TableGen/CodeGenIntrinsics.cpp | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index 089e5db9f22c..f6edd39b73e3 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -49,6 +49,8 @@ def IntrArgMemOnly : IntrinsicProperty; // accessible by the module being compiled. This is a weaker form of IntrNoMem. def IntrInaccessibleMemOnly : IntrinsicProperty; +def IntrReadInaccessibleMemOnly : IntrinsicProperty; + // IntrInaccessibleMemOrArgMemOnly -- This intrinsic only accesses memory that // its pointer-typed arguments point to or memory that is not accessible // by the module being compiled. This is a weaker form of IntrArgMemOnly. @@ -1719,10 +1721,10 @@ def int_syncregion_start : Intrinsic<[llvm_token_ty], [], [IntrArgMemOnly, IntrWillReturn]>; def int_tapir_runtime_start - : Intrinsic<[llvm_token_ty], [], [IntrArgMemOnly, IntrWillReturn]>; + : Intrinsic<[llvm_token_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; def int_tapir_runtime_end - : Intrinsic<[], [llvm_token_ty], [IntrArgMemOnly, IntrWillReturn]>; + : Intrinsic<[], [llvm_token_ty], [IntrInaccessibleMemOnly, IntrWillReturn]>; // Intrinsics for taskframes. @@ -1769,14 +1771,15 @@ def int_tapir_loop_grainsize // lowering transforms this intrinsic into ordinary frameaddress // intrinsics. def int_task_frameaddress - : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrWillReturn]>; + : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], + [IntrReadInaccessibleMemOnly,IntrWillReturn]>; // Ideally the types would be [llvm_anyptr_ty], [LLVMMatchType<0>] // but that does not work, so rely on the front end to insert bitcasts. def int_hyper_lookup : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty, llvm_anyint_ty, llvm_ptr_ty, llvm_ptr_ty], [ - IntrWillReturn, IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn, IntrReadInaccessibleMemOnly, IntrStrandPure, IntrHyperView, IntrInjective ]>; diff --git a/llvm/utils/TableGen/CodeGenIntrinsics.cpp b/llvm/utils/TableGen/CodeGenIntrinsics.cpp index 9eef4604ff03..fa0a026d372c 100644 --- a/llvm/utils/TableGen/CodeGenIntrinsics.cpp +++ b/llvm/utils/TableGen/CodeGenIntrinsics.cpp @@ -181,6 +181,8 @@ void CodeGenIntrinsic::setProperty(Record *R) { ME &= MemoryEffects::argMemOnly(); else if (R->getName() == "IntrInaccessibleMemOnly") ME &= MemoryEffects::inaccessibleMemOnly(); + else if (R->getName() == "IntrReadInaccessibleMemOnly") + ME &= MemoryEffects::inaccessibleMemOnly(ModRefInfo::Ref); else if (R->getName() == "IntrInaccessibleMemOrArgMemOnly") ME &= MemoryEffects::inaccessibleOrArgMemOnly(); else if (R->getName() == "Commutative") From f83b81882d363b6db172415605c7ece1bf8332ac Mon Sep 17 00:00:00 2001 From: "John F. Carr" Date: Fri, 23 Aug 2024 16:32:18 -0400 Subject: [PATCH 2/4] New builtin and intrinsic to get pointer to OpenCilk stack frame --- clang/include/clang/Basic/Builtins.def | 2 + clang/lib/CodeGen/CGBuiltin.cpp | 4 ++ clang/test/Cilk/tapir-frame.c | 29 +++++++++ llvm/include/llvm/IR/Intrinsics.td | 4 ++ .../llvm/Transforms/Tapir/LoweringUtils.h | 2 + .../llvm/Transforms/Tapir/OpenCilkABI.h | 3 + llvm/lib/CodeGen/IntrinsicLowering.cpp | 4 ++ llvm/lib/Transforms/Tapir/LoweringUtils.cpp | 1 + llvm/lib/Transforms/Tapir/OpenCilkABI.cpp | 48 +++++++++++++++ llvm/lib/Transforms/Tapir/TapirToTarget.cpp | 59 ++++++++++++------- 10 files changed, 135 insertions(+), 21 deletions(-) create mode 100644 clang/test/Cilk/tapir-frame.c diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 237b2950da74..895bce1b8f7f 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1800,6 +1800,8 @@ LANGBUILTIN(__arithmetic_fence, "v.", "tE", ALL_LANGUAGES) // and needs a builtin to carry the information to codegen. BUILTIN(__hyper_lookup, "v*vC*z.", "nU") +BUILTIN(__tapir_frame, "v*", "n") + #undef BUILTIN #undef LIBBUILTIN #undef LANGBUILTIN diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index a4c9c9ca0415..92c5ac87b399 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -5990,6 +5990,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Str.getPointer(), Zeros); return RValue::get(Ptr); } + case Builtin::BI__tapir_frame: { + Function *FF = CGM.getIntrinsic(Intrinsic::tapir_frame); + return RValue::get(Builder.CreateCall(FF, {})); + } case Builtin::BI__hyper_lookup: { llvm::Value *Size = EmitScalarExpr(E->getArg(1)); Function *F = CGM.getIntrinsic(Intrinsic::hyper_lookup, Size->getType()); diff --git a/clang/test/Cilk/tapir-frame.c b/clang/test/Cilk/tapir-frame.c new file mode 100644 index 000000000000..253f9c8c402d --- /dev/null +++ b/clang/test/Cilk/tapir-frame.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 %s -x c -O1 -fopencilk -mllvm -use-opencilk-runtime-bc=false -mllvm -debug-abi-calls=true -verify -S -emit-llvm -o - | FileCheck %s +// expected-no-diagnostics + +// CHECK-LABEL: zero +int zero() +{ + return __tapir_frame() != 0; + // CHECK: ret i32 0 +} + +// CHECK-LABEL: one +int one() +{ + extern void f(int); + _Cilk_spawn f(0); + _Cilk_spawn f(1); + // CHECK: ret i32 1 + return __tapir_frame() != 0; +} + +// CHECK-LABEL: function3 +int function3() +{ + extern void f(int); + extern int g(void *); + _Cilk_spawn f(0); + // CHECK: call i32 @g(ptr noundef nonnull %__cilkrts_sf) + return g(__tapir_frame()); +} diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index f6edd39b73e3..7fd340fe1818 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1767,6 +1767,10 @@ def int_tapir_loop_grainsize : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>], [IntrNoMem, IntrWillReturn, IntrSpeculatable]>; +// Return the stack_frame in an OpenCilk function, otherwise null. +def int_tapir_frame + : Intrinsic<[llvm_ptr_ty], [], [IntrWillReturn,IntrReadInaccessibleMemOnly]>; + // Intrinsic to get the frame address of a spawned task. Tapir // lowering transforms this intrinsic into ordinary frameaddress // intrinsics. diff --git a/llvm/include/llvm/Transforms/Tapir/LoweringUtils.h b/llvm/include/llvm/Transforms/Tapir/LoweringUtils.h index af71de32cc6d..9357ebc2b5e5 100644 --- a/llvm/include/llvm/Transforms/Tapir/LoweringUtils.h +++ b/llvm/include/llvm/Transforms/Tapir/LoweringUtils.h @@ -244,6 +244,8 @@ class TapirTarget { /// Lower a Tapir sync instruction \p SI. virtual void lowerSync(SyncInst &SI) = 0; + virtual void lowerFrameCall(CallBase *Call, DominatorTree &DT) {} + virtual void lowerReducerOperation(CallBase *Call) {} /// Lower calls to the tapir.runtime.{start,end} intrinsics. Only diff --git a/llvm/include/llvm/Transforms/Tapir/OpenCilkABI.h b/llvm/include/llvm/Transforms/Tapir/OpenCilkABI.h index 209a5d162039..af209b362ed2 100644 --- a/llvm/include/llvm/Transforms/Tapir/OpenCilkABI.h +++ b/llvm/include/llvm/Transforms/Tapir/OpenCilkABI.h @@ -157,6 +157,8 @@ class OpenCilkABI final : public TapirTarget { BasicBlock *GetDefaultSyncLandingpad(Function &F, Value *SF, DebugLoc Loc); + Value *getValidFrame(CallBase *FrameCall, DominatorTree &DT); + public: OpenCilkABI(Module &M); ~OpenCilkABI() { DetachCtxToStackFrame.clear(); } @@ -167,6 +169,7 @@ class OpenCilkABI final : public TapirTarget { Value *lowerGrainsizeCall(CallInst *GrainsizeCall) override final; void lowerSync(SyncInst &SI) override final; void lowerReducerOperation(CallBase *CI) override; + void lowerFrameCall(CallBase *CI, DominatorTree &DT) override; ArgStructMode getArgStructMode() const override final { return ArgStructMode::None; diff --git a/llvm/lib/CodeGen/IntrinsicLowering.cpp b/llvm/lib/CodeGen/IntrinsicLowering.cpp index 61920a0e04ab..c21a98f78c45 100644 --- a/llvm/lib/CodeGen/IntrinsicLowering.cpp +++ b/llvm/lib/CodeGen/IntrinsicLowering.cpp @@ -236,6 +236,10 @@ void IntrinsicLowering::LowerIntrinsicCall(CallInst *CI) { report_fatal_error("Code generator does not support intrinsic function '"+ Callee->getName()+"'!"); + case Intrinsic::tapir_frame: + CI->replaceAllUsesWith(Constant::getNullValue(CI->getType())); + break; + case Intrinsic::expect: { // Just replace __builtin_expect(exp, c) with EXP. Value *V = CI->getArgOperand(0); diff --git a/llvm/lib/Transforms/Tapir/LoweringUtils.cpp b/llvm/lib/Transforms/Tapir/LoweringUtils.cpp index 124b9f57ed3a..2db9d90a4a3e 100644 --- a/llvm/lib/Transforms/Tapir/LoweringUtils.cpp +++ b/llvm/lib/Transforms/Tapir/LoweringUtils.cpp @@ -1248,6 +1248,7 @@ bool TapirTarget::shouldProcessFunction(const Function &F) const { case Intrinsic::reducer_unregister: case Intrinsic::tapir_loop_grainsize: case Intrinsic::task_frameaddress: + case Intrinsic::tapir_frame: case Intrinsic::tapir_runtime_start: case Intrinsic::tapir_runtime_end: return true; diff --git a/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp b/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp index ab413f395302..b3992a891733 100644 --- a/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp +++ b/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringSet.h" #include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/CFG.h" #include "llvm/Analysis/TapirTaskInfo.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DebugInfo.h" @@ -1178,6 +1179,53 @@ OpenCilkABI::getLoopOutlineProcessor(const TapirLoopInfo *TL) { return nullptr; } +Value *OpenCilkABI::getValidFrame(CallBase *FrameCall, DominatorTree &DT) { + Function *F = FrameCall->getFunction(); + if (Value *Frame = DetachCtxToStackFrame.lookup(F)) { + // Make sure a call to enter_frame dominates this get_frame call + // and no call to leave_frame has potentially been executed. + // Otherwise return a null pointer value to mean unknown. + // This is correct in most functions and conservative in + // complicated functions. + bool Initialized = false; + Value *Enter1 = CILKRTS_FUNC(enter_frame_helper).getCallee(); + Value *Enter2 = CILKRTS_FUNC(enter_frame).getCallee(); + Value *Leave1 = CILKRTS_FUNC(leave_frame_helper).getCallee(); + Value *Leave2 = CILKRTS_FUNC(leave_frame).getCallee(); + Value *Leave3 = CilkHelperEpilogue.getCallee(); + Value *Leave4 = CilkParentEpilogue.getCallee(); + for (User *U : Frame->users()) { + if (CallBase *C = dyn_cast(U)) { + Function *Fn = C->getCalledFunction(); + if (Fn == nullptr) // indirect function call + continue; + if (Fn == Enter1 || Fn == Enter2) { + if (!Initialized && DT.dominates(C, FrameCall)) + Initialized = true; + continue; + } + if (Fn == Leave1 || Fn == Leave2 | Fn == Leave3 | Fn == Leave4) { + // TODO: ...unless an enter_frame call definitely intervenes. + if (isPotentiallyReachable(C, FrameCall, nullptr, &DT, nullptr)) + return Constant::getNullValue(FrameCall->getType()); + continue; + } + } + } + if (Initialized) + return Frame; + } + return Constant::getNullValue(FrameCall->getType()); +} + +void OpenCilkABI::lowerFrameCall(CallBase *FrameCall, DominatorTree &DT) { + assert(FrameCall->data_operands_size() == 0); + Value *Frame = getValidFrame(FrameCall, DT); + FrameCall->replaceAllUsesWith(Frame); + FrameCall->eraseFromParent(); +} + + void OpenCilkABI::lowerReducerOperation(CallBase *CI) { FunctionCallee Fn = nullptr; const Function *Called = CI->getCalledFunction(); diff --git a/llvm/lib/Transforms/Tapir/TapirToTarget.cpp b/llvm/lib/Transforms/Tapir/TapirToTarget.cpp index 84d9ffc43fb3..3d282487e885 100644 --- a/llvm/lib/Transforms/Tapir/TapirToTarget.cpp +++ b/llvm/lib/Transforms/Tapir/TapirToTarget.cpp @@ -180,35 +180,46 @@ bool TapirToTargetImpl::processSimpleABI(Function &F, BasicBlock *TFEntry) { SmallVector TaskFrameAddrCalls; SmallVector TapirRTCalls; SmallVector ReducerOperations; + SmallVector TapirFrameCalls; + for (BasicBlock &BB : F) { for (Instruction &I : BB) { - // Record calls to get Tapir-loop grainsizes. - if (IntrinsicInst *II = dyn_cast(&I)) - if (Intrinsic::tapir_loop_grainsize == II->getIntrinsicID()) + + // Record sync instructions in this function. + if (SyncInst *SI = dyn_cast(&I)) { + Syncs.push_back(SI); + continue; + } + + if (IntrinsicInst *II = dyn_cast(&I)) { + switch (II->getIntrinsicID()) { + case Intrinsic::tapir_loop_grainsize: GrainsizeCalls.push_back(II); + break; - // Record calls to task_frameaddr intrinsics. - if (IntrinsicInst *II = dyn_cast(&I)) - if (Intrinsic::task_frameaddress == II->getIntrinsicID()) + case Intrinsic::task_frameaddress: TaskFrameAddrCalls.push_back(II); + break; - // Record calls to tapir_runtime_start intrinsics. We rely on analyzing - // uses of these intrinsic calls to find calls to tapir_runtime_end. - if (IntrinsicInst *II = dyn_cast(&I)) - if (Intrinsic::tapir_runtime_start == II->getIntrinsicID()) + // Record calls to tapir_runtime_start intrinsics. + // We rely on analyzing uses of these intrinsic calls + // to find calls to tapir_runtime_end. + case Intrinsic::tapir_runtime_start: TapirRTCalls.push_back(II); - - // Record sync instructions in this function. - if (SyncInst *SI = dyn_cast(&I)) - Syncs.push_back(SI); - - if (!dyn_cast(&I)) + break; + + case Intrinsic::tapir_frame: + TapirFrameCalls.push_back(II); + break; + + case Intrinsic::hyper_lookup: + case Intrinsic::reducer_register: + case Intrinsic::reducer_unregister: + ReducerOperations.push_back(cast(&I)); + break; + } continue; - - if (isTapirIntrinsic(Intrinsic::hyper_lookup, &I, nullptr) || - isTapirIntrinsic(Intrinsic::reducer_register, &I, nullptr) || - isTapirIntrinsic(Intrinsic::reducer_unregister, &I, nullptr)) - ReducerOperations.push_back(cast(&I)); + } } } @@ -216,6 +227,12 @@ bool TapirToTargetImpl::processSimpleABI(Function &F, BasicBlock *TFEntry) { // helper functions generated by this process. bool Changed = false; + while (!TapirFrameCalls.empty()) { + CallInst *CI = TapirFrameCalls.pop_back_val(); + Target->lowerFrameCall(CI, GetDT(F)); + Changed = true; + } + // Lower calls to get Tapir-loop grainsizes. while (!GrainsizeCalls.empty()) { CallInst *GrainsizeCall = GrainsizeCalls.pop_back_val(); From febc042e46b4fb6d073d8b43039083eed92ea69a Mon Sep 17 00:00:00 2001 From: "John F. Carr" Date: Thu, 14 Nov 2024 15:45:21 -0500 Subject: [PATCH 3/4] Replace remaining calls to tapir_frame with null --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 5c422f44b338..1db380149702 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -7816,6 +7816,11 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, TLI.getFrameIndexTy(DAG.getDataLayout()), getValue(I.getArgOperand(0)))); return; + case Intrinsic::tapir_frame: { + EVT VT = TLI.getValueType(DAG.getDataLayout(), I.getType()); + setValue(&I, DAG.getConstant(0, sdl, VT)); + return; + } } } From 8cb8bf921b18473c05bee865faeb8b9f1e590f0a Mon Sep 17 00:00:00 2001 From: "John F. Carr" Date: Fri, 15 Nov 2024 15:17:36 -0500 Subject: [PATCH 4/4] More complicated way of tracking stack frame liveness --- llvm/lib/Transforms/Tapir/OpenCilkABI.cpp | 111 ++++++++++++++++------ 1 file changed, 82 insertions(+), 29 deletions(-) diff --git a/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp b/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp index b3992a891733..82cfa766ff29 100644 --- a/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp +++ b/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp @@ -1179,48 +1179,101 @@ OpenCilkABI::getLoopOutlineProcessor(const TapirLoopInfo *TL) { return nullptr; } +// Return the stack frame for this function if it is definitely +// initialized. Otherwise return null. +// The lookup call must be dominated by a call to enter frame. +// The lookup call must not be reachable by any call to leave +// frame unless there is an intervening enter frame. +// This function will conservatively return null in some cases +// it does not understand. Most likely to matter, if a basic +// block contains a leave frame call followed by an enter frame +// call it will be treated as leaving the frame invalid. + Value *OpenCilkABI::getValidFrame(CallBase *FrameCall, DominatorTree &DT) { - Function *F = FrameCall->getFunction(); - if (Value *Frame = DetachCtxToStackFrame.lookup(F)) { - // Make sure a call to enter_frame dominates this get_frame call - // and no call to leave_frame has potentially been executed. - // Otherwise return a null pointer value to mean unknown. - // This is correct in most functions and conservative in - // complicated functions. - bool Initialized = false; - Value *Enter1 = CILKRTS_FUNC(enter_frame_helper).getCallee(); - Value *Enter2 = CILKRTS_FUNC(enter_frame).getCallee(); - Value *Leave1 = CILKRTS_FUNC(leave_frame_helper).getCallee(); - Value *Leave2 = CILKRTS_FUNC(leave_frame).getCallee(); - Value *Leave3 = CilkHelperEpilogue.getCallee(); - Value *Leave4 = CilkParentEpilogue.getCallee(); - for (User *U : Frame->users()) { - if (CallBase *C = dyn_cast(U)) { - Function *Fn = C->getCalledFunction(); - if (Fn == nullptr) // indirect function call - continue; - if (Fn == Enter1 || Fn == Enter2) { + BasicBlock *FrameBlock = FrameCall->getParent(); + Function *F = FrameBlock->getParent(); + Value *Frame = DetachCtxToStackFrame.lookup(F); + if (!Frame) + return nullptr; + + // Blocks with enter frame calls. + SmallPtrSet EnterBlocks; + // The last enter or leave frame call before FrameCall + // in the same basic block. + CallBase *SameBlockCall = nullptr; + // True if the call is enter, false if leave or not yet set. + bool SameBlockEnter = false; + // Leave frame calls other than those before FrameCall + // in the same basic block. + SmallPtrSet Leaves; + + // Use pointer identity to check call target. Enter and leave + // are always called with a known getCalledFunction(). + // This function runs before inlining of runtime calls. + Value *Enter1 = CILKRTS_FUNC(enter_frame_helper).getCallee(); + Value *Enter2 = CILKRTS_FUNC(enter_frame).getCallee(); + Value *Leave1 = CILKRTS_FUNC(leave_frame_helper).getCallee(); + Value *Leave2 = CILKRTS_FUNC(leave_frame).getCallee(); + Value *Leave3 = CilkHelperEpilogue.getCallee(); + Value *Leave4 = CilkParentEpilogue.getCallee(); + + // Collect all enter_frame and leave_frame calls. + bool Initialized = false; + for (User *U : Frame->users()) { + if (CallBase *C = dyn_cast(U)) { + Function *Fn = C->getCalledFunction(); + BasicBlock *Block = C->getParent(); + if (Fn == Enter1 || Fn == Enter2) { + if (FrameBlock == Block && C->comesBefore(FrameCall)) { + if (!SameBlockCall || SameBlockCall->comesBefore(C)) { + SameBlockCall = C; + SameBlockEnter = true; + } + Initialized = true; + } else { + EnterBlocks.insert(Block); if (!Initialized && DT.dominates(C, FrameCall)) Initialized = true; - continue; } - if (Fn == Leave1 || Fn == Leave2 | Fn == Leave3 | Fn == Leave4) { - // TODO: ...unless an enter_frame call definitely intervenes. - if (isPotentiallyReachable(C, FrameCall, nullptr, &DT, nullptr)) - return Constant::getNullValue(FrameCall->getType()); - continue; + } else if (Fn == Leave1 || Fn == Leave2 || Fn == Leave3 || Fn == Leave4) { + if (Block == FrameBlock && C->comesBefore(FrameCall)) { + if (!SameBlockCall || SameBlockCall->comesBefore(C)) { + SameBlockCall = C; + SameBlockEnter = false; + } + } else { + Leaves.insert(C); } } } - if (Initialized) - return Frame; } - return Constant::getNullValue(FrameCall->getType()); + if (!Initialized) + return nullptr; + + // Is the answer found in the same basic block? + if (SameBlockCall) + return SameBlockEnter ? Frame : nullptr; + + // Ignore any enter frame call in a block that also contains + // a leave frame call. + for (CallBase *L : Leaves) { + EnterBlocks.erase(L->getParent()); + } + + // Look for leave frame calls that might reach the use without + // an intervening enter frame call. + for (CallBase *L : Leaves) { + if (isPotentiallyReachable(L, FrameCall, &EnterBlocks, &DT, nullptr)) + return nullptr; + } + return Frame; } void OpenCilkABI::lowerFrameCall(CallBase *FrameCall, DominatorTree &DT) { assert(FrameCall->data_operands_size() == 0); Value *Frame = getValidFrame(FrameCall, DT); + if (Frame == nullptr) + Frame = Constant::getNullValue(FrameCall->getType()); FrameCall->replaceAllUsesWith(Frame); FrameCall->eraseFromParent(); }