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