diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -179,6 +179,8 @@ - Clang now avoids duplicate warnings on unreachable ``[[fallthrough]];`` statements previously issued from ``-Wunreachable-code`` and ``-Wunreachable-code-fallthrough`` by prioritizing ``-Wunreachable-code-fallthrough``. +- New diagnostic ``-Wfunction-fallthrough`` warns when a function falls through + to the next function. Bug Fixes in This Version ------------------------- diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -90,6 +90,9 @@ def warn_fe_backend_warning_attr : Warning<"call to '%0' declared with 'warning' attribute: %1">, BackendInfo, InGroup; +def warn_fe_function_fallthrough: + Warning<"function '%0' falls through to next function">, + InGroup; def err_fe_invalid_code_complete_file : Error< "cannot locate code-completion file %0">, DefaultFatal; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1296,6 +1296,12 @@ def BackendOptimizationRemarkAnalysis : DiagGroup<"pass-analysis">; def BackendOptimizationFailure : DiagGroup<"pass-failed">; def BackendWarningAttributes : DiagGroup<"attribute-warning">; +def BackendWarningFunctionFallthrough : DiagGroup<"function-fallthrough">{ + code Documentation = [{ +The function falls through to the next function. This can occur due to either +the incorrect use of __builtin_unreachable() or potentially a bug in LLVM. +}]; +} // Instrumentation based profiling warnings. def ProfileInstrMissing : DiagGroup<"profile-instr-missing">; diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -462,6 +462,8 @@ /// Specialized handler for misexpect warnings. /// Note that misexpect remarks are emitted through ORE void MisExpectDiagHandler(const llvm::DiagnosticInfoMisExpect &D); + void + FunctionFallthroughHandler(const DiagnosticInfoFunctionFallthrough &DI); }; void BackendConsumer::anchor() {} @@ -875,6 +877,14 @@ << Filename << Line << Column; } +void BackendConsumer::FunctionFallthroughHandler( + const DiagnosticInfoFunctionFallthrough &DI) { + if (std::optional Loc = + getFunctionSourceLocation(DI.getFunction())) + Diags.Report(*Loc, diag::warn_fe_function_fallthrough) + << DI.getFunction().getName(); +} + /// This function is invoked when the backend needs /// to report something to the user. void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) { @@ -957,6 +967,9 @@ case llvm::DK_MisExpect: MisExpectDiagHandler(cast(DI)); return; + case llvm::DK_FunctionFallthrough: + FunctionFallthroughHandler(cast(DI)); + return; default: // Plugin IDs are not bound to any value as they are set dynamically. ComputeDiagRemarkID(Severity, backend_plugin, DiagID); diff --git a/clang/test/Frontend/backend-function-fallthrough.c b/clang/test/Frontend/backend-function-fallthrough.c new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/backend-function-fallthrough.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -O0 -verify -emit-codegen-only %s +// RUN: %clang_cc1 -O2 -verify=expected,optimization -emit-codegen-only %s +void foo (void); +int bar (int x) { // optimization-warning {{function 'bar' falls through to next function}} + if (x) { + foo(); + __builtin_unreachable(); + } + return 42; +} +void baz (void) { // expected-warning {{function 'baz' falls through to next function}} + __builtin_unreachable(); +} diff --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h --- a/llvm/include/llvm/IR/DiagnosticInfo.h +++ b/llvm/include/llvm/IR/DiagnosticInfo.h @@ -20,6 +20,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DebugLoc.h" +#include "llvm/IR/Function.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SourceMgr.h" @@ -38,7 +39,6 @@ class DIFile; class DISubprogram; class CallInst; -class Function; class Instruction; class InstructionCost; class Module; @@ -86,6 +86,7 @@ DK_SrcMgr, DK_DontCall, DK_MisExpect, + DK_FunctionFallthrough, DK_FirstPluginKind // Must be last value to work with // getNextAvailablePluginDiagnosticKind }; @@ -1051,6 +1052,17 @@ const Twine &Msg; }; +struct DiagnosticInfoFunctionFallthrough + : public DiagnosticInfoWithLocationBase { + DiagnosticInfoFunctionFallthrough(Function &F) + : DiagnosticInfoWithLocationBase(DK_FunctionFallthrough, DS_Warning, F, + F.getSubprogram()) {} + static bool classof(const DiagnosticInfo *DI) { + return DI->getKind() == DK_FunctionFallthrough; + } + void print(DiagnosticPrinter &DP) const override; +}; + static DiagnosticSeverity getDiagnosticSeverity(SourceMgr::DiagKind DK) { switch (DK) { case llvm::SourceMgr::DK_Error: diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -66,6 +66,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/EHPersonalities.h" #include "llvm/IR/Function.h" #include "llvm/IR/GCStrategy.h" @@ -2388,6 +2389,30 @@ return Res.first->second; } +static void diagnoseFunctionFallthrough(MachineFunction &MF) { + bool FT = false; + MachineBasicBlock &LastMBB = MF.back(); + if (LastMBB.canFallThrough()) + FT = true; + // TODO: MachineBasicBlock::canFallThrough is too conservative; it will not + // catch this! + else if (LastMBB.empty()) + FT = true; + // TODO: TII::isTailCall? + else if (LastMBB.succ_empty() && !LastMBB.isReturnBlock()) + // TODO: MachineBasicBlock::canFallThrough is too conservative; it will not + // catch this! + // Don't call MachineBasicBlock::back() without a + // !MachineBasicBlock::empty() guard. + if (const Function *Callee = LastMBB.back().getCalledFunction()) + FT = !Callee->hasFnAttribute(Attribute::NoReturn); + if (FT) { + Function &F = MF.getFunction(); + DiagnosticInfoFunctionFallthrough D(F); + F.getContext().diagnose(D); + } +} + void AsmPrinter::SetupMachineFunction(MachineFunction &MF) { this->MF = &MF; const Function &F = MF.getFunction(); @@ -2436,6 +2461,8 @@ } ORE = &getAnalysis().getORE(); + + diagnoseFunctionFallthrough(MF); } namespace { diff --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp --- a/llvm/lib/IR/DiagnosticInfo.cpp +++ b/llvm/lib/IR/DiagnosticInfo.cpp @@ -450,3 +450,8 @@ if (!getNote().empty()) DP << ": " << getNote(); } + +void DiagnosticInfoFunctionFallthrough::print(DiagnosticPrinter &DP) const { + DP << "function '" << getFunction().getName() + << "' falls through to next function"; +} diff --git a/llvm/test/CodeGen/AArch64/function-fallthrough.ll b/llvm/test/CodeGen/AArch64/function-fallthrough.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/function-fallthrough.ll @@ -0,0 +1,8 @@ +; RUN: llc %s 2>&1 | FileCheck %s +declare void @foo () + +define void @bar () { +; CHECK: warning: function 'bar' falls through to next function +entry: + unreachable +}