xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/NVPTX/NVPTXLowerUnreachable.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
106c3fb27SDimitry Andric //===-- NVPTXLowerUnreachable.cpp - Lower unreachables to exit =====--===//
206c3fb27SDimitry Andric //
306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric //
706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
806c3fb27SDimitry Andric //
906c3fb27SDimitry Andric // PTX does not have a notion of `unreachable`, which results in emitted basic
1006c3fb27SDimitry Andric // blocks having an edge to the next block:
1106c3fb27SDimitry Andric //
1206c3fb27SDimitry Andric //   block1:
1306c3fb27SDimitry Andric //     call @does_not_return();
1406c3fb27SDimitry Andric //     // unreachable
1506c3fb27SDimitry Andric //   block2:
1606c3fb27SDimitry Andric //     // ptxas will create a CFG edge from block1 to block2
1706c3fb27SDimitry Andric //
1806c3fb27SDimitry Andric // This may result in significant changes to the control flow graph, e.g., when
1906c3fb27SDimitry Andric // LLVM moves unreachable blocks to the end of the function. That's a problem
2006c3fb27SDimitry Andric // in the context of divergent control flow, as `ptxas` uses the CFG to
2106c3fb27SDimitry Andric // determine divergent regions, and some intructions may not be executed
2206c3fb27SDimitry Andric // divergently.
2306c3fb27SDimitry Andric //
2406c3fb27SDimitry Andric // For example, `bar.sync` is not allowed to be executed divergently on Pascal
2506c3fb27SDimitry Andric // or earlier. If we start with the following:
2606c3fb27SDimitry Andric //
2706c3fb27SDimitry Andric //   entry:
2806c3fb27SDimitry Andric //     // start of divergent region
2906c3fb27SDimitry Andric //     @%p0 bra cont;
3006c3fb27SDimitry Andric //     @%p1 bra unlikely;
3106c3fb27SDimitry Andric //     ...
3206c3fb27SDimitry Andric //     bra.uni cont;
3306c3fb27SDimitry Andric //   unlikely:
3406c3fb27SDimitry Andric //     ...
3506c3fb27SDimitry Andric //     // unreachable
3606c3fb27SDimitry Andric //   cont:
3706c3fb27SDimitry Andric //     // end of divergent region
3806c3fb27SDimitry Andric //     bar.sync 0;
3906c3fb27SDimitry Andric //     bra.uni exit;
4006c3fb27SDimitry Andric //   exit:
4106c3fb27SDimitry Andric //     ret;
4206c3fb27SDimitry Andric //
4306c3fb27SDimitry Andric // it is transformed by the branch-folder and block-placement passes to:
4406c3fb27SDimitry Andric //
4506c3fb27SDimitry Andric //   entry:
4606c3fb27SDimitry Andric //     // start of divergent region
4706c3fb27SDimitry Andric //     @%p0 bra cont;
4806c3fb27SDimitry Andric //     @%p1 bra unlikely;
4906c3fb27SDimitry Andric //     ...
5006c3fb27SDimitry Andric //     bra.uni cont;
5106c3fb27SDimitry Andric //   cont:
5206c3fb27SDimitry Andric //     bar.sync 0;
5306c3fb27SDimitry Andric //     bra.uni exit;
5406c3fb27SDimitry Andric //   unlikely:
5506c3fb27SDimitry Andric //     ...
5606c3fb27SDimitry Andric //     // unreachable
5706c3fb27SDimitry Andric //   exit:
5806c3fb27SDimitry Andric //     // end of divergent region
5906c3fb27SDimitry Andric //     ret;
6006c3fb27SDimitry Andric //
6106c3fb27SDimitry Andric // After moving the `unlikely` block to the end of the function, it has an edge
6206c3fb27SDimitry Andric // to the `exit` block, which widens the divergent region and makes the
6306c3fb27SDimitry Andric // `bar.sync` instruction happen divergently.
6406c3fb27SDimitry Andric //
6506c3fb27SDimitry Andric // To work around this, we add an `exit` instruction before every `unreachable`,
665f757f3fSDimitry Andric // as `ptxas` understands that exit terminates the CFG. We do only do this if
675f757f3fSDimitry Andric // `unreachable` is not lowered to `trap`, which has the same effect (although
685f757f3fSDimitry Andric // with current versions of `ptxas` only because it is emited as `trap; exit;`).
6906c3fb27SDimitry Andric //
7006c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
7106c3fb27SDimitry Andric 
7206c3fb27SDimitry Andric #include "NVPTX.h"
7306c3fb27SDimitry Andric #include "llvm/IR/Function.h"
7406c3fb27SDimitry Andric #include "llvm/IR/InlineAsm.h"
7506c3fb27SDimitry Andric #include "llvm/IR/Instructions.h"
7606c3fb27SDimitry Andric #include "llvm/IR/Type.h"
7706c3fb27SDimitry Andric #include "llvm/Pass.h"
7806c3fb27SDimitry Andric 
7906c3fb27SDimitry Andric using namespace llvm;
8006c3fb27SDimitry Andric 
8106c3fb27SDimitry Andric namespace llvm {
8206c3fb27SDimitry Andric void initializeNVPTXLowerUnreachablePass(PassRegistry &);
8306c3fb27SDimitry Andric }
8406c3fb27SDimitry Andric 
8506c3fb27SDimitry Andric namespace {
8606c3fb27SDimitry Andric class NVPTXLowerUnreachable : public FunctionPass {
875f757f3fSDimitry Andric   StringRef getPassName() const override;
8806c3fb27SDimitry Andric   bool runOnFunction(Function &F) override;
895f757f3fSDimitry Andric   bool isLoweredToTrap(const UnreachableInst &I) const;
9006c3fb27SDimitry Andric 
9106c3fb27SDimitry Andric public:
9206c3fb27SDimitry Andric   static char ID; // Pass identification, replacement for typeid
935f757f3fSDimitry Andric   NVPTXLowerUnreachable(bool TrapUnreachable, bool NoTrapAfterNoreturn)
945f757f3fSDimitry Andric       : FunctionPass(ID), TrapUnreachable(TrapUnreachable),
955f757f3fSDimitry Andric         NoTrapAfterNoreturn(NoTrapAfterNoreturn) {}
965f757f3fSDimitry Andric 
975f757f3fSDimitry Andric private:
985f757f3fSDimitry Andric   bool TrapUnreachable;
995f757f3fSDimitry Andric   bool NoTrapAfterNoreturn;
10006c3fb27SDimitry Andric };
10106c3fb27SDimitry Andric } // namespace
10206c3fb27SDimitry Andric 
10306c3fb27SDimitry Andric char NVPTXLowerUnreachable::ID = 1;
10406c3fb27SDimitry Andric 
10506c3fb27SDimitry Andric INITIALIZE_PASS(NVPTXLowerUnreachable, "nvptx-lower-unreachable",
10606c3fb27SDimitry Andric                 "Lower Unreachable", false, false)
10706c3fb27SDimitry Andric 
1085f757f3fSDimitry Andric StringRef NVPTXLowerUnreachable::getPassName() const {
1095f757f3fSDimitry Andric   return "add an exit instruction before every unreachable";
1105f757f3fSDimitry Andric }
1115f757f3fSDimitry Andric 
1125f757f3fSDimitry Andric // =============================================================================
1135f757f3fSDimitry Andric // Returns whether a `trap` intrinsic should be emitted before I.
1145f757f3fSDimitry Andric //
1155f757f3fSDimitry Andric // This is a copy of the logic in SelectionDAGBuilder::visitUnreachable().
1165f757f3fSDimitry Andric // =============================================================================
1175f757f3fSDimitry Andric bool NVPTXLowerUnreachable::isLoweredToTrap(const UnreachableInst &I) const {
1185f757f3fSDimitry Andric   if (!TrapUnreachable)
1195f757f3fSDimitry Andric     return false;
1205f757f3fSDimitry Andric   if (!NoTrapAfterNoreturn)
1215f757f3fSDimitry Andric     return true;
1225f757f3fSDimitry Andric   const CallInst *Call = dyn_cast_or_null<CallInst>(I.getPrevNode());
1235f757f3fSDimitry Andric   return Call && Call->doesNotReturn();
1245f757f3fSDimitry Andric }
1255f757f3fSDimitry Andric 
12606c3fb27SDimitry Andric // =============================================================================
12706c3fb27SDimitry Andric // Main function for this pass.
12806c3fb27SDimitry Andric // =============================================================================
12906c3fb27SDimitry Andric bool NVPTXLowerUnreachable::runOnFunction(Function &F) {
13006c3fb27SDimitry Andric   if (skipFunction(F))
13106c3fb27SDimitry Andric     return false;
1325f757f3fSDimitry Andric   // Early out iff isLoweredToTrap() always returns true.
1335f757f3fSDimitry Andric   if (TrapUnreachable && !NoTrapAfterNoreturn)
1345f757f3fSDimitry Andric     return false;
13506c3fb27SDimitry Andric 
13606c3fb27SDimitry Andric   LLVMContext &C = F.getContext();
13706c3fb27SDimitry Andric   FunctionType *ExitFTy = FunctionType::get(Type::getVoidTy(C), false);
13806c3fb27SDimitry Andric   InlineAsm *Exit = InlineAsm::get(ExitFTy, "exit;", "", true);
13906c3fb27SDimitry Andric 
14006c3fb27SDimitry Andric   bool Changed = false;
14106c3fb27SDimitry Andric   for (auto &BB : F)
14206c3fb27SDimitry Andric     for (auto &I : BB) {
14306c3fb27SDimitry Andric       if (auto unreachableInst = dyn_cast<UnreachableInst>(&I)) {
1445f757f3fSDimitry Andric         if (isLoweredToTrap(*unreachableInst))
1455f757f3fSDimitry Andric           continue; // trap is emitted as `trap; exit;`.
146*0fca6ea1SDimitry Andric         CallInst::Create(ExitFTy, Exit, "", unreachableInst->getIterator());
1475f757f3fSDimitry Andric         Changed = true;
14806c3fb27SDimitry Andric       }
14906c3fb27SDimitry Andric     }
15006c3fb27SDimitry Andric   return Changed;
15106c3fb27SDimitry Andric }
15206c3fb27SDimitry Andric 
1535f757f3fSDimitry Andric FunctionPass *llvm::createNVPTXLowerUnreachablePass(bool TrapUnreachable,
1545f757f3fSDimitry Andric                                                     bool NoTrapAfterNoreturn) {
1555f757f3fSDimitry Andric   return new NVPTXLowerUnreachable(TrapUnreachable, NoTrapAfterNoreturn);
15606c3fb27SDimitry Andric }
157