1*e8d8bef9SDimitry Andric //===---------------- BPFAdjustOpt.cpp - Adjust Optimization --------------===// 2*e8d8bef9SDimitry Andric // 3*e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*e8d8bef9SDimitry Andric // 7*e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8*e8d8bef9SDimitry Andric // 9*e8d8bef9SDimitry Andric // Adjust optimization to make the code more kernel verifier friendly. 10*e8d8bef9SDimitry Andric // 11*e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 12*e8d8bef9SDimitry Andric 13*e8d8bef9SDimitry Andric #include "BPF.h" 14*e8d8bef9SDimitry Andric #include "BPFCORE.h" 15*e8d8bef9SDimitry Andric #include "BPFTargetMachine.h" 16*e8d8bef9SDimitry Andric #include "llvm/IR/Instruction.h" 17*e8d8bef9SDimitry Andric #include "llvm/IR/Instructions.h" 18*e8d8bef9SDimitry Andric #include "llvm/IR/Module.h" 19*e8d8bef9SDimitry Andric #include "llvm/IR/Type.h" 20*e8d8bef9SDimitry Andric #include "llvm/IR/User.h" 21*e8d8bef9SDimitry Andric #include "llvm/IR/Value.h" 22*e8d8bef9SDimitry Andric #include "llvm/Pass.h" 23*e8d8bef9SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h" 24*e8d8bef9SDimitry Andric 25*e8d8bef9SDimitry Andric #define DEBUG_TYPE "bpf-adjust-opt" 26*e8d8bef9SDimitry Andric 27*e8d8bef9SDimitry Andric using namespace llvm; 28*e8d8bef9SDimitry Andric 29*e8d8bef9SDimitry Andric static cl::opt<bool> 30*e8d8bef9SDimitry Andric DisableBPFserializeICMP("bpf-disable-serialize-icmp", cl::Hidden, 31*e8d8bef9SDimitry Andric cl::desc("BPF: Disable Serializing ICMP insns."), 32*e8d8bef9SDimitry Andric cl::init(false)); 33*e8d8bef9SDimitry Andric 34*e8d8bef9SDimitry Andric static cl::opt<bool> DisableBPFavoidSpeculation( 35*e8d8bef9SDimitry Andric "bpf-disable-avoid-speculation", cl::Hidden, 36*e8d8bef9SDimitry Andric cl::desc("BPF: Disable Avoiding Speculative Code Motion."), 37*e8d8bef9SDimitry Andric cl::init(false)); 38*e8d8bef9SDimitry Andric 39*e8d8bef9SDimitry Andric namespace { 40*e8d8bef9SDimitry Andric 41*e8d8bef9SDimitry Andric class BPFAdjustOpt final : public ModulePass { 42*e8d8bef9SDimitry Andric public: 43*e8d8bef9SDimitry Andric static char ID; 44*e8d8bef9SDimitry Andric 45*e8d8bef9SDimitry Andric BPFAdjustOpt() : ModulePass(ID) {} 46*e8d8bef9SDimitry Andric bool runOnModule(Module &M) override; 47*e8d8bef9SDimitry Andric }; 48*e8d8bef9SDimitry Andric 49*e8d8bef9SDimitry Andric class BPFAdjustOptImpl { 50*e8d8bef9SDimitry Andric struct PassThroughInfo { 51*e8d8bef9SDimitry Andric Instruction *Input; 52*e8d8bef9SDimitry Andric Instruction *UsedInst; 53*e8d8bef9SDimitry Andric uint32_t OpIdx; 54*e8d8bef9SDimitry Andric PassThroughInfo(Instruction *I, Instruction *U, uint32_t Idx) 55*e8d8bef9SDimitry Andric : Input(I), UsedInst(U), OpIdx(Idx) {} 56*e8d8bef9SDimitry Andric }; 57*e8d8bef9SDimitry Andric 58*e8d8bef9SDimitry Andric public: 59*e8d8bef9SDimitry Andric BPFAdjustOptImpl(Module *M) : M(M) {} 60*e8d8bef9SDimitry Andric 61*e8d8bef9SDimitry Andric bool run(); 62*e8d8bef9SDimitry Andric 63*e8d8bef9SDimitry Andric private: 64*e8d8bef9SDimitry Andric Module *M; 65*e8d8bef9SDimitry Andric SmallVector<PassThroughInfo, 16> PassThroughs; 66*e8d8bef9SDimitry Andric 67*e8d8bef9SDimitry Andric void adjustBasicBlock(BasicBlock &BB); 68*e8d8bef9SDimitry Andric bool serializeICMPCrossBB(BasicBlock &BB); 69*e8d8bef9SDimitry Andric void adjustInst(Instruction &I); 70*e8d8bef9SDimitry Andric bool serializeICMPInBB(Instruction &I); 71*e8d8bef9SDimitry Andric bool avoidSpeculation(Instruction &I); 72*e8d8bef9SDimitry Andric bool insertPassThrough(); 73*e8d8bef9SDimitry Andric }; 74*e8d8bef9SDimitry Andric 75*e8d8bef9SDimitry Andric } // End anonymous namespace 76*e8d8bef9SDimitry Andric 77*e8d8bef9SDimitry Andric char BPFAdjustOpt::ID = 0; 78*e8d8bef9SDimitry Andric INITIALIZE_PASS(BPFAdjustOpt, "bpf-adjust-opt", "BPF Adjust Optimization", 79*e8d8bef9SDimitry Andric false, false) 80*e8d8bef9SDimitry Andric 81*e8d8bef9SDimitry Andric ModulePass *llvm::createBPFAdjustOpt() { return new BPFAdjustOpt(); } 82*e8d8bef9SDimitry Andric 83*e8d8bef9SDimitry Andric bool BPFAdjustOpt::runOnModule(Module &M) { return BPFAdjustOptImpl(&M).run(); } 84*e8d8bef9SDimitry Andric 85*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::run() { 86*e8d8bef9SDimitry Andric for (Function &F : *M) 87*e8d8bef9SDimitry Andric for (auto &BB : F) { 88*e8d8bef9SDimitry Andric adjustBasicBlock(BB); 89*e8d8bef9SDimitry Andric for (auto &I : BB) 90*e8d8bef9SDimitry Andric adjustInst(I); 91*e8d8bef9SDimitry Andric } 92*e8d8bef9SDimitry Andric 93*e8d8bef9SDimitry Andric return insertPassThrough(); 94*e8d8bef9SDimitry Andric } 95*e8d8bef9SDimitry Andric 96*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::insertPassThrough() { 97*e8d8bef9SDimitry Andric for (auto &Info : PassThroughs) { 98*e8d8bef9SDimitry Andric auto *CI = BPFCoreSharedInfo::insertPassThrough( 99*e8d8bef9SDimitry Andric M, Info.UsedInst->getParent(), Info.Input, Info.UsedInst); 100*e8d8bef9SDimitry Andric Info.UsedInst->setOperand(Info.OpIdx, CI); 101*e8d8bef9SDimitry Andric } 102*e8d8bef9SDimitry Andric 103*e8d8bef9SDimitry Andric return !PassThroughs.empty(); 104*e8d8bef9SDimitry Andric } 105*e8d8bef9SDimitry Andric 106*e8d8bef9SDimitry Andric // To avoid combining conditionals in the same basic block by 107*e8d8bef9SDimitry Andric // instrcombine optimization. 108*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::serializeICMPInBB(Instruction &I) { 109*e8d8bef9SDimitry Andric // For: 110*e8d8bef9SDimitry Andric // comp1 = icmp <opcode> ...; 111*e8d8bef9SDimitry Andric // comp2 = icmp <opcode> ...; 112*e8d8bef9SDimitry Andric // ... or comp1 comp2 ... 113*e8d8bef9SDimitry Andric // changed to: 114*e8d8bef9SDimitry Andric // comp1 = icmp <opcode> ...; 115*e8d8bef9SDimitry Andric // comp2 = icmp <opcode> ...; 116*e8d8bef9SDimitry Andric // new_comp1 = __builtin_bpf_passthrough(seq_num, comp1) 117*e8d8bef9SDimitry Andric // ... or new_comp1 comp2 ... 118*e8d8bef9SDimitry Andric if (I.getOpcode() != Instruction::Or) 119*e8d8bef9SDimitry Andric return false; 120*e8d8bef9SDimitry Andric auto *Icmp1 = dyn_cast<ICmpInst>(I.getOperand(0)); 121*e8d8bef9SDimitry Andric if (!Icmp1) 122*e8d8bef9SDimitry Andric return false; 123*e8d8bef9SDimitry Andric auto *Icmp2 = dyn_cast<ICmpInst>(I.getOperand(1)); 124*e8d8bef9SDimitry Andric if (!Icmp2) 125*e8d8bef9SDimitry Andric return false; 126*e8d8bef9SDimitry Andric 127*e8d8bef9SDimitry Andric Value *Icmp1Op0 = Icmp1->getOperand(0); 128*e8d8bef9SDimitry Andric Value *Icmp2Op0 = Icmp2->getOperand(0); 129*e8d8bef9SDimitry Andric if (Icmp1Op0 != Icmp2Op0) 130*e8d8bef9SDimitry Andric return false; 131*e8d8bef9SDimitry Andric 132*e8d8bef9SDimitry Andric // Now we got two icmp instructions which feed into 133*e8d8bef9SDimitry Andric // an "or" instruction. 134*e8d8bef9SDimitry Andric PassThroughInfo Info(Icmp1, &I, 0); 135*e8d8bef9SDimitry Andric PassThroughs.push_back(Info); 136*e8d8bef9SDimitry Andric return true; 137*e8d8bef9SDimitry Andric } 138*e8d8bef9SDimitry Andric 139*e8d8bef9SDimitry Andric // To avoid combining conditionals in the same basic block by 140*e8d8bef9SDimitry Andric // instrcombine optimization. 141*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::serializeICMPCrossBB(BasicBlock &BB) { 142*e8d8bef9SDimitry Andric // For: 143*e8d8bef9SDimitry Andric // B1: 144*e8d8bef9SDimitry Andric // comp1 = icmp <opcode> ...; 145*e8d8bef9SDimitry Andric // if (comp1) goto B2 else B3; 146*e8d8bef9SDimitry Andric // B2: 147*e8d8bef9SDimitry Andric // comp2 = icmp <opcode> ...; 148*e8d8bef9SDimitry Andric // if (comp2) goto B4 else B5; 149*e8d8bef9SDimitry Andric // B4: 150*e8d8bef9SDimitry Andric // ... 151*e8d8bef9SDimitry Andric // changed to: 152*e8d8bef9SDimitry Andric // B1: 153*e8d8bef9SDimitry Andric // comp1 = icmp <opcode> ...; 154*e8d8bef9SDimitry Andric // comp1 = __builtin_bpf_passthrough(seq_num, comp1); 155*e8d8bef9SDimitry Andric // if (comp1) goto B2 else B3; 156*e8d8bef9SDimitry Andric // B2: 157*e8d8bef9SDimitry Andric // comp2 = icmp <opcode> ...; 158*e8d8bef9SDimitry Andric // if (comp2) goto B4 else B5; 159*e8d8bef9SDimitry Andric // B4: 160*e8d8bef9SDimitry Andric // ... 161*e8d8bef9SDimitry Andric 162*e8d8bef9SDimitry Andric // Check basic predecessors, if two of them (say B1, B2) are using 163*e8d8bef9SDimitry Andric // icmp instructions to generate conditions and one is the predesessor 164*e8d8bef9SDimitry Andric // of another (e.g., B1 is the predecessor of B2). Add a passthrough 165*e8d8bef9SDimitry Andric // barrier after icmp inst of block B1. 166*e8d8bef9SDimitry Andric BasicBlock *B2 = BB.getSinglePredecessor(); 167*e8d8bef9SDimitry Andric if (!B2) 168*e8d8bef9SDimitry Andric return false; 169*e8d8bef9SDimitry Andric 170*e8d8bef9SDimitry Andric BasicBlock *B1 = B2->getSinglePredecessor(); 171*e8d8bef9SDimitry Andric if (!B1) 172*e8d8bef9SDimitry Andric return false; 173*e8d8bef9SDimitry Andric 174*e8d8bef9SDimitry Andric Instruction *TI = B2->getTerminator(); 175*e8d8bef9SDimitry Andric auto *BI = dyn_cast<BranchInst>(TI); 176*e8d8bef9SDimitry Andric if (!BI || !BI->isConditional()) 177*e8d8bef9SDimitry Andric return false; 178*e8d8bef9SDimitry Andric auto *Cond = dyn_cast<ICmpInst>(BI->getCondition()); 179*e8d8bef9SDimitry Andric if (!Cond || B2->getFirstNonPHI() != Cond) 180*e8d8bef9SDimitry Andric return false; 181*e8d8bef9SDimitry Andric Value *B2Op0 = Cond->getOperand(0); 182*e8d8bef9SDimitry Andric auto Cond2Op = Cond->getPredicate(); 183*e8d8bef9SDimitry Andric 184*e8d8bef9SDimitry Andric TI = B1->getTerminator(); 185*e8d8bef9SDimitry Andric BI = dyn_cast<BranchInst>(TI); 186*e8d8bef9SDimitry Andric if (!BI || !BI->isConditional()) 187*e8d8bef9SDimitry Andric return false; 188*e8d8bef9SDimitry Andric Cond = dyn_cast<ICmpInst>(BI->getCondition()); 189*e8d8bef9SDimitry Andric if (!Cond) 190*e8d8bef9SDimitry Andric return false; 191*e8d8bef9SDimitry Andric Value *B1Op0 = Cond->getOperand(0); 192*e8d8bef9SDimitry Andric auto Cond1Op = Cond->getPredicate(); 193*e8d8bef9SDimitry Andric 194*e8d8bef9SDimitry Andric if (B1Op0 != B2Op0) 195*e8d8bef9SDimitry Andric return false; 196*e8d8bef9SDimitry Andric 197*e8d8bef9SDimitry Andric if (Cond1Op == ICmpInst::ICMP_SGT || Cond1Op == ICmpInst::ICMP_SGE) { 198*e8d8bef9SDimitry Andric if (Cond2Op != ICmpInst::ICMP_SLT && Cond1Op != ICmpInst::ICMP_SLE) 199*e8d8bef9SDimitry Andric return false; 200*e8d8bef9SDimitry Andric } else if (Cond1Op == ICmpInst::ICMP_SLT || Cond1Op == ICmpInst::ICMP_SLE) { 201*e8d8bef9SDimitry Andric if (Cond2Op != ICmpInst::ICMP_SGT && Cond1Op != ICmpInst::ICMP_SGE) 202*e8d8bef9SDimitry Andric return false; 203*e8d8bef9SDimitry Andric } else { 204*e8d8bef9SDimitry Andric return false; 205*e8d8bef9SDimitry Andric } 206*e8d8bef9SDimitry Andric 207*e8d8bef9SDimitry Andric PassThroughInfo Info(Cond, BI, 0); 208*e8d8bef9SDimitry Andric PassThroughs.push_back(Info); 209*e8d8bef9SDimitry Andric 210*e8d8bef9SDimitry Andric return true; 211*e8d8bef9SDimitry Andric } 212*e8d8bef9SDimitry Andric 213*e8d8bef9SDimitry Andric // To avoid speculative hoisting certain computations out of 214*e8d8bef9SDimitry Andric // a basic block. 215*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::avoidSpeculation(Instruction &I) { 216*e8d8bef9SDimitry Andric if (auto *LdInst = dyn_cast<LoadInst>(&I)) { 217*e8d8bef9SDimitry Andric if (auto *GV = dyn_cast<GlobalVariable>(LdInst->getOperand(0))) { 218*e8d8bef9SDimitry Andric if (GV->hasAttribute(BPFCoreSharedInfo::AmaAttr) || 219*e8d8bef9SDimitry Andric GV->hasAttribute(BPFCoreSharedInfo::TypeIdAttr)) 220*e8d8bef9SDimitry Andric return false; 221*e8d8bef9SDimitry Andric } 222*e8d8bef9SDimitry Andric } 223*e8d8bef9SDimitry Andric 224*e8d8bef9SDimitry Andric if (!isa<LoadInst>(&I) && !isa<CallInst>(&I)) 225*e8d8bef9SDimitry Andric return false; 226*e8d8bef9SDimitry Andric 227*e8d8bef9SDimitry Andric // For: 228*e8d8bef9SDimitry Andric // B1: 229*e8d8bef9SDimitry Andric // var = ... 230*e8d8bef9SDimitry Andric // ... 231*e8d8bef9SDimitry Andric // /* icmp may not be in the same block as var = ... */ 232*e8d8bef9SDimitry Andric // comp1 = icmp <opcode> var, <const>; 233*e8d8bef9SDimitry Andric // if (comp1) goto B2 else B3; 234*e8d8bef9SDimitry Andric // B2: 235*e8d8bef9SDimitry Andric // ... var ... 236*e8d8bef9SDimitry Andric // change to: 237*e8d8bef9SDimitry Andric // B1: 238*e8d8bef9SDimitry Andric // var = ... 239*e8d8bef9SDimitry Andric // ... 240*e8d8bef9SDimitry Andric // /* icmp may not be in the same block as var = ... */ 241*e8d8bef9SDimitry Andric // comp1 = icmp <opcode> var, <const>; 242*e8d8bef9SDimitry Andric // if (comp1) goto B2 else B3; 243*e8d8bef9SDimitry Andric // B2: 244*e8d8bef9SDimitry Andric // var = __builtin_bpf_passthrough(seq_num, var); 245*e8d8bef9SDimitry Andric // ... var ... 246*e8d8bef9SDimitry Andric bool isCandidate = false; 247*e8d8bef9SDimitry Andric SmallVector<PassThroughInfo, 4> Candidates; 248*e8d8bef9SDimitry Andric for (User *U : I.users()) { 249*e8d8bef9SDimitry Andric Instruction *Inst = dyn_cast<Instruction>(U); 250*e8d8bef9SDimitry Andric if (!Inst) 251*e8d8bef9SDimitry Andric continue; 252*e8d8bef9SDimitry Andric 253*e8d8bef9SDimitry Andric // May cover a little bit more than the 254*e8d8bef9SDimitry Andric // above pattern. 255*e8d8bef9SDimitry Andric if (auto *Icmp1 = dyn_cast<ICmpInst>(Inst)) { 256*e8d8bef9SDimitry Andric Value *Icmp1Op1 = Icmp1->getOperand(1); 257*e8d8bef9SDimitry Andric if (!isa<Constant>(Icmp1Op1)) 258*e8d8bef9SDimitry Andric return false; 259*e8d8bef9SDimitry Andric isCandidate = true; 260*e8d8bef9SDimitry Andric continue; 261*e8d8bef9SDimitry Andric } 262*e8d8bef9SDimitry Andric 263*e8d8bef9SDimitry Andric // Ignore the use in the same basic block as the definition. 264*e8d8bef9SDimitry Andric if (Inst->getParent() == I.getParent()) 265*e8d8bef9SDimitry Andric continue; 266*e8d8bef9SDimitry Andric 267*e8d8bef9SDimitry Andric // use in a different basic block, If there is a call or 268*e8d8bef9SDimitry Andric // load/store insn before this instruction in this basic 269*e8d8bef9SDimitry Andric // block. Most likely it cannot be hoisted out. Skip it. 270*e8d8bef9SDimitry Andric for (auto &I2 : *Inst->getParent()) { 271*e8d8bef9SDimitry Andric if (dyn_cast<CallInst>(&I2)) 272*e8d8bef9SDimitry Andric return false; 273*e8d8bef9SDimitry Andric if (dyn_cast<LoadInst>(&I2) || dyn_cast<StoreInst>(&I2)) 274*e8d8bef9SDimitry Andric return false; 275*e8d8bef9SDimitry Andric if (&I2 == Inst) 276*e8d8bef9SDimitry Andric break; 277*e8d8bef9SDimitry Andric } 278*e8d8bef9SDimitry Andric 279*e8d8bef9SDimitry Andric // It should be used in a GEP or a simple arithmetic like 280*e8d8bef9SDimitry Andric // ZEXT/SEXT which is used for GEP. 281*e8d8bef9SDimitry Andric if (Inst->getOpcode() == Instruction::ZExt || 282*e8d8bef9SDimitry Andric Inst->getOpcode() == Instruction::SExt) { 283*e8d8bef9SDimitry Andric PassThroughInfo Info(&I, Inst, 0); 284*e8d8bef9SDimitry Andric Candidates.push_back(Info); 285*e8d8bef9SDimitry Andric } else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) { 286*e8d8bef9SDimitry Andric // traverse GEP inst to find Use operand index 287*e8d8bef9SDimitry Andric unsigned i, e; 288*e8d8bef9SDimitry Andric for (i = 1, e = GI->getNumOperands(); i != e; ++i) { 289*e8d8bef9SDimitry Andric Value *V = GI->getOperand(i); 290*e8d8bef9SDimitry Andric if (V == &I) 291*e8d8bef9SDimitry Andric break; 292*e8d8bef9SDimitry Andric } 293*e8d8bef9SDimitry Andric if (i == e) 294*e8d8bef9SDimitry Andric continue; 295*e8d8bef9SDimitry Andric 296*e8d8bef9SDimitry Andric PassThroughInfo Info(&I, GI, i); 297*e8d8bef9SDimitry Andric Candidates.push_back(Info); 298*e8d8bef9SDimitry Andric } 299*e8d8bef9SDimitry Andric } 300*e8d8bef9SDimitry Andric 301*e8d8bef9SDimitry Andric if (!isCandidate || Candidates.empty()) 302*e8d8bef9SDimitry Andric return false; 303*e8d8bef9SDimitry Andric 304*e8d8bef9SDimitry Andric llvm::append_range(PassThroughs, Candidates); 305*e8d8bef9SDimitry Andric return true; 306*e8d8bef9SDimitry Andric } 307*e8d8bef9SDimitry Andric 308*e8d8bef9SDimitry Andric void BPFAdjustOptImpl::adjustBasicBlock(BasicBlock &BB) { 309*e8d8bef9SDimitry Andric if (!DisableBPFserializeICMP && serializeICMPCrossBB(BB)) 310*e8d8bef9SDimitry Andric return; 311*e8d8bef9SDimitry Andric } 312*e8d8bef9SDimitry Andric 313*e8d8bef9SDimitry Andric void BPFAdjustOptImpl::adjustInst(Instruction &I) { 314*e8d8bef9SDimitry Andric if (!DisableBPFserializeICMP && serializeICMPInBB(I)) 315*e8d8bef9SDimitry Andric return; 316*e8d8bef9SDimitry Andric if (!DisableBPFavoidSpeculation && avoidSpeculation(I)) 317*e8d8bef9SDimitry Andric return; 318*e8d8bef9SDimitry Andric } 319*e8d8bef9SDimitry Andric 320*e8d8bef9SDimitry Andric PreservedAnalyses BPFAdjustOptPass::run(Module &M, ModuleAnalysisManager &AM) { 321*e8d8bef9SDimitry Andric return BPFAdjustOptImpl(&M).run() ? PreservedAnalyses::none() 322*e8d8bef9SDimitry Andric : PreservedAnalyses::all(); 323*e8d8bef9SDimitry Andric } 324