Path: blob/main/contrib/llvm-project/llvm/lib/Target/BPF/BPFAdjustOpt.cpp
35269 views
//===---------------- BPFAdjustOpt.cpp - Adjust Optimization --------------===//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// Adjust optimization to make the code more kernel verifier friendly.9//10//===----------------------------------------------------------------------===//1112#include "BPF.h"13#include "BPFCORE.h"14#include "BPFTargetMachine.h"15#include "llvm/IR/Instruction.h"16#include "llvm/IR/Instructions.h"17#include "llvm/IR/IntrinsicsBPF.h"18#include "llvm/IR/Module.h"19#include "llvm/IR/PatternMatch.h"20#include "llvm/IR/Type.h"21#include "llvm/IR/User.h"22#include "llvm/IR/Value.h"23#include "llvm/Pass.h"24#include "llvm/Transforms/Utils/BasicBlockUtils.h"2526#define DEBUG_TYPE "bpf-adjust-opt"2728using namespace llvm;29using namespace llvm::PatternMatch;3031static cl::opt<bool>32DisableBPFserializeICMP("bpf-disable-serialize-icmp", cl::Hidden,33cl::desc("BPF: Disable Serializing ICMP insns."),34cl::init(false));3536static cl::opt<bool> DisableBPFavoidSpeculation(37"bpf-disable-avoid-speculation", cl::Hidden,38cl::desc("BPF: Disable Avoiding Speculative Code Motion."),39cl::init(false));4041namespace {42class BPFAdjustOptImpl {43struct PassThroughInfo {44Instruction *Input;45Instruction *UsedInst;46uint32_t OpIdx;47PassThroughInfo(Instruction *I, Instruction *U, uint32_t Idx)48: Input(I), UsedInst(U), OpIdx(Idx) {}49};5051public:52BPFAdjustOptImpl(Module *M) : M(M) {}5354bool run();5556private:57Module *M;58SmallVector<PassThroughInfo, 16> PassThroughs;5960bool adjustICmpToBuiltin();61void adjustBasicBlock(BasicBlock &BB);62bool serializeICMPCrossBB(BasicBlock &BB);63void adjustInst(Instruction &I);64bool serializeICMPInBB(Instruction &I);65bool avoidSpeculation(Instruction &I);66bool insertPassThrough();67};6869} // End anonymous namespace7071bool BPFAdjustOptImpl::run() {72bool Changed = adjustICmpToBuiltin();7374for (Function &F : *M)75for (auto &BB : F) {76adjustBasicBlock(BB);77for (auto &I : BB)78adjustInst(I);79}80return insertPassThrough() || Changed;81}8283// Commit acabad9ff6bf ("[InstCombine] try to canonicalize icmp with84// trunc op into mask and cmp") added a transformation to85// convert "(conv)a < power_2_const" to "a & <const>" in certain86// cases and bpf kernel verifier has to handle the resulted code87// conservatively and this may reject otherwise legitimate program.88// Here, we change related icmp code to a builtin which will89// be restored to original icmp code later to prevent that90// InstCombine transformatin.91bool BPFAdjustOptImpl::adjustICmpToBuiltin() {92bool Changed = false;93ICmpInst *ToBeDeleted = nullptr;94for (Function &F : *M)95for (auto &BB : F)96for (auto &I : BB) {97if (ToBeDeleted) {98ToBeDeleted->eraseFromParent();99ToBeDeleted = nullptr;100}101102auto *Icmp = dyn_cast<ICmpInst>(&I);103if (!Icmp)104continue;105106Value *Op0 = Icmp->getOperand(0);107if (!isa<TruncInst>(Op0))108continue;109110auto ConstOp1 = dyn_cast<ConstantInt>(Icmp->getOperand(1));111if (!ConstOp1)112continue;113114auto ConstOp1Val = ConstOp1->getValue().getZExtValue();115auto Op = Icmp->getPredicate();116if (Op == ICmpInst::ICMP_ULT || Op == ICmpInst::ICMP_UGE) {117if ((ConstOp1Val - 1) & ConstOp1Val)118continue;119} else if (Op == ICmpInst::ICMP_ULE || Op == ICmpInst::ICMP_UGT) {120if (ConstOp1Val & (ConstOp1Val + 1))121continue;122} else {123continue;124}125126Constant *Opcode =127ConstantInt::get(Type::getInt32Ty(BB.getContext()), Op);128Function *Fn = Intrinsic::getDeclaration(129M, Intrinsic::bpf_compare, {Op0->getType(), ConstOp1->getType()});130auto *NewInst = CallInst::Create(Fn, {Opcode, Op0, ConstOp1});131NewInst->insertBefore(&I);132Icmp->replaceAllUsesWith(NewInst);133Changed = true;134ToBeDeleted = Icmp;135}136137return Changed;138}139140bool BPFAdjustOptImpl::insertPassThrough() {141for (auto &Info : PassThroughs) {142auto *CI = BPFCoreSharedInfo::insertPassThrough(143M, Info.UsedInst->getParent(), Info.Input, Info.UsedInst);144Info.UsedInst->setOperand(Info.OpIdx, CI);145}146147return !PassThroughs.empty();148}149150// To avoid combining conditionals in the same basic block by151// instrcombine optimization.152bool BPFAdjustOptImpl::serializeICMPInBB(Instruction &I) {153// For:154// comp1 = icmp <opcode> ...;155// comp2 = icmp <opcode> ...;156// ... or comp1 comp2 ...157// changed to:158// comp1 = icmp <opcode> ...;159// comp2 = icmp <opcode> ...;160// new_comp1 = __builtin_bpf_passthrough(seq_num, comp1)161// ... or new_comp1 comp2 ...162Value *Op0, *Op1;163// Use LogicalOr (accept `or i1` as well as `select i1 Op0, true, Op1`)164if (!match(&I, m_LogicalOr(m_Value(Op0), m_Value(Op1))))165return false;166auto *Icmp1 = dyn_cast<ICmpInst>(Op0);167if (!Icmp1)168return false;169auto *Icmp2 = dyn_cast<ICmpInst>(Op1);170if (!Icmp2)171return false;172173Value *Icmp1Op0 = Icmp1->getOperand(0);174Value *Icmp2Op0 = Icmp2->getOperand(0);175if (Icmp1Op0 != Icmp2Op0)176return false;177178// Now we got two icmp instructions which feed into179// an "or" instruction.180PassThroughInfo Info(Icmp1, &I, 0);181PassThroughs.push_back(Info);182return true;183}184185// To avoid combining conditionals in the same basic block by186// instrcombine optimization.187bool BPFAdjustOptImpl::serializeICMPCrossBB(BasicBlock &BB) {188// For:189// B1:190// comp1 = icmp <opcode> ...;191// if (comp1) goto B2 else B3;192// B2:193// comp2 = icmp <opcode> ...;194// if (comp2) goto B4 else B5;195// B4:196// ...197// changed to:198// B1:199// comp1 = icmp <opcode> ...;200// comp1 = __builtin_bpf_passthrough(seq_num, comp1);201// if (comp1) goto B2 else B3;202// B2:203// comp2 = icmp <opcode> ...;204// if (comp2) goto B4 else B5;205// B4:206// ...207208// Check basic predecessors, if two of them (say B1, B2) are using209// icmp instructions to generate conditions and one is the predesessor210// of another (e.g., B1 is the predecessor of B2). Add a passthrough211// barrier after icmp inst of block B1.212BasicBlock *B2 = BB.getSinglePredecessor();213if (!B2)214return false;215216BasicBlock *B1 = B2->getSinglePredecessor();217if (!B1)218return false;219220Instruction *TI = B2->getTerminator();221auto *BI = dyn_cast<BranchInst>(TI);222if (!BI || !BI->isConditional())223return false;224auto *Cond = dyn_cast<ICmpInst>(BI->getCondition());225if (!Cond || B2->getFirstNonPHI() != Cond)226return false;227Value *B2Op0 = Cond->getOperand(0);228auto Cond2Op = Cond->getPredicate();229230TI = B1->getTerminator();231BI = dyn_cast<BranchInst>(TI);232if (!BI || !BI->isConditional())233return false;234Cond = dyn_cast<ICmpInst>(BI->getCondition());235if (!Cond)236return false;237Value *B1Op0 = Cond->getOperand(0);238auto Cond1Op = Cond->getPredicate();239240if (B1Op0 != B2Op0)241return false;242243if (Cond1Op == ICmpInst::ICMP_SGT || Cond1Op == ICmpInst::ICMP_SGE) {244if (Cond2Op != ICmpInst::ICMP_SLT && Cond2Op != ICmpInst::ICMP_SLE)245return false;246} else if (Cond1Op == ICmpInst::ICMP_SLT || Cond1Op == ICmpInst::ICMP_SLE) {247if (Cond2Op != ICmpInst::ICMP_SGT && Cond2Op != ICmpInst::ICMP_SGE)248return false;249} else if (Cond1Op == ICmpInst::ICMP_ULT || Cond1Op == ICmpInst::ICMP_ULE) {250if (Cond2Op != ICmpInst::ICMP_UGT && Cond2Op != ICmpInst::ICMP_UGE)251return false;252} else if (Cond1Op == ICmpInst::ICMP_UGT || Cond1Op == ICmpInst::ICMP_UGE) {253if (Cond2Op != ICmpInst::ICMP_ULT && Cond2Op != ICmpInst::ICMP_ULE)254return false;255} else {256return false;257}258259PassThroughInfo Info(Cond, BI, 0);260PassThroughs.push_back(Info);261262return true;263}264265// To avoid speculative hoisting certain computations out of266// a basic block.267bool BPFAdjustOptImpl::avoidSpeculation(Instruction &I) {268if (auto *LdInst = dyn_cast<LoadInst>(&I)) {269if (auto *GV = dyn_cast<GlobalVariable>(LdInst->getOperand(0))) {270if (GV->hasAttribute(BPFCoreSharedInfo::AmaAttr) ||271GV->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))272return false;273}274}275276if (!isa<LoadInst>(&I) && !isa<CallInst>(&I))277return false;278279// For:280// B1:281// var = ...282// ...283// /* icmp may not be in the same block as var = ... */284// comp1 = icmp <opcode> var, <const>;285// if (comp1) goto B2 else B3;286// B2:287// ... var ...288// change to:289// B1:290// var = ...291// ...292// /* icmp may not be in the same block as var = ... */293// comp1 = icmp <opcode> var, <const>;294// if (comp1) goto B2 else B3;295// B2:296// var = __builtin_bpf_passthrough(seq_num, var);297// ... var ...298bool isCandidate = false;299SmallVector<PassThroughInfo, 4> Candidates;300for (User *U : I.users()) {301Instruction *Inst = dyn_cast<Instruction>(U);302if (!Inst)303continue;304305// May cover a little bit more than the306// above pattern.307if (auto *Icmp1 = dyn_cast<ICmpInst>(Inst)) {308Value *Icmp1Op1 = Icmp1->getOperand(1);309if (!isa<Constant>(Icmp1Op1))310return false;311isCandidate = true;312continue;313}314315// Ignore the use in the same basic block as the definition.316if (Inst->getParent() == I.getParent())317continue;318319// use in a different basic block, If there is a call or320// load/store insn before this instruction in this basic321// block. Most likely it cannot be hoisted out. Skip it.322for (auto &I2 : *Inst->getParent()) {323if (isa<CallInst>(&I2))324return false;325if (isa<LoadInst>(&I2) || isa<StoreInst>(&I2))326return false;327if (&I2 == Inst)328break;329}330331// It should be used in a GEP or a simple arithmetic like332// ZEXT/SEXT which is used for GEP.333if (Inst->getOpcode() == Instruction::ZExt ||334Inst->getOpcode() == Instruction::SExt) {335PassThroughInfo Info(&I, Inst, 0);336Candidates.push_back(Info);337} else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) {338// traverse GEP inst to find Use operand index339unsigned i, e;340for (i = 1, e = GI->getNumOperands(); i != e; ++i) {341Value *V = GI->getOperand(i);342if (V == &I)343break;344}345if (i == e)346continue;347348PassThroughInfo Info(&I, GI, i);349Candidates.push_back(Info);350}351}352353if (!isCandidate || Candidates.empty())354return false;355356llvm::append_range(PassThroughs, Candidates);357return true;358}359360void BPFAdjustOptImpl::adjustBasicBlock(BasicBlock &BB) {361if (!DisableBPFserializeICMP && serializeICMPCrossBB(BB))362return;363}364365void BPFAdjustOptImpl::adjustInst(Instruction &I) {366if (!DisableBPFserializeICMP && serializeICMPInBB(I))367return;368if (!DisableBPFavoidSpeculation && avoidSpeculation(I))369return;370}371372PreservedAnalyses BPFAdjustOptPass::run(Module &M, ModuleAnalysisManager &AM) {373return BPFAdjustOptImpl(&M).run() ? PreservedAnalyses::none()374: PreservedAnalyses::all();375}376377378