diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 8 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.h | 2 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenerator.cpp | 2 | ||||
-rw-r--r-- | clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp | 145 | ||||
-rw-r--r-- | clang/lib/CIR/Dialect/Transforms/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 29 | ||||
-rw-r--r-- | clang/lib/CIR/FrontendAction/CMakeLists.txt | 2 | ||||
-rw-r--r-- | clang/lib/CIR/Lowering/CIRPasses.cpp | 21 | ||||
-rw-r--r-- | clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Driver/ToolChains/Clang.cpp | 9 | ||||
-rw-r--r-- | clang/lib/Frontend/CompilerInvocation.cpp | 8 | ||||
-rw-r--r-- | clang/lib/FrontendTool/CMakeLists.txt | 9 | ||||
-rw-r--r-- | clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp | 20 |
13 files changed, 256 insertions, 2 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 0e3e15c..36bfc2c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -23,6 +23,7 @@ #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Location.h" #include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Verifier.h" using namespace clang; using namespace clang::CIRGen; @@ -488,6 +489,13 @@ mlir::Type CIRGenModule::convertType(QualType type) { return genTypes.convertType(type); } +bool CIRGenModule::verifyModule() const { + // Verify the module after we have finished constructing it, this will + // check the structural properties of the IR and invoke any specific + // verifiers we have on the CIR operations. + return mlir::verify(theModule).succeeded(); +} + DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc, llvm::StringRef feature) { unsigned diagID = diags.getCustomDiagID( diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 2a798f4..734cafa 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -91,6 +91,8 @@ public: void emitTopLevelDecl(clang::Decl *decl); + bool verifyModule() const; + /// Return the address of the given function. If funcType is non-null, then /// this function will use the specified type if it has to create it. // TODO: this is a bit weird as `GetAddr` given we give back a FuncOp? diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp index 6fa31ab..33f0c29 100644 --- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -40,6 +40,8 @@ void CIRGenerator::Initialize(ASTContext &astContext) { *mlirContext.get(), astContext, codeGenOpts, diags); } +bool CIRGenerator::verifyModule() const { return cgm->verifyModule(); } + mlir::ModuleOp CIRGenerator::getModule() const { return cgm->getModule(); } bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) { diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp new file mode 100644 index 0000000..d32691d --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -0,0 +1,145 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements pass that canonicalizes CIR operations, eliminating +// redundant branches, empty scopes, and other unnecessary operations. +// +//===----------------------------------------------------------------------===// + +#include "PassDetail.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/IR/Block.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/IR/Region.h" +#include "mlir/Support/LogicalResult.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/Passes.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace mlir; +using namespace cir; + +namespace { + +/// Removes branches between two blocks if it is the only branch. +/// +/// From: +/// ^bb0: +/// cir.br ^bb1 +/// ^bb1: // pred: ^bb0 +/// cir.return +/// +/// To: +/// ^bb0: +/// cir.return +struct RemoveRedundantBranches : public OpRewritePattern<BrOp> { + using OpRewritePattern<BrOp>::OpRewritePattern; + + LogicalResult matchAndRewrite(BrOp op, + PatternRewriter &rewriter) const final { + Block *block = op.getOperation()->getBlock(); + Block *dest = op.getDest(); + + assert(!cir::MissingFeatures::labelOp()); + + // Single edge between blocks: merge it. + if (block->getNumSuccessors() == 1 && + dest->getSinglePredecessor() == block) { + rewriter.eraseOp(op); + rewriter.mergeBlocks(dest, block); + return success(); + } + + return failure(); + } +}; + +struct RemoveEmptyScope + : public OpRewritePattern<ScopeOp>::SplitMatchAndRewrite { + using SplitMatchAndRewrite::SplitMatchAndRewrite; + + LogicalResult match(ScopeOp op) const final { + // TODO: Remove this logic once CIR uses MLIR infrastructure to remove + // trivially dead operations + if (op.isEmpty()) + return success(); + + Region ®ion = op.getScopeRegion(); + if (region.getBlocks().front().getOperations().size() == 1) + return success(isa<YieldOp>(region.getBlocks().front().front())); + + return failure(); + } + + void rewrite(ScopeOp op, PatternRewriter &rewriter) const final { + rewriter.eraseOp(op); + } +}; + +//===----------------------------------------------------------------------===// +// CIRCanonicalizePass +//===----------------------------------------------------------------------===// + +struct CIRCanonicalizePass : public CIRCanonicalizeBase<CIRCanonicalizePass> { + using CIRCanonicalizeBase::CIRCanonicalizeBase; + + // The same operation rewriting done here could have been performed + // by CanonicalizerPass (adding hasCanonicalizer for target Ops and + // implementing the same from above in CIRDialects.cpp). However, it's + // currently too aggressive for static analysis purposes, since it might + // remove things where a diagnostic can be generated. + // + // FIXME: perhaps we can add one more mode to GreedyRewriteConfig to + // disable this behavior. + void runOnOperation() override; +}; + +void populateCIRCanonicalizePatterns(RewritePatternSet &patterns) { + // clang-format off + patterns.add< + RemoveRedundantBranches, + RemoveEmptyScope + >(patterns.getContext()); + // clang-format on +} + +void CIRCanonicalizePass::runOnOperation() { + // Collect rewrite patterns. + RewritePatternSet patterns(&getContext()); + populateCIRCanonicalizePatterns(patterns); + + // Collect operations to apply patterns. + llvm::SmallVector<Operation *, 16> ops; + getOperation()->walk([&](Operation *op) { + assert(!cir::MissingFeatures::brCondOp()); + assert(!cir::MissingFeatures::switchOp()); + assert(!cir::MissingFeatures::tryOp()); + assert(!cir::MissingFeatures::unaryOp()); + assert(!cir::MissingFeatures::selectOp()); + assert(!cir::MissingFeatures::complexCreateOp()); + assert(!cir::MissingFeatures::complexRealOp()); + assert(!cir::MissingFeatures::complexImagOp()); + assert(!cir::MissingFeatures::callOp()); + // CastOp here is to perform a manual `fold` in + // applyOpPatternsGreedily + if (isa<BrOp, ScopeOp, CastOp>(op)) + ops.push_back(op); + }); + + // Apply patterns. + if (applyOpPatternsGreedily(ops, std::move(patterns)).failed()) + signalPassFailure(); +} + +} // namespace + +std::unique_ptr<Pass> mlir::createCIRCanonicalizePass() { + return std::make_unique<CIRCanonicalizePass>(); +} diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt index aa27074..648666d 100644 --- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt @@ -1,4 +1,5 @@ add_clang_library(MLIRCIRTransforms + CIRCanonicalize.cpp FlattenCFG.cpp DEPENDS diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index 0f686a3..a32e6a7 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -9,7 +9,9 @@ #include "clang/CIR/FrontendAction/CIRGenAction.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/OwningOpRef.h" +#include "clang/Basic/DiagnosticFrontend.h" #include "clang/CIR/CIRGenerator.h" +#include "clang/CIR/CIRToCIRPasses.h" #include "clang/CIR/LowerToLLVM.h" #include "clang/CodeGen/BackendUtil.h" #include "clang/Frontend/CompilerInstance.h" @@ -59,6 +61,7 @@ class CIRGenConsumer : public clang::ASTConsumer { ASTContext *Context{nullptr}; IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS; std::unique_ptr<CIRGenerator> Gen; + const FrontendOptions &FEOptions; public: CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI, @@ -66,7 +69,8 @@ public: : Action(Action), CI(CI), OutputStream(std::move(OS)), FS(&CI.getVirtualFileSystem()), Gen(std::make_unique<CIRGenerator>(CI.getDiagnostics(), std::move(FS), - CI.getCodeGenOpts())) {} + CI.getCodeGenOpts())), + FEOptions(CI.getFrontendOpts()) {} void Initialize(ASTContext &Ctx) override { assert(!Context && "initialized multiple times"); @@ -81,7 +85,30 @@ public: void HandleTranslationUnit(ASTContext &C) override { Gen->HandleTranslationUnit(C); + + if (!FEOptions.ClangIRDisableCIRVerifier) { + if (!Gen->verifyModule()) { + CI.getDiagnostics().Report( + diag::err_cir_verification_failed_pre_passes); + llvm::report_fatal_error( + "CIR codegen: module verification error before running CIR passes"); + return; + } + } + mlir::ModuleOp MlirModule = Gen->getModule(); + mlir::MLIRContext &MlirCtx = Gen->getMLIRContext(); + + if (!FEOptions.ClangIRDisablePasses) { + // Setup and run CIR pipeline. + if (runCIRToCIRPasses(MlirModule, MlirCtx, C, + !FEOptions.ClangIRDisableCIRVerifier) + .failed()) { + CI.getDiagnostics().Report(diag::err_cir_to_cir_transform_failed); + return; + } + } + switch (Action) { case CIRGenAction::OutputType::EmitCIR: if (OutputStream && MlirModule) { diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt index 1ebac07..50d6ea7 100644 --- a/clang/lib/CIR/FrontendAction/CMakeLists.txt +++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt @@ -15,8 +15,10 @@ add_clang_library(clangCIRFrontendAction LINK_LIBS clangAST + clangBasic clangFrontend clangCIR + clangCIRLoweringCommon clangCIRLoweringDirectToLLVM clangCodeGen MLIRCIR diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp index 235acbf..1616ac6 100644 --- a/clang/lib/CIR/Lowering/CIRPasses.cpp +++ b/clang/lib/CIR/Lowering/CIRPasses.cpp @@ -11,9 +11,28 @@ //===----------------------------------------------------------------------===// // #include "clang/AST/ASTContext.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Pass/PassManager.h" #include "clang/CIR/Dialect/Passes.h" +#include "llvm/Support/TimeProfiler.h" -#include "mlir/Pass/PassManager.h" +namespace cir { +mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule, + mlir::MLIRContext &mlirContext, + clang::ASTContext &astContext, + bool enableVerifier) { + + llvm::TimeTraceScope scope("CIR To CIR Passes"); + + mlir::PassManager pm(&mlirContext); + pm.addPass(mlir::createCIRCanonicalizePass()); + + pm.enableVerifier(enableVerifier); + (void)mlir::applyPassManagerCLOptions(pm); + return pm.run(theModule); +} + +} // namespace cir namespace mlir { diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 93d83f2..db94cf5 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1115,6 +1115,8 @@ lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx) { mlir::PassManager pm(mlirCtx); populateCIRToLLVMPasses(pm); + (void)mlir::applyPassManagerCLOptions(pm); + if (mlir::failed(pm.run(mlirModule))) { // FIXME: Handle any errors where they occurs and return a nullptr here. report_fatal_error( diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 1012128..fa4fb41 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5520,6 +5520,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_Wa_COMMA, options::OPT_Xassembler, options::OPT_mllvm, + options::OPT_mmlir, }; for (const auto &A : Args) if (llvm::is_contained(kBitcodeOptionIgnorelist, A->getOption().getID())) @@ -7778,6 +7779,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // features enabled through -Xclang -target-feature flags. SanitizeArgs.addArgs(TC, Args, CmdArgs, InputType); +#if CLANG_ENABLE_CIR + // Forward -mmlir arguments to to the MLIR option parser. + for (const Arg *A : Args.filtered(options::OPT_mmlir)) { + A->claim(); + A->render(Args, CmdArgs); + } +#endif // CLANG_ENABLE_CIR + // With -save-temps, we want to save the unoptimized bitcode output from the // CompileJobAction, use -disable-llvm-passes to get pristine IR generated // by the frontend. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index e708bee..b2d8d12 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3105,6 +3105,14 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, if (Args.hasArg(OPT_fclangir) || Args.hasArg(OPT_emit_cir)) Opts.UseClangIRPipeline = true; +#if CLANG_ENABLE_CIR + if (Args.hasArg(OPT_clangir_disable_passes)) + Opts.ClangIRDisablePasses = true; + + if (Args.hasArg(OPT_clangir_disable_verifier)) + Opts.ClangIRDisableCIRVerifier = true; +#endif // CLANG_ENABLE_CIR + if (Args.hasArg(OPT_aux_target_cpu)) Opts.AuxTargetCPU = std::string(Args.getLastArgValue(OPT_aux_target_cpu)); if (Args.hasArg(OPT_aux_target_feature)) diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt index 7c83086..061e54c 100644 --- a/clang/lib/FrontendTool/CMakeLists.txt +++ b/clang/lib/FrontendTool/CMakeLists.txt @@ -20,8 +20,17 @@ if(CLANG_ENABLE_CIR) ) list(APPEND link_libs clangCIRFrontendAction + MLIRCIRTransforms MLIRIR + MLIRPass ) + list(APPEND deps + MLIRBuiltinLocationAttributesIncGen + MLIRBuiltinTypeInterfacesIncGen + ) + + include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include) + include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include) endif() if(CLANG_ENABLE_STATIC_ANALYZER) diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index bb3bb0a..6fc587f 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -32,6 +32,10 @@ #include "llvm/Support/ErrorHandling.h" #if CLANG_ENABLE_CIR +#include "mlir/IR/AsmState.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/Pass/PassManager.h" +#include "clang/CIR/Dialect/Passes.h" #include "clang/CIR/FrontendAction/CIRGenAction.h" #endif @@ -270,6 +274,22 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) { } #endif +#if CLANG_ENABLE_CIR + if (!Clang->getFrontendOpts().MLIRArgs.empty()) { + mlir::registerCIRPasses(); + mlir::registerMLIRContextCLOptions(); + mlir::registerPassManagerCLOptions(); + mlir::registerAsmPrinterCLOptions(); + unsigned NumArgs = Clang->getFrontendOpts().MLIRArgs.size(); + auto Args = std::make_unique<const char *[]>(NumArgs + 2); + Args[0] = "clang (MLIR option parsing)"; + for (unsigned i = 0; i != NumArgs; ++i) + Args[i + 1] = Clang->getFrontendOpts().MLIRArgs[i].c_str(); + Args[NumArgs + 1] = nullptr; + llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); + } +#endif + // If there were errors in processing arguments, don't do anything else. if (Clang->getDiagnostics().hasErrorOccurred()) return false; |