diff --git a/llvm/lib/Target/BPF/BPF.h b/llvm/lib/Target/BPF/BPF.h index f07ae4c9baf1c..10a4a14571f33 100644 --- a/llvm/lib/Target/BPF/BPF.h +++ b/llvm/lib/Target/BPF/BPF.h @@ -22,6 +22,7 @@ class BPFTargetMachine; class InstructionSelector; class PassRegistry; +ModulePass *createBPFCheckUnreachableIR(); ModulePass *createBPFCheckAndAdjustIR(); FunctionPass *createBPFISelDag(BPFTargetMachine &TM); @@ -34,6 +35,7 @@ InstructionSelector *createBPFInstructionSelector(const BPFTargetMachine &, const BPFSubtarget &, const BPFRegisterBankInfo &); +void initializeBPFCheckUnreachableIRPass(PassRegistry &); void initializeBPFCheckAndAdjustIRPass(PassRegistry&); void initializeBPFDAGToDAGISelLegacyPass(PassRegistry &); void initializeBPFMIPeepholePass(PassRegistry &); diff --git a/llvm/lib/Target/BPF/BPFCheckUnreachableIR.cpp b/llvm/lib/Target/BPF/BPFCheckUnreachableIR.cpp new file mode 100644 index 0000000000000..ea8912d0c0c20 --- /dev/null +++ b/llvm/lib/Target/BPF/BPFCheckUnreachableIR.cpp @@ -0,0 +1,129 @@ +//===--------- BPFCheckUnreachableIR.cpp - Issue Unreachable Error --------===// +// +// 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 'unreachable' IRs and issue proper errors. +// +//===----------------------------------------------------------------------===// + +#include "BPF.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Value.h" +#include "llvm/Pass.h" + +#define DEBUG_TYPE "bpf-check-unreachable-ir" + +using namespace llvm; + +static cl::opt + DisableCheckUnreachableIR("bpf-disable-check-unreachable-ir", cl::Hidden, + cl::desc("BPF: Disable Checking Unreachable IR"), + cl::init(false)); + +namespace { + +class BPFCheckUnreachableIR final : public ModulePass { + bool runOnModule(Module &F) override; + +public: + static char ID; + BPFCheckUnreachableIR() : ModulePass(ID) {} + +private: + void BPFCheckUnreachableIRImpl(Function &F); + void BPFCheckInst(Function &F, BasicBlock &BB, Instruction &I); + void HandleUnreachableInsn(Function &F, BasicBlock &BB, Instruction &I); +}; +} // End anonymous namespace + +char BPFCheckUnreachableIR::ID = 0; +INITIALIZE_PASS(BPFCheckUnreachableIR, DEBUG_TYPE, "BPF Check Unreachable IRs", + false, false) + +ModulePass *llvm::createBPFCheckUnreachableIR() { + return new BPFCheckUnreachableIR(); +} + +void BPFCheckUnreachableIR::HandleUnreachableInsn(Function &F, BasicBlock &BB, + Instruction &I) { + // LLVM may create a switch statement with default to a 'unreachable' basic + // block. Do not warn for such cases. + unsigned NumNoSwitches = 0, NumSwitches = 0; + for (BasicBlock *Pred : predecessors(&BB)) { + const Instruction *Term = Pred->getTerminator(); + if (Term && Term->getOpcode() == Instruction::Switch) { + NumSwitches++; + continue; + } + NumNoSwitches++; + } + if (NumSwitches > 0 && NumNoSwitches == 0) + return; + + // If the previous insn is no return, do not warn for such cases. + // One example is __bpf_unreachable from libbpf bpf_headers.h. + Instruction *PrevI = I.getPrevNonDebugInstruction(); + if (PrevI) { + auto *CI = dyn_cast(PrevI); + if (CI && CI->doesNotReturn()) + return; + } + + // Typically the 'unreachable' insn is the last insn in the function. + // Find the closest line number to this insn and report such info to users. + uint32_t LineNum = 0; + for (Instruction &Insn : llvm::reverse(BB)) { + const DebugLoc &DL = Insn.getDebugLoc(); + if (!DL || DL.getLine() == 0) + continue; + LineNum = DL.getLine(); + break; + } + + std::string LineInfo; + if (LineNum) + LineInfo = + " from line " + std::to_string(LineNum) + " to the end of function"; + + F.getContext().diagnose(DiagnosticInfoGeneric( + Twine("in function ") + .concat("\"" + F.getName() + "\"") + .concat(LineInfo) + .concat(" that code was deleted as unreachable.\n") + .concat(" due to uninitialized variable? try -Wuninitialized?"), + DS_Error)); +} + +void BPFCheckUnreachableIR::BPFCheckInst(Function &F, BasicBlock &BB, + Instruction &I) { + if (I.getOpcode() == Instruction::Unreachable) + HandleUnreachableInsn(F, BB, I); +} + +void BPFCheckUnreachableIR::BPFCheckUnreachableIRImpl(Function &F) { + // A 'unreachable' will be added to the end of naked function. + // Let ignore these naked functions. + if (F.hasFnAttribute(Attribute::Naked)) + return; + + for (auto &BB : F) { + for (auto &I : BB) + BPFCheckInst(F, BB, I); + } +} + +bool BPFCheckUnreachableIR::runOnModule(Module &M) { + if (DisableCheckUnreachableIR) + return false; + for (Function &F : M) + BPFCheckUnreachableIRImpl(F); + return false; +} diff --git a/llvm/lib/Target/BPF/BPFTargetMachine.cpp b/llvm/lib/Target/BPF/BPFTargetMachine.cpp index 3379af6fe8744..4ca4600fbe566 100644 --- a/llvm/lib/Target/BPF/BPFTargetMachine.cpp +++ b/llvm/lib/Target/BPF/BPFTargetMachine.cpp @@ -45,6 +45,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeBPFTarget() { PassRegistry &PR = *PassRegistry::getPassRegistry(); initializeGlobalISel(PR); + initializeBPFCheckUnreachableIRPass(PR); initializeBPFCheckAndAdjustIRPass(PR); initializeBPFMIPeepholePass(PR); initializeBPFDAGToDAGISelLegacyPass(PR); @@ -143,6 +144,7 @@ void BPFTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { } void BPFPassConfig::addIRPasses() { + addPass(createBPFCheckUnreachableIR()); addPass(createAtomicExpandLegacyPass()); addPass(createBPFCheckAndAdjustIR()); diff --git a/llvm/lib/Target/BPF/CMakeLists.txt b/llvm/lib/Target/BPF/CMakeLists.txt index eade4cacb7100..1babf59ee0d81 100644 --- a/llvm/lib/Target/BPF/CMakeLists.txt +++ b/llvm/lib/Target/BPF/CMakeLists.txt @@ -26,6 +26,7 @@ add_llvm_target(BPFCodeGen BPFAsmPrinter.cpp BPFASpaceCastSimplifyPass.cpp BPFCheckAndAdjustIR.cpp + BPFCheckUnreachableIR.cpp BPFFrameLowering.cpp BPFInstrInfo.cpp BPFIRPeephole.cpp diff --git a/llvm/test/CodeGen/BPF/naked-fn-with-frame-pointer.ll b/llvm/test/CodeGen/BPF/naked-fn-with-frame-pointer.ll index 4e4436296f3b5..253b321171ba4 100644 --- a/llvm/test/CodeGen/BPF/naked-fn-with-frame-pointer.ll +++ b/llvm/test/CodeGen/BPF/naked-fn-with-frame-pointer.ll @@ -37,5 +37,5 @@ define dso_local void @normal() "frame-pointer"="all" { ; CHECK-BE-NEXT: # %bb.0: ; CHECK-BE-NEXT: call main call void @main() - unreachable + ret void } diff --git a/llvm/test/CodeGen/BPF/store_imm.ll b/llvm/test/CodeGen/BPF/store_imm.ll index dba83937837b6..efcbd9a4f2d53 100644 --- a/llvm/test/CodeGen/BPF/store_imm.ll +++ b/llvm/test/CodeGen/BPF/store_imm.ll @@ -13,7 +13,7 @@ define void @byte(ptr %p0) { store volatile i8 1, ptr %p0, align 1 store volatile i8 -1, ptr %p1, align 1 - unreachable + ret void } define void @half(ptr, ptr %p0) { @@ -26,7 +26,7 @@ define void @half(ptr, ptr %p0) { store volatile i16 1, ptr %p0, align 2 store volatile i16 -1, ptr %p1, align 2 - unreachable + ret void } define void @word(ptr, ptr, ptr %p0) { @@ -47,7 +47,7 @@ define void @word(ptr, ptr, ptr %p0) { store volatile i32 4294967295, ptr %p3, align 4 store volatile i32 4294967296, ptr %p3, align 4 - unreachable + ret void } define void @dword(ptr, ptr, ptr, ptr %p0) { @@ -69,7 +69,7 @@ define void @dword(ptr, ptr, ptr, ptr %p0) { store volatile i64 -2000000000, ptr %p2, align 8 store volatile i64 4294967295, ptr %p3, align 8 - unreachable + ret void } define void @unaligned(ptr %p0) { @@ -88,7 +88,7 @@ define void @unaligned(ptr %p0) { store volatile i32 -2, ptr %p1, align 2 store volatile i64 -2, ptr %p2, align 4 - unreachable + ret void } define void @inline_asm(ptr %p0) { @@ -100,5 +100,5 @@ define void @inline_asm(ptr %p0) { ; CHECK-NEXT: #NO_APP call void asm "*(u32 *)(r0 + 42) = 7;", "~{r0},~{mem}"() - unreachable + ret void }