https://github.com/badumbatish created https://github.com/llvm/llvm-project/pull/169081
draft PR for avoid deletion, >From d015af2cb7ca61a0445a78781b31178e3f6fa9f2 Mon Sep 17 00:00:00 2001 From: Jasmine Tang <[email protected]> Date: Thu, 20 Nov 2025 05:23:58 -0800 Subject: [PATCH 1/2] Skeleton --- .../include/clang/CIR/Sema/CIRAnalysisKind.h | 117 ++++++++++++ .../clang/CIR/Sema/FallThroughWarning.h | 66 +++++++ .../include/clang/Frontend/FrontendOptions.h | 3 + clang/include/clang/Options/Options.td | 5 + clang/lib/CIR/CMakeLists.txt | 1 + clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 36 ++++ clang/lib/CIR/FrontendAction/CMakeLists.txt | 1 + clang/lib/CIR/Sema/CIRAnalysisKind.cpp | 67 +++++++ clang/lib/CIR/Sema/CMakeLists.txt | 19 ++ clang/lib/CIR/Sema/FallThroughWarning.cpp | 170 ++++++++++++++++++ 10 files changed, 485 insertions(+) create mode 100644 clang/include/clang/CIR/Sema/CIRAnalysisKind.h create mode 100644 clang/include/clang/CIR/Sema/FallThroughWarning.h create mode 100644 clang/lib/CIR/Sema/CIRAnalysisKind.cpp create mode 100644 clang/lib/CIR/Sema/CMakeLists.txt create mode 100644 clang/lib/CIR/Sema/FallThroughWarning.cpp diff --git a/clang/include/clang/CIR/Sema/CIRAnalysisKind.h b/clang/include/clang/CIR/Sema/CIRAnalysisKind.h new file mode 100644 index 0000000000000..304acfcf433db --- /dev/null +++ b/clang/include/clang/CIR/Sema/CIRAnalysisKind.h @@ -0,0 +1,117 @@ +//===--- CIRAnalysisKind.h - CIR Analysis Pass Kinds -----------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines the CIR analysis pass kinds enum and related utilities. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_SEMA_CIRANALYSISKIND_H +#define LLVM_CLANG_CIR_SEMA_CIRANALYSISKIND_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> +#include <string> +#include <vector> + +namespace cir { + +/// Enumeration of available CIR semantic analysis passes +enum class CIRAnalysisKind : unsigned { + Unrecognized = 0, + FallThrough = 1 << 0, // Fallthrough warning analysis + UnreachableCode = 1 << 1, // Unreachable code detection + NullCheck = 1 << 2, // Null pointer checks + UninitializedVar = 1 << 3, // Uninitialized variable detection + // Add more analysis passes here as needed +}; + +/// A set of CIR analysis passes (bitmask) +class CIRAnalysisSet { + unsigned mask = 0; + +public: + CIRAnalysisSet() = default; + explicit CIRAnalysisSet(CIRAnalysisKind kind) + : mask(static_cast<unsigned>(kind)) {} + explicit CIRAnalysisSet(unsigned mask) : mask(mask) {} + + /// Check if a specific analysis is enabled + bool has(CIRAnalysisKind kind) const { + return (mask & static_cast<unsigned>(kind)) != 0; + } + + /// Enable a specific analysis + void enable(CIRAnalysisKind kind) { + mask |= static_cast<unsigned>(kind); + } + + /// Disable a specific analysis + void disable(CIRAnalysisKind kind) { + mask &= ~static_cast<unsigned>(kind); + } + + /// Check if any analysis is enabled + bool hasAny() const { return mask != 0; } + + /// Check if no analysis is enabled + bool empty() const { return mask == 0; } + + /// Get the raw mask value + unsigned getMask() const { return mask; } + + /// Union with another set + CIRAnalysisSet &operator|=(const CIRAnalysisSet &other) { + mask |= other.mask; + return *this; + } + + /// Union operator + CIRAnalysisSet operator|(const CIRAnalysisSet &other) const { + return CIRAnalysisSet(mask | other.mask); + } + + /// Intersection with another set + CIRAnalysisSet &operator&=(const CIRAnalysisSet &other) { + mask &= other.mask; + return *this; + } + + /// Intersection operator + CIRAnalysisSet operator&(const CIRAnalysisSet &other) const { + return CIRAnalysisSet(mask & other.mask); + } + + bool operator==(const CIRAnalysisSet &other) const { + return mask == other.mask; + } + + bool operator!=(const CIRAnalysisSet &other) const { + return mask != other.mask; + } + + /// Print the analysis set to an output stream + void print(llvm::raw_ostream &OS) const; +}; + +/// Parse a single analysis name into a CIRAnalysisKind +/// Returns std::nullopt if the name is not recognized +CIRAnalysisKind parseCIRAnalysisKind(llvm::StringRef name); + +/// Parse a list of analysis names (from command line) into a CIRAnalysisSet +/// Handles comma and semicolon separators +/// Invalid names are ignored and optionally reported via InvalidNames +CIRAnalysisSet parseCIRAnalysisList( + const std::vector<std::string> &analysisList, + llvm::SmallVectorImpl<std::string> *invalidNames = nullptr); + +} // namespace cir + +#endif // LLVM_CLANG_CIR_SEMA_CIRANALYSISKIND_H diff --git a/clang/include/clang/CIR/Sema/FallThroughWarning.h b/clang/include/clang/CIR/Sema/FallThroughWarning.h new file mode 100644 index 0000000000000..808cf43f92ce0 --- /dev/null +++ b/clang/include/clang/CIR/Sema/FallThroughWarning.h @@ -0,0 +1,66 @@ +//===--- FallThroughWarning.h - CIR Fall-Through Analysis ------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines the FallThroughWarningPass for CIR fall-through analysis. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_SEMA_FALLTHROUGHWARNING_H +#define LLVM_CLANG_CIR_SEMA_FALLTHROUGHWARNING_H + +#include "clang/AST/Type.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Sema/AnalysisBasedWarnings.h" + +#include "mlir/IR/Block.h" +#include "mlir/IR/Operation.h" +#include "mlir/Support/LLVM.h" +namespace cir { +class FuncOp; +} // namespace cir + +namespace clang { +class Sema; + +enum ControlFlowKind { + UnknownFallThrough, + NeverFallThrough, + MaybeFallThrough, + AlwaysFallThrough, + NeverFallThroughOrReturn +}; + +/// Configuration for fall-through diagnostics +struct CheckFallThroughDiagnostics { + unsigned diagFallThrough = 0; + unsigned diagReturn = 0; + unsigned diagFallThroughAttr = 0; + unsigned funKind = 0; + SourceLocation funcLoc; + + bool checkDiagnostics(DiagnosticsEngine &d, bool returnsVoid, + bool hasNoReturn) const; +}; + +/// Pass for analyzing fall-through behavior in CIR functions +class FallThroughWarningPass { +public: + FallThroughWarningPass() = default; + + /// Check fall-through behavior for a CIR function body + void checkFallThroughForFuncBody(Sema &s, cir::FuncOp cfg, QualType blockType, + const CheckFallThroughDiagnostics &cd); + ControlFlowKind checkFallThrough(cir::FuncOp cfg); + mlir::DenseSet<mlir::Block *> getLiveSet(cir::FuncOp cfg); +}; + +} // namespace clang + +#endif // LLVM_CLANG_CIR_SEMA_FALLTHROUGHWARNING_H diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index c919a53ae089e..a152309212bf8 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -420,6 +420,9 @@ class FrontendOptions { LLVM_PREFERRED_TYPE(bool) unsigned ClangIRDisableCIRVerifier : 1; + /// List of ClangIR semantic analysis passes to enable + std::vector<std::string> ClangIRAnalysisList; + CodeCompleteOptions CodeCompleteOpts; /// Specifies the output format of the AST. diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 2f7434d8afe11..16821a7b6f230 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -3150,6 +3150,11 @@ def clangir_disable_verifier : Flag<["-"], "clangir-disable-verifier">, HelpText<"ClangIR: Disable MLIR module verifier">, MarshallingInfoFlag<FrontendOpts<"ClangIRDisableCIRVerifier">>; +def fclangir_analysis_EQ : CommaJoined<["-"], "fclangir-analysis=">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enable ClangIR semantic analysis passes. Pass comma or semicolon separated list of analysis names">, + MarshallingInfoStringVector<FrontendOpts<"ClangIRAnalysisList">>; + defm clangir : BoolFOption<"clangir", FrontendOpts<"UseClangIRPipeline">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to compile">, diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt index 7bdf3fcc59035..f86ad0933bce6 100644 --- a/clang/lib/CIR/CMakeLists.txt +++ b/clang/lib/CIR/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(CodeGen) add_subdirectory(FrontendAction) add_subdirectory(Interfaces) add_subdirectory(Lowering) +add_subdirectory(Sema) diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index 67bb5657d4001..fb86b5f848628 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -10,12 +10,20 @@ #include "mlir/IR/MLIRContext.h" #include "mlir/IR/OwningOpRef.h" #include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/CIR/CIRGenerator.h" #include "clang/CIR/CIRToCIRPasses.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/LowerToLLVM.h" +#include "clang/CIR/Sema/CIRAnalysisKind.h" +#include "clang/CIR/Sema/FallThroughWarning.h" #include "clang/CodeGen/BackendUtil.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/AnalysisBasedWarnings.h" +#include "clang/Sema/Sema.h" #include "llvm/IR/Module.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" using namespace cir; using namespace clang; @@ -108,6 +116,34 @@ class CIRGenConsumer : public clang::ASTConsumer { mlir::ModuleOp MlirModule = Gen->getModule(); mlir::MLIRContext &MlirCtx = Gen->getMLIRContext(); + // Run CIR analysis passes if requested + if (!FEOptions.ClangIRAnalysisList.empty()) { + CIRAnalysisSet AnalysisSet = parseCIRAnalysisList(FEOptions.ClangIRAnalysisList); + AnalysisSet.print(llvm::errs()); + + if (AnalysisSet.has(CIRAnalysisKind::FallThrough)) { + // Get Sema for diagnostics + if (CI.hasSema()) { + Sema &S = CI.getSema(); + FallThroughWarningPass FallThroughPass; + + // Iterate over all functions in the CIR module + MlirModule.walk([&](cir::FuncOp FuncOp) { + // TODO: Get the proper QualType for the function + // For now, use an invalid QualType as placeholder + + QualType FuncType; + + // Set up diagnostics configuration + CheckFallThroughDiagnostics Diags; + + // Run fall-through analysis on this function + FallThroughPass.checkFallThroughForFuncBody(S, FuncOp, FuncType, Diags); + }); + } + } + } + if (!FEOptions.ClangIRDisablePasses) { // Setup and run CIR pipeline. if (runCIRToCIRPasses(MlirModule, MlirCtx, C, diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt index 50d6ea7108ce1..f43ef2d8cbbf5 100644 --- a/clang/lib/CIR/FrontendAction/CMakeLists.txt +++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt @@ -18,6 +18,7 @@ add_clang_library(clangCIRFrontendAction clangBasic clangFrontend clangCIR + clangCIRSema clangCIRLoweringCommon clangCIRLoweringDirectToLLVM clangCodeGen diff --git a/clang/lib/CIR/Sema/CIRAnalysisKind.cpp b/clang/lib/CIR/Sema/CIRAnalysisKind.cpp new file mode 100644 index 0000000000000..3ae24979cfe68 --- /dev/null +++ b/clang/lib/CIR/Sema/CIRAnalysisKind.cpp @@ -0,0 +1,67 @@ +//===--- CIRAnalysisKind.cpp - CIR Analysis Pass Kinds -------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/CIR/Sema/CIRAnalysisKind.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> + +namespace cir { + +CIRAnalysisKind parseCIRAnalysisKind(llvm::StringRef name) { + auto parseResult = llvm::StringSwitch<CIRAnalysisKind>(name) + .Case("fallthrough", CIRAnalysisKind::FallThrough) + .Case("fall-through", CIRAnalysisKind::FallThrough) + .Default(CIRAnalysisKind::Unrecognized); + + return parseResult; +} + + +CIRAnalysisSet parseCIRAnalysisList( + const std::vector<std::string> &analysisList, + llvm::SmallVectorImpl<std::string> *invalidNames) { + CIRAnalysisSet result; + + for (const std::string &item : analysisList) { + llvm::StringRef remaining = item; + CIRAnalysisKind parseKind = parseCIRAnalysisKind(remaining); + if (parseKind == CIRAnalysisKind::Unrecognized) { + llvm::errs() << "Unrecognized CIR analysis option: " << remaining << "\n"; + continue; + } + result.enable(parseKind); + } + + return result; +} + +void CIRAnalysisSet::print(llvm::raw_ostream &OS) const { + if (empty()) { + OS << "none"; + return; + } + + bool first = true; + auto printIfEnabled = [&](CIRAnalysisKind kind, llvm::StringRef name) { + if (has(kind)) { + if (!first) + OS << ", "; + OS << name; + first = false; + } + }; + + printIfEnabled(CIRAnalysisKind::FallThrough, "fallthrough"); + printIfEnabled(CIRAnalysisKind::UnreachableCode, "unreachable-code"); + printIfEnabled(CIRAnalysisKind::NullCheck, "null-check"); + printIfEnabled(CIRAnalysisKind::UninitializedVar, "uninitialized-var"); +} + +} // namespace cir diff --git a/clang/lib/CIR/Sema/CMakeLists.txt b/clang/lib/CIR/Sema/CMakeLists.txt new file mode 100644 index 0000000000000..acf49742d8e4c --- /dev/null +++ b/clang/lib/CIR/Sema/CMakeLists.txt @@ -0,0 +1,19 @@ +add_clang_library(clangCIRSema + FallThroughWarning.cpp + CIRAnalysisKind.cpp + + DEPENDS + MLIRCIRPassIncGen + + LINK_LIBS PUBLIC + clangAST + clangBasic + + MLIRAnalysis + MLIRIR + MLIRPass + MLIRTransformUtils + + MLIRCIR + MLIRCIRInterfaces +) diff --git a/clang/lib/CIR/Sema/FallThroughWarning.cpp b/clang/lib/CIR/Sema/FallThroughWarning.cpp new file mode 100644 index 0000000000000..5fdc9ffc083cb --- /dev/null +++ b/clang/lib/CIR/Sema/FallThroughWarning.cpp @@ -0,0 +1,170 @@ +#include "clang/CIR/Sema/FallThroughWarning.h" +#include "clang/AST/ASTContext.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/raw_ostream.h" + +using namespace mlir; +using namespace cir; +using namespace clang; + +namespace clang { + +//===----------------------------------------------------------------------===// +// Check for missing return value. +//===----------------------------------------------------------------------===// + +bool CheckFallThroughDiagnostics::checkDiagnostics(DiagnosticsEngine &d, + bool returnsVoid, + bool hasNoReturn) const { + if (funKind == diag::FalloffFunctionKind::Function) { + return (returnsVoid || d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && + (!hasNoReturn || + d.isIgnored(diag::warn_noreturn_has_return_expr, funcLoc)) && + (!returnsVoid || + d.isIgnored(diag::warn_suggest_noreturn_block, funcLoc)); + } + if (funKind == diag::FalloffFunctionKind::Coroutine) { + return (returnsVoid || d.isIgnored(diag::warn_falloff_nonvoid, funcLoc)) && + (!hasNoReturn); + } + // For blocks / lambdas. + return returnsVoid && !hasNoReturn; +} + +// TODO: Add a class for fall through config later + +void FallThroughWarningPass::checkFallThroughForFuncBody( + Sema &s, cir::FuncOp cfg, QualType blockType, + const CheckFallThroughDiagnostics &cd) { + + llvm::errs() << "Hello world, you're in CIR sema analysis\n"; + bool returnsVoid = false; + bool hasNoReturn = false; + + // Supposedly all function in cir is FuncOp + // 1. If normal function (FunctionDecl), check if it's coroutine. + // 1a. if coroutine -> check the fallthrough handler (idk what this means, + // TODO for now) + if (cfg.getCoroutine()) { + // TODO: Let's not worry about coroutine for now + } else + returnsVoid = isa<cir::VoidType>(cfg.getFunctionType().getReturnType()); + + // TODO: Do we need to check for InferredNoReturnAttr just like in OG? + hasNoReturn = cfg.getFunctionType().getReturnTypes().empty(); + + DiagnosticsEngine &diags = s.getDiagnostics(); + if (cd.checkDiagnostics(diags, returnsVoid, hasNoReturn)) { + return; + } + + // cpu_dispatch functions permit empty function bodies for ICC compatibility. + // TODO: Do we have isCPUDispatchMultiVersion? + checkFallThrough(cfg); +} + +mlir::DenseSet<mlir::Block *> +FallThroughWarningPass::getLiveSet(cir::FuncOp cfg) { + mlir::DenseSet<mlir::Block *> liveSet; + if (cfg.getBody().empty()) + return liveSet; + + auto &first = cfg.getBody().getBlocks().front(); + + for (auto &block : cfg.getBody()) { + if (block.isReachable(&first)) + liveSet.insert(&block); + } + return liveSet; +} + +ControlFlowKind FallThroughWarningPass::checkFallThrough(cir::FuncOp cfg) { + + assert(cfg && "there can't be a null func op"); + + // TODO: Is no CFG akin to a declaration? + if (cfg.isDeclaration()) { + return UnknownFallThrough; + } + + mlir::DenseSet<mlir::Block *> liveSet = this->getLiveSet(cfg); + + unsigned count = liveSet.size(); + + bool hasLiveReturn = false; + bool hasFakeEdge = false; + bool hasPlainEdge = false; + bool hasAbnormalEdge = false; + + auto &exitBlock = cfg.getBody().back(); + // INFO: in OG clang CFG, they have an empty exit block, so when they query + // pred of exit OG, they get all exit blocks + // + // I guess in CIR, we can pretend exit blocks are all blocks that have no + // successor? + for (mlir::Block &pred : cfg.getBody().getBlocks()) { + if (!liveSet.contains(&pred)) + continue; + + // We consider no predecessors as 'exit blocks' + if (!pred.hasNoSuccessors()) + continue; + + if (!pred.mightHaveTerminator()) + continue; + + mlir::Operation *term = pred.getTerminator(); + if (isa<cir::ReturnOp>(term)) { + hasAbnormalEdge = true; + continue; + } + + // INFO: In OG, we'll be looking for destructor since it can appear past + // return but i guess not in CIR? In this case we'll only be examining the + // terminator + + if (isa<cir::TryOp>(term)) { + hasAbnormalEdge = true; + continue; + } + + // INFO: OG clang has this equals true whenever ri == re, which means this + // is true only when a block only has the terminator, or its size is 1. + hasPlainEdge = std::distance(pred.begin(), pred.end()) == 1; + + if (isa<cir::ReturnOp>(term)) { + hasLiveReturn = true; + continue; + } + if (isa<cir::TryOp>(term)) { + hasLiveReturn = true; + continue; + } + + // TODO: Maybe one day throw will be terminator? + // + // TODO: We need to add a microsoft inline assembly enum + + // TODO: We don't concer with try op either since it's not terminator + + hasPlainEdge = true; + } + + if (!hasPlainEdge) { + if (hasLiveReturn) + return NeverFallThrough; + return NeverFallThroughOrReturn; + } + if (hasAbnormalEdge || hasFakeEdge || hasLiveReturn) + return MaybeFallThrough; + // This says AlwaysFallThrough for calls to functions that are not marked + // noreturn, that don't return. If people would like this warning to be more + // accurate, such functions should be marked as noreturn. + return AlwaysFallThrough; +} +} // namespace clang >From 0eafeed1b29169fa5b8100084da13086855ca263 Mon Sep 17 00:00:00 2001 From: Jasmine Tang <[email protected]> Date: Thu, 20 Nov 2025 19:05:04 -0800 Subject: [PATCH 2/2] Fix reachable behaviors --- clang/lib/CIR/Sema/FallThroughWarning.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/Sema/FallThroughWarning.cpp b/clang/lib/CIR/Sema/FallThroughWarning.cpp index 5fdc9ffc083cb..dd22317fb48ba 100644 --- a/clang/lib/CIR/Sema/FallThroughWarning.cpp +++ b/clang/lib/CIR/Sema/FallThroughWarning.cpp @@ -77,7 +77,7 @@ FallThroughWarningPass::getLiveSet(cir::FuncOp cfg) { auto &first = cfg.getBody().getBlocks().front(); for (auto &block : cfg.getBody()) { - if (block.isReachable(&first)) + if (first.isReachable(&block)) liveSet.insert(&block); } return liveSet; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
