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