1 //===------------ BPFCheckAndAdjustIR.cpp - Check and Adjust IR -----------===//
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 // Check IR and adjust IR for verifier friendly codes.
10 // The following are done for IR checking:
11 // - no relocation globals in PHI node.
12 // The following are done for IR adjustment:
13 // - remove __builtin_bpf_passthrough builtins. Target independent IR
14 // optimizations are done and those builtins can be removed.
15 //
16 //===----------------------------------------------------------------------===//
17
18 #include "BPF.h"
19 #include "BPFCORE.h"
20 #include "BPFTargetMachine.h"
21 #include "llvm/IR/DebugInfoMetadata.h"
22 #include "llvm/IR/GlobalVariable.h"
23 #include "llvm/IR/Instruction.h"
24 #include "llvm/IR/Instructions.h"
25 #include "llvm/IR/Module.h"
26 #include "llvm/IR/Type.h"
27 #include "llvm/IR/User.h"
28 #include "llvm/IR/Value.h"
29 #include "llvm/Pass.h"
30 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
31
32 #define DEBUG_TYPE "bpf-check-and-opt-ir"
33
34 using namespace llvm;
35
36 namespace {
37
38 class BPFCheckAndAdjustIR final : public ModulePass {
39 bool runOnModule(Module &F) override;
40
41 public:
42 static char ID;
BPFCheckAndAdjustIR()43 BPFCheckAndAdjustIR() : ModulePass(ID) {}
44
45 private:
46 void checkIR(Module &M);
47 bool adjustIR(Module &M);
48 bool removePassThroughBuiltin(Module &M);
49 bool removeCompareBuiltin(Module &M);
50 };
51 } // End anonymous namespace
52
53 char BPFCheckAndAdjustIR::ID = 0;
54 INITIALIZE_PASS(BPFCheckAndAdjustIR, DEBUG_TYPE, "BPF Check And Adjust IR",
55 false, false)
56
createBPFCheckAndAdjustIR()57 ModulePass *llvm::createBPFCheckAndAdjustIR() {
58 return new BPFCheckAndAdjustIR();
59 }
60
checkIR(Module & M)61 void BPFCheckAndAdjustIR::checkIR(Module &M) {
62 // Ensure relocation global won't appear in PHI node
63 // This may happen if the compiler generated the following code:
64 // B1:
65 // g1 = @llvm.skb_buff:0:1...
66 // ...
67 // goto B_COMMON
68 // B2:
69 // g2 = @llvm.skb_buff:0:2...
70 // ...
71 // goto B_COMMON
72 // B_COMMON:
73 // g = PHI(g1, g2)
74 // x = load g
75 // ...
76 // If anything likes the above "g = PHI(g1, g2)", issue a fatal error.
77 for (Function &F : M)
78 for (auto &BB : F)
79 for (auto &I : BB) {
80 PHINode *PN = dyn_cast<PHINode>(&I);
81 if (!PN || PN->use_empty())
82 continue;
83 for (int i = 0, e = PN->getNumIncomingValues(); i < e; ++i) {
84 auto *GV = dyn_cast<GlobalVariable>(PN->getIncomingValue(i));
85 if (!GV)
86 continue;
87 if (GV->hasAttribute(BPFCoreSharedInfo::AmaAttr) ||
88 GV->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
89 report_fatal_error("relocation global in PHI node");
90 }
91 }
92 }
93
removePassThroughBuiltin(Module & M)94 bool BPFCheckAndAdjustIR::removePassThroughBuiltin(Module &M) {
95 // Remove __builtin_bpf_passthrough()'s which are used to prevent
96 // certain IR optimizations. Now major IR optimizations are done,
97 // remove them.
98 bool Changed = false;
99 CallInst *ToBeDeleted = nullptr;
100 for (Function &F : M)
101 for (auto &BB : F)
102 for (auto &I : BB) {
103 if (ToBeDeleted) {
104 ToBeDeleted->eraseFromParent();
105 ToBeDeleted = nullptr;
106 }
107
108 auto *Call = dyn_cast<CallInst>(&I);
109 if (!Call)
110 continue;
111 auto *GV = dyn_cast<GlobalValue>(Call->getCalledOperand());
112 if (!GV)
113 continue;
114 if (!GV->getName().startswith("llvm.bpf.passthrough"))
115 continue;
116 Changed = true;
117 Value *Arg = Call->getArgOperand(1);
118 Call->replaceAllUsesWith(Arg);
119 ToBeDeleted = Call;
120 }
121 return Changed;
122 }
123
removeCompareBuiltin(Module & M)124 bool BPFCheckAndAdjustIR::removeCompareBuiltin(Module &M) {
125 // Remove __builtin_bpf_compare()'s which are used to prevent
126 // certain IR optimizations. Now major IR optimizations are done,
127 // remove them.
128 bool Changed = false;
129 CallInst *ToBeDeleted = nullptr;
130 for (Function &F : M)
131 for (auto &BB : F)
132 for (auto &I : BB) {
133 if (ToBeDeleted) {
134 ToBeDeleted->eraseFromParent();
135 ToBeDeleted = nullptr;
136 }
137
138 auto *Call = dyn_cast<CallInst>(&I);
139 if (!Call)
140 continue;
141 auto *GV = dyn_cast<GlobalValue>(Call->getCalledOperand());
142 if (!GV)
143 continue;
144 if (!GV->getName().startswith("llvm.bpf.compare"))
145 continue;
146
147 Changed = true;
148 Value *Arg0 = Call->getArgOperand(0);
149 Value *Arg1 = Call->getArgOperand(1);
150 Value *Arg2 = Call->getArgOperand(2);
151
152 auto OpVal = cast<ConstantInt>(Arg0)->getValue().getZExtValue();
153 CmpInst::Predicate Opcode = (CmpInst::Predicate)OpVal;
154
155 auto *ICmp = new ICmpInst(Opcode, Arg1, Arg2);
156 ICmp->insertBefore(Call);
157
158 Call->replaceAllUsesWith(ICmp);
159 ToBeDeleted = Call;
160 }
161 return Changed;
162 }
163
adjustIR(Module & M)164 bool BPFCheckAndAdjustIR::adjustIR(Module &M) {
165 bool Changed = removePassThroughBuiltin(M);
166 Changed = removeCompareBuiltin(M) || Changed;
167 return Changed;
168 }
169
runOnModule(Module & M)170 bool BPFCheckAndAdjustIR::runOnModule(Module &M) {
171 checkIR(M);
172 return adjustIR(M);
173 }
174