//===------------ BPFCheckAndAdjustIR.cpp - Check and Adjust IR -----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Check IR and adjust IR for verifier friendly codes. // The following are done for IR checking: // - no relocation globals in PHI node. // The following are done for IR adjustment: // - remove __builtin_bpf_passthrough builtins. Target independent IR // optimizations are done and those builtins can be removed. // //===----------------------------------------------------------------------===// #include "BPF.h" #include "BPFCORE.h" #include "BPFTargetMachine.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" #include "llvm/IR/Value.h" #include "llvm/Pass.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #define DEBUG_TYPE "bpf-check-and-opt-ir" using namespace llvm; namespace { class BPFCheckAndAdjustIR final : public ModulePass { bool runOnModule(Module &F) override; public: static char ID; BPFCheckAndAdjustIR() : ModulePass(ID) {} private: void checkIR(Module &M); bool adjustIR(Module &M); bool removePassThroughBuiltin(Module &M); bool removeCompareBuiltin(Module &M); }; } // End anonymous namespace char BPFCheckAndAdjustIR::ID = 0; INITIALIZE_PASS(BPFCheckAndAdjustIR, DEBUG_TYPE, "BPF Check And Adjust IR", false, false) ModulePass *llvm::createBPFCheckAndAdjustIR() { return new BPFCheckAndAdjustIR(); } void BPFCheckAndAdjustIR::checkIR(Module &M) { // Ensure relocation global won't appear in PHI node // This may happen if the compiler generated the following code: // B1: // g1 = @llvm.skb_buff:0:1... // ... // goto B_COMMON // B2: // g2 = @llvm.skb_buff:0:2... // ... // goto B_COMMON // B_COMMON: // g = PHI(g1, g2) // x = load g // ... // If anything likes the above "g = PHI(g1, g2)", issue a fatal error. for (Function &F : M) for (auto &BB : F) for (auto &I : BB) { PHINode *PN = dyn_cast(&I); if (!PN || PN->use_empty()) continue; for (int i = 0, e = PN->getNumIncomingValues(); i < e; ++i) { auto *GV = dyn_cast(PN->getIncomingValue(i)); if (!GV) continue; if (GV->hasAttribute(BPFCoreSharedInfo::AmaAttr) || GV->hasAttribute(BPFCoreSharedInfo::TypeIdAttr)) report_fatal_error("relocation global in PHI node"); } } } bool BPFCheckAndAdjustIR::removePassThroughBuiltin(Module &M) { // Remove __builtin_bpf_passthrough()'s which are used to prevent // certain IR optimizations. Now major IR optimizations are done, // remove them. bool Changed = false; CallInst *ToBeDeleted = nullptr; for (Function &F : M) for (auto &BB : F) for (auto &I : BB) { if (ToBeDeleted) { ToBeDeleted->eraseFromParent(); ToBeDeleted = nullptr; } auto *Call = dyn_cast(&I); if (!Call) continue; auto *GV = dyn_cast(Call->getCalledOperand()); if (!GV) continue; if (!GV->getName().startswith("llvm.bpf.passthrough")) continue; Changed = true; Value *Arg = Call->getArgOperand(1); Call->replaceAllUsesWith(Arg); ToBeDeleted = Call; } return Changed; } bool BPFCheckAndAdjustIR::removeCompareBuiltin(Module &M) { // Remove __builtin_bpf_compare()'s which are used to prevent // certain IR optimizations. Now major IR optimizations are done, // remove them. bool Changed = false; CallInst *ToBeDeleted = nullptr; for (Function &F : M) for (auto &BB : F) for (auto &I : BB) { if (ToBeDeleted) { ToBeDeleted->eraseFromParent(); ToBeDeleted = nullptr; } auto *Call = dyn_cast(&I); if (!Call) continue; auto *GV = dyn_cast(Call->getCalledOperand()); if (!GV) continue; if (!GV->getName().startswith("llvm.bpf.compare")) continue; Changed = true; Value *Arg0 = Call->getArgOperand(0); Value *Arg1 = Call->getArgOperand(1); Value *Arg2 = Call->getArgOperand(2); auto OpVal = cast(Arg0)->getValue().getZExtValue(); CmpInst::Predicate Opcode = (CmpInst::Predicate)OpVal; auto *ICmp = new ICmpInst(Opcode, Arg1, Arg2); ICmp->insertBefore(Call); Call->replaceAllUsesWith(ICmp); ToBeDeleted = Call; } return Changed; } bool BPFCheckAndAdjustIR::adjustIR(Module &M) { bool Changed = removePassThroughBuiltin(M); Changed = removeCompareBuiltin(M) || Changed; return Changed; } bool BPFCheckAndAdjustIR::runOnModule(Module &M) { checkIR(M); return adjustIR(M); }