Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce llvm.experimental.widenable_condition intrinsic
This patch introduces a new instinsic `@llvm.experimental.widenable_condition` that allows explicit representation for guards. It is an alternative to using `@llvm.experimental.guard` intrinsic that does not contain implicit control flow. We keep finding places where `@llvm.experimental.guard` is not supported or treated too conservatively, and there are 2 reasons to that: - `@llvm.experimental.guard` has memory write side effect to model implicit control flow, and this sometimes confuses passes and analyzes that work with memory; - Not all passes and analysis are aware of the semantics of guards. These passes treat them as regular throwing call and have no idea that the condition of guard may be used to prove something. One well-known place which had caused us troubles in the past is explicit loop iteration count calculation in SCEV. Another example is new loop unswitching which is not aware of guards. Whenever a new pass appears, we potentially have this problem there. Rather than go and fix all these places (and commit to keep track of them and add support in future), it seems more reasonable to leverage the existing optimizer's logic as much as possible. The only significant difference between guards and regular explicit branches is that guard's condition can be widened. It means that a guard contains (explicitly or implicitly) a `deopt` block successor, and it is always legal to go there no matter what the guard condition is. The other successor is a guarded block, and it is only legal to go there if the condition is true. This patch introduces a new explicit form of guards alternative to `@llvm.experimental.guard` intrinsic. Now a widenable guard can be represented in the CFG explicitly like this: %widenable_condition = call i1 @llvm.experimental.widenable.condition() %new_condition = and i1 %cond, %widenable_condition br i1 %new_condition, label %guarded, label %deopt guarded: ; Guarded instructions deopt: call type @llvm.experimental.deoptimize(<args...>) [ "deopt"(<deopt_args...>) ] The new intrinsic `@llvm.experimental.widenable.condition` has semantics of an `undef`, but the intrinsic prevents the optimizer from folding it early. This form should exploit all optimization boons provided to `br` instuction, and it still can be widened by replacing the result of `@llvm.experimental.widenable.condition()` with `and` with any arbitrary boolean value (as long as the branch that is taken when it is `false` has a deopt and has no side-effects). For more motivation, please check llvm-dev discussion "[llvm-dev] Giving up using implicit control flow in guards". This patch introduces this new intrinsic with respective LangRef changes and a pass that converts old-style guards (expressed as intrinsics) into the new form. The naming discussion is still ungoing. Merging this to unblock further items. We can later change the name of this intrinsic. Reviewed By: reames, fedor.sergeev, sanjoy Differential Revision: https://reviews.llvm.org/D51207 llvm-svn: 348593
- Loading branch information
Max Kazantsev
committed
Dec 7, 2018
1 parent
d6e6e23
commit b9e65cb
Showing
11 changed files
with
532 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
//===-- MakeGuardsExplicit.h - Turn guard intrinsics into guard branches --===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This pass lowers the @llvm.experimental.guard intrinsic to the new form of | ||
// guard represented as widenable explicit branch to the deopt block. The | ||
// difference between this pass and LowerGuardIntrinsic is that after this pass | ||
// the guard represented as intrinsic: | ||
// | ||
// call void(i1, ...) @llvm.experimental.guard(i1 %old_cond) [ "deopt"() ] | ||
// | ||
// transforms to a guard represented as widenable explicit branch: | ||
// | ||
// %widenable_cond = call i1 @llvm.experimental.widenable.condition() | ||
// br i1 (%old_cond & %widenable_cond), label %guarded, label %deopt | ||
// | ||
// Here: | ||
// - The semantics of @llvm.experimental.widenable.condition allows to replace | ||
// %widenable_cond with the construction (%widenable_cond & %any_other_cond) | ||
// without loss of correctness; | ||
// - %guarded is the lower part of old guard intrinsic's parent block split by | ||
// the intrinsic call; | ||
// - %deopt is a block containing a sole call to @llvm.experimental.deoptimize | ||
// intrinsic. | ||
// | ||
// Therefore, this branch preserves the property of widenability. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
#ifndef LLVM_TRANSFORMS_SCALAR_MAKEGUARDSEXPLICIT_H | ||
#define LLVM_TRANSFORMS_SCALAR_MAKEGUARDSEXPLICIT_H | ||
|
||
#include "llvm/IR/PassManager.h" | ||
|
||
namespace llvm { | ||
|
||
struct MakeGuardsExplicitPass : public PassInfoMixin<MakeGuardsExplicitPass> { | ||
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); | ||
}; | ||
|
||
} // namespace llvm | ||
|
||
#endif //LLVM_TRANSFORMS_SCALAR_MAKEGUARDSEXPLICIT_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
//===- MakeGuardsExplicit.cpp - Turn guard intrinsics into guard branches -===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This pass lowers the @llvm.experimental.guard intrinsic to the new form of | ||
// guard represented as widenable explicit branch to the deopt block. The | ||
// difference between this pass and LowerGuardIntrinsic is that after this pass | ||
// the guard represented as intrinsic: | ||
// | ||
// call void(i1, ...) @llvm.experimental.guard(i1 %old_cond) [ "deopt"() ] | ||
// | ||
// transforms to a guard represented as widenable explicit branch: | ||
// | ||
// %widenable_cond = call i1 @llvm.experimental.widenable.condition() | ||
// br i1 (%old_cond & %widenable_cond), label %guarded, label %deopt | ||
// | ||
// Here: | ||
// - The semantics of @llvm.experimental.widenable.condition allows to replace | ||
// %widenable_cond with the construction (%widenable_cond & %any_other_cond) | ||
// without loss of correctness; | ||
// - %guarded is the lower part of old guard intrinsic's parent block split by | ||
// the intrinsic call; | ||
// - %deopt is a block containing a sole call to @llvm.experimental.deoptimize | ||
// intrinsic. | ||
// | ||
// Therefore, this branch preserves the property of widenability. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "llvm/Transforms/Scalar/MakeGuardsExplicit.h" | ||
#include "llvm/Analysis/GuardUtils.h" | ||
#include "llvm/IR/InstIterator.h" | ||
#include "llvm/IR/IntrinsicInst.h" | ||
#include "llvm/IR/Intrinsics.h" | ||
#include "llvm/IR/IRBuilder.h" | ||
#include "llvm/Pass.h" | ||
#include "llvm/Transforms/Scalar.h" | ||
#include "llvm/Transforms/Utils/GuardUtils.h" | ||
|
||
using namespace llvm; | ||
|
||
namespace { | ||
struct MakeGuardsExplicitLegacyPass : public FunctionPass { | ||
static char ID; | ||
MakeGuardsExplicitLegacyPass() : FunctionPass(ID) { | ||
initializeMakeGuardsExplicitLegacyPassPass(*PassRegistry::getPassRegistry()); | ||
} | ||
|
||
bool runOnFunction(Function &F) override; | ||
}; | ||
} | ||
|
||
static void turnToExplicitForm(CallInst *Guard, Function *DeoptIntrinsic) { | ||
// Replace the guard with an explicit branch (just like in GuardWidening). | ||
BasicBlock *BB = Guard->getParent(); | ||
makeGuardControlFlowExplicit(DeoptIntrinsic, Guard); | ||
BranchInst *ExplicitGuard = cast<BranchInst>(BB->getTerminator()); | ||
assert(ExplicitGuard->isConditional() && "Must be!"); | ||
|
||
// We want the guard to be expressed as explicit control flow, but still be | ||
// widenable. For that, we add Widenable Condition intrinsic call to the | ||
// guard's condition. | ||
IRBuilder<> B(ExplicitGuard); | ||
auto *WidenableCondition = | ||
B.CreateIntrinsic(Intrinsic::experimental_widenable_condition, | ||
{}, {}, nullptr, "widenable_cond"); | ||
WidenableCondition->setCallingConv(Guard->getCallingConv()); | ||
auto *NewCond = | ||
B.CreateAnd(ExplicitGuard->getCondition(), WidenableCondition); | ||
NewCond->setName("exiplicit_guard_cond"); | ||
ExplicitGuard->setCondition(NewCond); | ||
Guard->eraseFromParent(); | ||
} | ||
|
||
static bool explicifyGuards(Function &F) { | ||
// Check if we can cheaply rule out the possibility of not having any work to | ||
// do. | ||
auto *GuardDecl = F.getParent()->getFunction( | ||
Intrinsic::getName(Intrinsic::experimental_guard)); | ||
if (!GuardDecl || GuardDecl->use_empty()) | ||
return false; | ||
|
||
SmallVector<CallInst *, 8> GuardIntrinsics; | ||
for (auto &I : instructions(F)) | ||
if (isGuard(&I)) | ||
GuardIntrinsics.push_back(cast<CallInst>(&I)); | ||
|
||
if (GuardIntrinsics.empty()) | ||
return false; | ||
|
||
auto *DeoptIntrinsic = Intrinsic::getDeclaration( | ||
F.getParent(), Intrinsic::experimental_deoptimize, {F.getReturnType()}); | ||
DeoptIntrinsic->setCallingConv(GuardDecl->getCallingConv()); | ||
|
||
for (auto *Guard : GuardIntrinsics) | ||
turnToExplicitForm(Guard, DeoptIntrinsic); | ||
|
||
return true; | ||
} | ||
|
||
bool MakeGuardsExplicitLegacyPass::runOnFunction(Function &F) { | ||
return explicifyGuards(F); | ||
} | ||
|
||
char MakeGuardsExplicitLegacyPass::ID = 0; | ||
INITIALIZE_PASS(MakeGuardsExplicitLegacyPass, "make-guards-explicit", | ||
"Lower the guard intrinsic to explicit control flow form", | ||
false, false) | ||
|
||
PreservedAnalyses MakeGuardsExplicitPass::run(Function &F, | ||
FunctionAnalysisManager &) { | ||
if (explicifyGuards(F)) | ||
return PreservedAnalyses::none(); | ||
return PreservedAnalyses::all(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.