- Notifications
You must be signed in to change notification settings - Fork 15.3k
[X86,SimplifyCFG] Support hoisting load/store with conditional faulting (Part I) #96878
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
8416d9f fabf994 75a84ba dc7f830 4be6358 d5293e4 17d9d5f 976a916 36704d2 5088626 69493b3 d71e158 491aeab 600beeb 1cdad18 46bb03a ae99932 450eafe 232e1ba File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| | @@ -117,6 +117,12 @@ static cl::opt<bool> | |||||||||||||
| HoistCommon("simplifycfg-hoist-common", cl::Hidden, cl::init(true), | ||||||||||||||
| cl::desc("Hoist common instructions up to the parent block")); | ||||||||||||||
| | ||||||||||||||
| static cl::opt<bool> HoistLoadsStoresWithCondFaulting( | ||||||||||||||
| "simplifycfg-hoist-loads-stores-with-cond-faulting", cl::Hidden, | ||||||||||||||
| cl::init(true), | ||||||||||||||
| cl::desc("Hoist loads/stores if the target supports " | ||||||||||||||
| "conditional faulting")); | ||||||||||||||
| | ||||||||||||||
| static cl::opt<unsigned> | ||||||||||||||
| HoistCommonSkipLimit("simplifycfg-hoist-common-skip-limit", cl::Hidden, | ||||||||||||||
| cl::init(20), | ||||||||||||||
| | @@ -2978,6 +2984,25 @@ static bool isProfitableToSpeculate(const BranchInst *BI, bool Invert, | |||||||||||||
| return BIEndProb < Likely; | ||||||||||||||
| } | ||||||||||||||
| | ||||||||||||||
| static bool isSafeCheapLoadStore(const Instruction *I, | ||||||||||||||
| const TargetTransformInfo &TTI) { | ||||||||||||||
| // Not handle volatile or atomic. | ||||||||||||||
| if (auto *L = dyn_cast<LoadInst>(I)) { | ||||||||||||||
| if (!L->isSimple()) | ||||||||||||||
| return false; | ||||||||||||||
| } else if (auto *S = dyn_cast<StoreInst>(I)) { | ||||||||||||||
| if (!S->isSimple()) | ||||||||||||||
| return false; | ||||||||||||||
| } else | ||||||||||||||
| return false; | ||||||||||||||
| | ||||||||||||||
| // llvm.masked.load/store use i32 for alignment while load/store use i64. | ||||||||||||||
| // That's why we have the alignment limitation. | ||||||||||||||
| // FIXME: Update the prototype of the intrinsics? | ||||||||||||||
| return TTI.hasConditionalLoadStoreForType(getLoadStoreType(I)) && | ||||||||||||||
| getLoadStoreAlignment(I) < Value::MaximumAlignment; | ||||||||||||||
| } | ||||||||||||||
| | ||||||||||||||
| /// Speculate a conditional basic block flattening the CFG. | ||||||||||||||
| /// | ||||||||||||||
| /// Note that this is a very risky transform currently. Speculating | ||||||||||||||
| | @@ -3052,6 +3077,9 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(BranchInst *BI, | |||||||||||||
| SmallVector<Instruction *, 4> SpeculatedDbgIntrinsics; | ||||||||||||||
| | ||||||||||||||
| unsigned SpeculatedInstructions = 0; | ||||||||||||||
| bool HositLoadsStores = HoistLoadsStoresWithCondFaulting && | ||||||||||||||
| Options.HoistLoadsStoresWithCondFaulting; | ||||||||||||||
| SmallVector<Instruction *, 2> SpeculatedConditionalLoadsStores; | ||||||||||||||
| Value *SpeculatedStoreValue = nullptr; | ||||||||||||||
| StoreInst *SpeculatedStore = nullptr; | ||||||||||||||
| EphemeralValueTracker EphTracker; | ||||||||||||||
| | @@ -3080,22 +3108,31 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(BranchInst *BI, | |||||||||||||
| | ||||||||||||||
| // Only speculatively execute a single instruction (not counting the | ||||||||||||||
| // terminator) for now. | ||||||||||||||
| ++SpeculatedInstructions; | ||||||||||||||
| bool IsSafeCheapLoadStore = | ||||||||||||||
| HositLoadsStores && isSafeCheapLoadStore(&I, TTI); | ||||||||||||||
| // Not count load/store into cost if target supports conditional faulting | ||||||||||||||
| // b/c it's cheap to speculate it. | ||||||||||||||
KanRobert marked this conversation as resolved. Show resolved Hide resolved | ||||||||||||||
| if (IsSafeCheapLoadStore) | ||||||||||||||
| SpeculatedConditionalLoadsStores.push_back(&I); | ||||||||||||||
| else | ||||||||||||||
| ++SpeculatedInstructions; | ||||||||||||||
| | ||||||||||||||
| if (SpeculatedInstructions > 1) | ||||||||||||||
| return false; | ||||||||||||||
| | ||||||||||||||
| // Don't hoist the instruction if it's unsafe or expensive. | ||||||||||||||
| if (!isSafeToSpeculativelyExecute(&I) && | ||||||||||||||
| !(HoistCondStores && (SpeculatedStoreValue = isSafeToSpeculateStore( | ||||||||||||||
| &I, BB, ThenBB, EndBB)))) | ||||||||||||||
| if (!IsSafeCheapLoadStore && !isSafeToSpeculativelyExecute(&I) && | ||||||||||||||
| !(HoistCondStores && !SpeculatedStoreValue && | ||||||||||||||
| (SpeculatedStoreValue = | ||||||||||||||
| isSafeToSpeculateStore(&I, BB, ThenBB, EndBB)))) | ||||||||||||||
| return false; | ||||||||||||||
| if (!SpeculatedStoreValue && | ||||||||||||||
| if (!IsSafeCheapLoadStore && !SpeculatedStoreValue && | ||||||||||||||
| computeSpeculationCost(&I, TTI) > | ||||||||||||||
| PHINodeFoldingThreshold * TargetTransformInfo::TCC_Basic) | ||||||||||||||
| return false; | ||||||||||||||
| | ||||||||||||||
| // Store the store speculation candidate. | ||||||||||||||
| if (SpeculatedStoreValue) | ||||||||||||||
| if (!SpeculatedStore && SpeculatedStoreValue) | ||||||||||||||
| SpeculatedStore = cast<StoreInst>(&I); | ||||||||||||||
| | ||||||||||||||
| // Do not hoist the instruction if any of its operands are defined but not | ||||||||||||||
| | @@ -3122,11 +3159,11 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(BranchInst *BI, | |||||||||||||
| | ||||||||||||||
| // Check that we can insert the selects and that it's not too expensive to do | ||||||||||||||
| // so. | ||||||||||||||
| bool Convert = SpeculatedStore != nullptr; | ||||||||||||||
| bool Convert = | ||||||||||||||
| SpeculatedStore != nullptr || !SpeculatedConditionalLoadsStores.empty(); | ||||||||||||||
| InstructionCost Cost = 0; | ||||||||||||||
| Convert |= validateAndCostRequiredSelects(BB, ThenBB, EndBB, | ||||||||||||||
| SpeculatedInstructions, | ||||||||||||||
| Cost, TTI); | ||||||||||||||
| SpeculatedInstructions, Cost, TTI); | ||||||||||||||
| if (!Convert || Cost > Budget) | ||||||||||||||
| return false; | ||||||||||||||
| | ||||||||||||||
| | @@ -3214,6 +3251,107 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(BranchInst *BI, | |||||||||||||
| BB->splice(BI->getIterator(), ThenBB, ThenBB->begin(), | ||||||||||||||
| std::prev(ThenBB->end())); | ||||||||||||||
| | ||||||||||||||
| // If the target supports conditional faulting, | ||||||||||||||
| // we look for the following pattern: | ||||||||||||||
| // \code | ||||||||||||||
| // BB: | ||||||||||||||
| // ... | ||||||||||||||
| // %cond = icmp ult %x, %y | ||||||||||||||
| // br i1 %cond, label %TrueBB, label %FalseBB | ||||||||||||||
| // FalseBB: | ||||||||||||||
| // store i32 1, ptr %q, align 4 | ||||||||||||||
| // ... | ||||||||||||||
| // TrueBB: | ||||||||||||||
| // %maskedloadstore = load i32, ptr %b, align 4 | ||||||||||||||
| // store i32 %maskedloadstore, ptr %p, align 4 | ||||||||||||||
| // ... | ||||||||||||||
| // \endcode | ||||||||||||||
| // | ||||||||||||||
| // and transform it into: | ||||||||||||||
| // | ||||||||||||||
| // \code | ||||||||||||||
| // BB: | ||||||||||||||
| // ... | ||||||||||||||
| // %cond = icmp ult %x, %y | ||||||||||||||
| // %maskedloadstore = cload i32, ptr %b, %cond | ||||||||||||||
| // cstore i32 %maskedloadstore, ptr %p, %cond | ||||||||||||||
| // cstore i32 1, ptr %q, ~%cond | ||||||||||||||
| // br i1 %cond, label %TrueBB, label %FalseBB | ||||||||||||||
| // FalseBB: | ||||||||||||||
| // ... | ||||||||||||||
| // TrueBB: | ||||||||||||||
| // ... | ||||||||||||||
| // \endcode | ||||||||||||||
| // | ||||||||||||||
| // where cload/cstore are represented by llvm.masked.load/store intrinsics, | ||||||||||||||
| // e.g. | ||||||||||||||
| // | ||||||||||||||
| // \code | ||||||||||||||
| // %vcond = bitcast i1 %cond to <1 x i1> | ||||||||||||||
| // %v0 = call <1 x i32> @llvm.masked.load.v1i32.p0 | ||||||||||||||
| // (ptr %b, i32 4, <1 x i1> %vcond, <1 x i32> poison) | ||||||||||||||
| // %maskedloadstore = bitcast <1 x i32> %v0 to i32 | ||||||||||||||
| // call void @llvm.masked.store.v1i32.p0 | ||||||||||||||
| // (<1 x i32> %v0, ptr %p, i32 4, <1 x i1> %vcond) | ||||||||||||||
| // %cond.not = xor i1 %cond, true | ||||||||||||||
| // %vcond.not = bitcast i1 %cond.not to <1 x i> | ||||||||||||||
| // call void @llvm.masked.store.v1i32.p0 | ||||||||||||||
| // (<1 x i32> <i32 1>, ptr %q, i32 4, <1x i1> %vcond.not) | ||||||||||||||
| // \endcode | ||||||||||||||
| // | ||||||||||||||
| // So we need to turn hoisted load/store into cload/cstore. | ||||||||||||||
| auto &Context = BI->getParent()->getContext(); | ||||||||||||||
| auto *VCondTy = FixedVectorType::get(Type::getInt1Ty(Context), 1); | ||||||||||||||
| auto *Cond = BI->getOperand(0); | ||||||||||||||
| Value *Mask = nullptr; | ||||||||||||||
| // Construct the condition if needed. | ||||||||||||||
| if (!SpeculatedConditionalLoadsStores.empty()) { | ||||||||||||||
| IRBuilder<> Builder(SpeculatedConditionalLoadsStores.back()); | ||||||||||||||
| if (Invert) | ||||||||||||||
| Mask = Builder.CreateBitCast( | ||||||||||||||
| Builder.CreateXor(Cond, ConstantInt::getTrue(Context)), VCondTy); | ||||||||||||||
| else | ||||||||||||||
| Mask = Builder.CreateBitCast(Cond, VCondTy); | ||||||||||||||
| ||||||||||||||
| if (Invert) | |
| Mask = Builder.CreateBitCast( | |
| Builder.CreateXor(Cond, ConstantInt::getTrue(Context)), VCondTy); | |
| else | |
| Mask = Builder.CreateBitCast(Cond, VCondTy); | |
| Mask = Builder.CreateBitCast(Invert ? Builder.CreateXor(Cond, ConstantInt::getTrue(Context)) : Cond, VCondTy); |
Is it possible to avoid creating a null pointer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
The variable Mask is a loop invariant and we definitely want to define it outside the loop below. And the Mask instruction should be only created when the candidates for CLOAD/CSTORE is not empty. So we have to assign the value inside the if clause while declaring it outside the if clause.
Of course, we can keep Mask uninitialized at the declaration site w/o affecting the correctness b/c it's never used if the CLOAD/CSTORE optimization can not be done. But I think it's not worthy b/c it does not follow the best practice and nullptr initialization should not bring any cost indeed.
Uh oh!
There was an error while loading. Please reload this page.