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