Path: blob/main/contrib/llvm-project/llvm/lib/CodeGen/CallBrPrepare.cpp
35233 views
//===-- CallBrPrepare - Prepare callbr for code generation ----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This pass lowers callbrs in LLVM IR in order to to assist SelectionDAG's9// codegen.10//11// In particular, this pass assists in inserting register copies for the output12// values of a callbr along the edges leading to the indirect target blocks.13// Though the output SSA value is defined by the callbr instruction itself in14// the IR representation, the value cannot be copied to the appropriate virtual15// registers prior to jumping to an indirect label, since the jump occurs16// within the user-provided assembly blob.17//18// Instead, those copies must occur separately at the beginning of each19// indirect target. That requires that we create a separate SSA definition in20// each of them (via llvm.callbr.landingpad), and may require splitting21// critical edges so we have a location to place the intrinsic. Finally, we22// remap users of the original callbr output SSA value to instead point to the23// appropriate llvm.callbr.landingpad value.24//25// Ideally, this could be done inside SelectionDAG, or in the26// MachineInstruction representation, without the use of an IR-level intrinsic.27// But, within the current framework, it’s simpler to implement as an IR pass.28// (If support for callbr in GlobalISel is implemented, it’s worth considering29// whether this is still required.)30//31//===----------------------------------------------------------------------===//3233#include "llvm/CodeGen/CallBrPrepare.h"34#include "llvm/ADT/ArrayRef.h"35#include "llvm/ADT/SmallPtrSet.h"36#include "llvm/ADT/SmallVector.h"37#include "llvm/ADT/iterator.h"38#include "llvm/Analysis/CFG.h"39#include "llvm/CodeGen/Passes.h"40#include "llvm/IR/BasicBlock.h"41#include "llvm/IR/Dominators.h"42#include "llvm/IR/Function.h"43#include "llvm/IR/IRBuilder.h"44#include "llvm/IR/Instructions.h"45#include "llvm/IR/IntrinsicInst.h"46#include "llvm/IR/Intrinsics.h"47#include "llvm/InitializePasses.h"48#include "llvm/Pass.h"49#include "llvm/Transforms/Utils/BasicBlockUtils.h"50#include "llvm/Transforms/Utils/SSAUpdater.h"5152using namespace llvm;5354#define DEBUG_TYPE "callbr-prepare"5556static bool SplitCriticalEdges(ArrayRef<CallBrInst *> CBRs, DominatorTree &DT);57static bool InsertIntrinsicCalls(ArrayRef<CallBrInst *> CBRs,58DominatorTree &DT);59static void UpdateSSA(DominatorTree &DT, CallBrInst *CBR, CallInst *Intrinsic,60SSAUpdater &SSAUpdate);61static SmallVector<CallBrInst *, 2> FindCallBrs(Function &Fn);6263namespace {6465class CallBrPrepare : public FunctionPass {66public:67CallBrPrepare() : FunctionPass(ID) {}68void getAnalysisUsage(AnalysisUsage &AU) const override;69bool runOnFunction(Function &Fn) override;70static char ID;71};7273} // end anonymous namespace7475PreservedAnalyses CallBrPreparePass::run(Function &Fn,76FunctionAnalysisManager &FAM) {77bool Changed = false;78SmallVector<CallBrInst *, 2> CBRs = FindCallBrs(Fn);7980if (CBRs.empty())81return PreservedAnalyses::all();8283auto &DT = FAM.getResult<DominatorTreeAnalysis>(Fn);8485Changed |= SplitCriticalEdges(CBRs, DT);86Changed |= InsertIntrinsicCalls(CBRs, DT);8788if (!Changed)89return PreservedAnalyses::all();90PreservedAnalyses PA;91PA.preserve<DominatorTreeAnalysis>();92return PA;93}9495char CallBrPrepare::ID = 0;96INITIALIZE_PASS_BEGIN(CallBrPrepare, "callbrprepare", "Prepare callbr", false,97false)98INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)99INITIALIZE_PASS_END(CallBrPrepare, "callbrprepare", "Prepare callbr", false,100false)101102FunctionPass *llvm::createCallBrPass() { return new CallBrPrepare(); }103104void CallBrPrepare::getAnalysisUsage(AnalysisUsage &AU) const {105AU.addPreserved<DominatorTreeWrapperPass>();106}107108SmallVector<CallBrInst *, 2> FindCallBrs(Function &Fn) {109SmallVector<CallBrInst *, 2> CBRs;110for (BasicBlock &BB : Fn)111if (auto *CBR = dyn_cast<CallBrInst>(BB.getTerminator()))112if (!CBR->getType()->isVoidTy() && !CBR->use_empty())113CBRs.push_back(CBR);114return CBRs;115}116117bool SplitCriticalEdges(ArrayRef<CallBrInst *> CBRs, DominatorTree &DT) {118bool Changed = false;119CriticalEdgeSplittingOptions Options(&DT);120Options.setMergeIdenticalEdges();121122// The indirect destination might be duplicated between another parameter...123// %0 = callbr ... [label %x, label %x]124// ...hence MergeIdenticalEdges and AllowIndentical edges, but we don't need125// to split the default destination if it's duplicated between an indirect126// destination...127// %1 = callbr ... to label %x [label %x]128// ...hence starting at 1 and checking against successor 0 (aka the default129// destination).130for (CallBrInst *CBR : CBRs)131for (unsigned i = 1, e = CBR->getNumSuccessors(); i != e; ++i)132if (CBR->getSuccessor(i) == CBR->getSuccessor(0) ||133isCriticalEdge(CBR, i, /*AllowIdenticalEdges*/ true))134if (SplitKnownCriticalEdge(CBR, i, Options))135Changed = true;136return Changed;137}138139bool InsertIntrinsicCalls(ArrayRef<CallBrInst *> CBRs, DominatorTree &DT) {140bool Changed = false;141SmallPtrSet<const BasicBlock *, 4> Visited;142IRBuilder<> Builder(CBRs[0]->getContext());143for (CallBrInst *CBR : CBRs) {144if (!CBR->getNumIndirectDests())145continue;146147SSAUpdater SSAUpdate;148SSAUpdate.Initialize(CBR->getType(), CBR->getName());149SSAUpdate.AddAvailableValue(CBR->getParent(), CBR);150SSAUpdate.AddAvailableValue(CBR->getDefaultDest(), CBR);151152for (BasicBlock *IndDest : CBR->getIndirectDests()) {153if (!Visited.insert(IndDest).second)154continue;155Builder.SetInsertPoint(&*IndDest->begin());156CallInst *Intrinsic = Builder.CreateIntrinsic(157CBR->getType(), Intrinsic::callbr_landingpad, {CBR});158SSAUpdate.AddAvailableValue(IndDest, Intrinsic);159UpdateSSA(DT, CBR, Intrinsic, SSAUpdate);160Changed = true;161}162}163return Changed;164}165166static bool IsInSameBasicBlock(const Use &U, const BasicBlock *BB) {167const auto *I = dyn_cast<Instruction>(U.getUser());168return I && I->getParent() == BB;169}170171#ifndef NDEBUG172static void PrintDebugDomInfo(const DominatorTree &DT, const Use &U,173const BasicBlock *BB, bool IsDefaultDest) {174if (!isa<Instruction>(U.getUser()))175return;176LLVM_DEBUG(dbgs() << "Use: " << *U.getUser() << ", in block "177<< cast<Instruction>(U.getUser())->getParent()->getName()178<< ", is " << (DT.dominates(BB, U) ? "" : "NOT ")179<< "dominated by " << BB->getName() << " ("180<< (IsDefaultDest ? "in" : "") << "direct)\n");181}182#endif183184void UpdateSSA(DominatorTree &DT, CallBrInst *CBR, CallInst *Intrinsic,185SSAUpdater &SSAUpdate) {186187SmallPtrSet<Use *, 4> Visited;188BasicBlock *DefaultDest = CBR->getDefaultDest();189BasicBlock *LandingPad = Intrinsic->getParent();190191SmallVector<Use *, 4> Uses(make_pointer_range(CBR->uses()));192for (Use *U : Uses) {193if (!Visited.insert(U).second)194continue;195196#ifndef NDEBUG197PrintDebugDomInfo(DT, *U, LandingPad, /*IsDefaultDest*/ false);198PrintDebugDomInfo(DT, *U, DefaultDest, /*IsDefaultDest*/ true);199#endif200201// Don't rewrite the use in the newly inserted intrinsic.202if (const auto *II = dyn_cast<IntrinsicInst>(U->getUser()))203if (II->getIntrinsicID() == Intrinsic::callbr_landingpad)204continue;205206// If the Use is in the same BasicBlock as the Intrinsic call, replace207// the Use with the value of the Intrinsic call.208if (IsInSameBasicBlock(*U, LandingPad)) {209U->set(Intrinsic);210continue;211}212213// If the Use is dominated by the default dest, do not touch it.214if (DT.dominates(DefaultDest, *U))215continue;216217SSAUpdate.RewriteUse(*U);218}219}220221bool CallBrPrepare::runOnFunction(Function &Fn) {222bool Changed = false;223SmallVector<CallBrInst *, 2> CBRs = FindCallBrs(Fn);224225if (CBRs.empty())226return Changed;227228// It's highly likely that most programs do not contain CallBrInsts. Follow a229// similar pattern from SafeStackLegacyPass::runOnFunction to reuse previous230// domtree analysis if available, otherwise compute it lazily. This avoids231// forcing Dominator Tree Construction at -O0 for programs that likely do not232// contain CallBrInsts. It does pessimize programs with callbr at higher233// optimization levels, as the DominatorTree created here is not reused by234// subsequent passes.235DominatorTree *DT;236std::optional<DominatorTree> LazilyComputedDomTree;237if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>())238DT = &DTWP->getDomTree();239else {240LazilyComputedDomTree.emplace(Fn);241DT = &*LazilyComputedDomTree;242}243244if (SplitCriticalEdges(CBRs, *DT))245Changed = true;246247if (InsertIntrinsicCalls(CBRs, *DT))248Changed = true;249250return Changed;251}252253254