https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/131891

This change introduces the cir-canonicalize pass. This is a simple cir-to-cir 
transformation that eliminates empty scopes and redundant branches. It will be 
expanded in future changes to simplify other redundant instruction sequences.

MLIR verification and mlir-specific command-line option handling is also 
introduced here.

>From d81f7cbd9015fa05c9bfddb80772ffc4237ac91a Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Mon, 17 Mar 2025 14:49:56 -0700
Subject: [PATCH] [CIR] Upstream cir-canonicalize pass

This change introduces the cir-canonicalize pass. This is a simple
cir-to-cir transformation that eliminates empty scopes and redundant
branches. It will be expanded in future changes to simplify other
redundant instruction sequences.

MLIR verification and mlir-specific command-line option handling is
also introduced here.
---
 .../clang/Basic/DiagnosticFrontendKinds.td    |   8 +
 clang/include/clang/CIR/CIRGenerator.h        |   4 +
 clang/include/clang/CIR/CIRToCIRPasses.h      |  39 +++++
 clang/include/clang/CIR/Dialect/Passes.h      |   1 +
 clang/include/clang/CIR/Dialect/Passes.td     |  18 +++
 clang/include/clang/CIR/MissingFeatures.h     |  12 ++
 clang/include/clang/Driver/Options.td         |  14 +-
 .../include/clang/Frontend/FrontendOptions.h  |  13 +-
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |   8 +
 clang/lib/CIR/CodeGen/CIRGenModule.h          |   2 +
 clang/lib/CIR/CodeGen/CIRGenerator.cpp        |   2 +
 .../Dialect/Transforms/CIRCanonicalize.cpp    | 147 ++++++++++++++++++
 .../lib/CIR/Dialect/Transforms/CMakeLists.txt |   1 +
 clang/lib/CIR/FrontendAction/CIRGenAction.cpp |  30 +++-
 clang/lib/CIR/FrontendAction/CMakeLists.txt   |   2 +
 clang/lib/CIR/Lowering/CIRPasses.cpp          |  21 ++-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |   2 +
 clang/lib/Driver/ToolChains/Clang.cpp         |   9 ++
 clang/lib/Frontend/CompilerInvocation.cpp     |   8 +
 clang/lib/FrontendTool/CMakeLists.txt         |   9 ++
 .../ExecuteCompilerInvocation.cpp             |  20 +++
 clang/test/CIR/Transforms/canonicalize.cir    |  72 +++++++++
 clang/test/CIR/mlirargs.c                     |  12 ++
 clang/test/CIR/mlprint.c                      |  14 ++
 clang/tools/cir-opt/cir-opt.cpp               |   4 +
 clang/tools/driver/cc1as_main.cpp             |   1 +
 26 files changed, 468 insertions(+), 5 deletions(-)
 create mode 100644 clang/include/clang/CIR/CIRToCIRPasses.h
 create mode 100644 clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
 create mode 100644 clang/test/CIR/Transforms/canonicalize.cir
 create mode 100644 clang/test/CIR/mlirargs.c
 create mode 100644 clang/test/CIR/mlprint.c

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td 
b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 5a05f6c4e3e30..fcbbe4caf3c9d 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -386,4 +386,12 @@ def warn_hlsl_langstd_minimal :
   Warning<"support for HLSL language version %0 is incomplete, "
           "recommend using %1 instead">,
   InGroup<HLSLDXCCompat>;
+
+// ClangIR frontend errors
+def err_cir_to_cir_transform_failed : Error<
+    "CIR-to-CIR transformation failed">, DefaultFatal;
+
+def err_cir_verification_failed_pre_passes : Error<
+    "CIR module verification error before running CIR-to-CIR passes">,
+    DefaultFatal;
 }
diff --git a/clang/include/clang/CIR/CIRGenerator.h 
b/clang/include/clang/CIR/CIRGenerator.h
index 414eba80b88b8..883dce9deb8e3 100644
--- a/clang/include/clang/CIR/CIRGenerator.h
+++ b/clang/include/clang/CIR/CIRGenerator.h
@@ -55,6 +55,10 @@ class CIRGenerator : public clang::ASTConsumer {
   void Initialize(clang::ASTContext &astContext) override;
   bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
   mlir::ModuleOp getModule() const;
+  mlir::MLIRContext &getMLIRContext() { return *mlirContext; };
+  const mlir::MLIRContext &getMLIRContext() const { return *mlirContext; };
+
+  bool verifyModule() const;
 };
 
 } // namespace cir
diff --git a/clang/include/clang/CIR/CIRToCIRPasses.h 
b/clang/include/clang/CIR/CIRToCIRPasses.h
new file mode 100644
index 0000000000000..361ebb9e9b840
--- /dev/null
+++ b/clang/include/clang/CIR/CIRToCIRPasses.h
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 declares an interface for running CIR-to-CIR passes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_CIRTOCIRPASSES_H
+#define CLANG_CIR_CIRTOCIRPASSES_H
+
+#include "mlir/Pass/Pass.h"
+
+#include <memory>
+
+namespace clang {
+class ASTContext;
+}
+
+namespace mlir {
+class MLIRContext;
+class ModuleOp;
+} // namespace mlir
+
+namespace cir {
+
+// Run set of cleanup/prepare/etc passes CIR <-> CIR.
+mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
+                                      mlir::MLIRContext &mlirCtx,
+                                      clang::ASTContext &astCtx,
+                                      bool enableVerifier);
+
+} // namespace cir
+
+#endif // CLANG_CIR_CIRTOCIRPASSES_H_
diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index b691849dfc563..aa84241bdecf0 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -20,6 +20,7 @@ class ASTContext;
 }
 namespace mlir {
 
+std::unique_ptr<Pass> createCIRCanonicalizePass();
 std::unique_ptr<Pass> createCIRFlattenCFGPass();
 
 void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
diff --git a/clang/include/clang/CIR/Dialect/Passes.td 
b/clang/include/clang/CIR/Dialect/Passes.td
index 84b7ecba2630a..1d9707541e917 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -11,6 +11,24 @@
 
 include "mlir/Pass/PassBase.td"
 
+def CIRCanonicalize : Pass<"cir-canonicalize"> {
+  let summary = "Performs CIR canonicalization";
+  let description = [{
+    Perform canonicalizations on CIR and removes some redundant operations.
+
+    This pass performs basic cleanup and canonicalization transformations that
+    hopefully do not affect CIR-to-source fidelity and high-level code analysis
+    passes too much. Example transformations performed in this pass include
+    empty scope cleanup, trivial try cleanup, redundant branch cleanup, etc.
+    Those more "heavyweight" transformations and those transformations that
+    could significantly affect CIR-to-source fidelity are performed in the
+    `cir-simplify` pass.
+  }];
+
+  let constructor = "mlir::createCIRCanonicalizePass()";
+  let dependentDialects = ["cir::CIRDialect"];
+}
+
 def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
   let summary = "Produces flatten CFG";
   let description = [{
diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 6adff30f5c91a..52bf56727a51b 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -103,6 +103,18 @@ struct MissingFeatures {
   static bool scalableVectors() { return false; }
   static bool unsizedTypes() { return false; }
   static bool vectorType() { return false; }
+
+  // Future CIR operations
+  static bool labelOp() { return false; }
+  static bool brCondOp() { return false; }
+  static bool switchOp() { return false; }
+  static bool tryOp() { return false; }
+  static bool unaryOp() { return false; }
+  static bool selectOp() { return false; }
+  static bool complexCreateOp() { return false; }
+  static bool complexRealOp() { return false; }
+  static bool complexImagOp() { return false; }
+  static bool callOp() { return false; }
 };
 
 } // namespace cir
diff --git a/clang/include/clang/Driver/Options.td 
b/clang/include/clang/Driver/Options.td
index 66ae8f1c7f064..75d00b5b32cd4 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2978,6 +2978,15 @@ def fapple_link_rtlib : Flag<["-"], 
"fapple-link-rtlib">, Group<f_Group>,
   HelpText<"Force linking the clang builtins runtime library">;
 
 /// ClangIR-specific options - BEGIN
+def clangir_disable_passes : Flag<["-"], "clangir-disable-passes">,
+  Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Disable CIR transformations pipeline">,
+  MarshallingInfoFlag<FrontendOpts<"ClangIRDisablePasses">>;
+def clangir_disable_verifier : Flag<["-"], "clangir-disable-verifier">,
+  Visibility<[ClangOption, CC1Option]>,
+  HelpText<"ClangIR: Disable MLIR module verifier">,
+  MarshallingInfoFlag<FrontendOpts<"ClangIRDisableCIRVerifier">>;
+
 defm clangir : BoolFOption<"clangir",
   FrontendOpts<"UseClangIRPipeline">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to 
compile">,
@@ -4822,8 +4831,9 @@ def : Joined<["-"], "mllvm=">,
   Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, Alias<mllvm>,
   HelpText<"Alias for -mllvm">, MetaVarName<"<arg>">;
 def mmlir : Separate<["-"], "mmlir">,
-  Visibility<[ClangOption, CLOption, FC1Option, FlangOption]>,
-  HelpText<"Additional arguments to forward to MLIR's option processing">;
+  Visibility<[ClangOption, CC1Option, FC1Option, FlangOption]>,
+  HelpText<"Additional arguments to forward to MLIR's option processing">,
+  MarshallingInfoStringVector<FrontendOpts<"MLIRArgs">>;
 def ffuchsia_api_level_EQ : Joined<["-"], "ffuchsia-api-level=">,
   Group<m_Group>, Visibility<[ClangOption, CC1Option]>,
   HelpText<"Set Fuchsia API level">,
diff --git a/clang/include/clang/Frontend/FrontendOptions.h 
b/clang/include/clang/Frontend/FrontendOptions.h
index 99b2d9a98ed1f..0e0cbaa537332 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -412,6 +412,12 @@ class FrontendOptions {
   LLVM_PREFERRED_TYPE(bool)
   unsigned UseClangIRPipeline : 1;
 
+  /// Disable Clang IR specific (CIR) passes
+  unsigned ClangIRDisablePasses : 1;
+
+  /// Disable Clang IR (CIR) verifier
+  unsigned ClangIRDisableCIRVerifier : 1;
+
   CodeCompleteOptions CodeCompleteOpts;
 
   /// Specifies the output format of the AST.
@@ -488,6 +494,10 @@ class FrontendOptions {
   /// should only be used for debugging and experimental features.
   std::vector<std::string> LLVMArgs;
 
+  /// A list of arguments to forward to MLIR's option processing; this
+  /// should only be used for debugging and experimental features.
+  std::vector<std::string> MLIRArgs;
+
   /// File name of the file that will provide record layouts
   /// (in the format produced by -fdump-record-layouts).
   std::string OverrideRecordLayoutsFile;
@@ -533,7 +543,8 @@ class FrontendOptions {
         EmitExtensionSymbolGraphs(false),
         EmitSymbolGraphSymbolLabelsForTesting(false),
         EmitPrettySymbolGraphs(false), GenReducedBMI(false),
-        UseClangIRPipeline(false), TimeTraceGranularity(500),
+        UseClangIRPipeline(false), ClangIRDisablePasses(false),
+        ClangIRDisableCIRVerifier(false), TimeTraceGranularity(500),
         TimeTraceVerbose(false) {}
 
   /// getInputKindForExtension - Return the appropriate input kind for a file
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 0e3e15ca2cadc..36bfc2cdf6f02 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 2a798f4cd56a9..734cafa2e07bb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -91,6 +91,8 @@ class CIRGenModule : public CIRGenTypeCache {
 
   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 6fa31ab707139..33f0c292c7710 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 0000000000000..8aa73ea5c1d2a
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 *region = &(op.getScopeRegion()); // getRegions().front();
+    if (region && 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 aa27074cc6131..648666d2461de 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 0f686a36b982b..8ef2e8fe15db7 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 @@ class CIRGenConsumer : public clang::ASTConsumer {
       : 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,31 @@ class CIRGenConsumer : public clang::ASTConsumer {
 
   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.
+      std::string passOptParsingFailure;
+      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 1ebac07f44662..50d6ea7108ce1 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 235acbfee8967..1616ac6145151 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 93d83f2e822ca..db94cf511ba63 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 1012128085c7a..fa4fb412fe8b2 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 e708bee464c5c..b2d8d124a3a6f 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 7c83086a7da3b..061e54c3e62d0 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 bb3bb0aac78bf..6fc587f4de9de 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;
diff --git a/clang/test/CIR/Transforms/canonicalize.cir 
b/clang/test/CIR/Transforms/canonicalize.cir
new file mode 100644
index 0000000000000..925746a90bf19
--- /dev/null
+++ b/clang/test/CIR/Transforms/canonicalize.cir
@@ -0,0 +1,72 @@
+// RUN: cir-opt %s -cir-canonicalize -o - | FileCheck %s
+
+module {
+  cir.func @redundant_br() {
+    cir.br ^bb1
+  ^bb1:  // pred: ^bb0
+    %0 = cir.alloca !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>, ["a", init] 
{alignment = 4 : i64}
+    %1 = cir.const #cir.int<4> : !cir.int<u, 32>
+    cir.store %1, %0 : !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>
+    cir.br ^bb2
+  ^bb2:  // pred: ^bb1
+    cir.return
+  }
+  // CHECK:  cir.func @redundant_br() {
+  // CHECK-NOT: ^
+  // CHECK:     %[[A:.*]] = cir.alloca !cir.int<u, 32>, !cir.ptr<!cir.int<u, 
32>>, ["a", init] {alignment = 4 : i64}
+  // CHECK:     %[[FOUR:.*]] = cir.const #cir.int<4> : !cir.int<u, 32>
+  // CHECK:     cir.store %[[FOUR]], %[[A]] : !cir.int<u, 32>, 
!cir.ptr<!cir.int<u, 32>>
+  // CHECK-NOT: cir.br
+  // CHECK-NOT: ^
+  // CHECK:     cir.return
+  // CHECK:  }
+
+  cir.func @empty_scope() {
+    cir.scope {
+    }
+    cir.return
+  }
+  // CHECK:  cir.func @empty_scope() {
+  // CHECK-NOT: cir.scope
+  // CHECK:     cir.return
+  // CHECK:  }
+
+  cir.func @cast1(%arg0: !cir.bool) -> !cir.bool {
+    %0 = cir.cast(bool_to_int, %arg0 : !cir.bool), !cir.int<s, 32>
+    %1 = cir.cast(int_to_bool, %0 : !cir.int<s, 32>), !cir.bool
+    cir.return %1 : !cir.bool
+  }
+  // CHECK:  cir.func @cast1(%[[ARG0:.*]]: !cir.bool) -> !cir.bool
+  // CHECK-NOT: cir.cast
+  // CHECK:     cir.return %[[ARG0]] : !cir.bool
+
+  cir.func @cast2(%arg0: !cir.int<s, 32>) -> !cir.bool {
+    %0 = cir.cast(int_to_bool, %arg0 : !cir.int<s, 32>), !cir.bool
+    %1 = cir.cast(bool_to_int, %0 : !cir.bool), !cir.int<s, 32>
+    %2 = cir.cast(integral, %1 : !cir.int<s, 32>), !cir.int<s, 64>
+    %3 = cir.cast(int_to_bool, %2 : !cir.int<s, 64>), !cir.bool
+    cir.return %3 : !cir.bool
+  }
+  // CHECK:  cir.func @cast2(%[[ARG0:.*]]: !cir.int<s, 32>) -> !cir.bool
+  // CHECK:    %[[CAST:.*]] = cir.cast(int_to_bool, %[[ARG0]] : !cir.int<s, 
32>), !cir.bool
+  // CHECK-NOT: cir.cast
+  // CHECK:    cir.return %[[CAST]] : !cir.bool
+
+  cir.func @no_fold_cast(%arg0: !cir.int<s, 32>) -> !cir.int<s, 64> {
+    %0 = cir.cast(int_to_bool, %arg0 : !cir.int<s, 32>), !cir.bool
+    %1 = cir.cast(bool_to_int, %0 : !cir.bool), !cir.int<s, 32>
+    %2 = cir.cast(integral, %1 : !cir.int<s, 32>), !cir.int<s, 64>
+    cir.return %2 : !cir.int<s, 64>
+  }
+  // CHECK:  cir.func @no_fold_cast(%[[ARG0:.*]]: !cir.int<s, 32>) -> 
!cir.int<s, 64>
+  // CHECK:    %[[CAST:.*]] = cir.cast(int_to_bool, %[[ARG0]] : !cir.int<s, 
32>), !cir.bool
+  // CHECK:    %[[CAST2:.*]] = cir.cast(bool_to_int, %[[CAST]] : !cir.bool), 
!cir.int<s, 32>
+  // CHECK:    %[[CAST3:.*]] = cir.cast(integral, %[[CAST2]] : !cir.int<s, 
32>), !cir.int<s, 64>
+  // CHECK:    cir.return %[[CAST3]] : !cir.int<s, 64>
+
+}
+
+
+
+
+
diff --git a/clang/test/CIR/mlirargs.c b/clang/test/CIR/mlirargs.c
new file mode 100644
index 0000000000000..cfb07197ef185
--- /dev/null
+++ b/clang/test/CIR/mlirargs.c
@@ -0,0 +1,12 @@
+// Clang returns 1 when wrong arguments are given.
+// RUN: not %clang_cc1 -mmlir -mlir-disable-threadingd  -mmlir 
-mlir-print-op-genericd 2>&1 | FileCheck %s --check-prefix=WRONG
+// Test that the driver can pass mlir args to cc1.
+// RUN: %clang -### -mmlir -mlir-disable-threading %s 2>&1 | FileCheck %s 
--check-prefix=CC1
+
+
+// WRONG: clang (MLIR option parsing): Unknown command line argument 
'-mlir-disable-threadingd'.  Try: 'clang (MLIR option parsing) --help'
+// WRONG: clang (MLIR option parsing): Did you mean '--mlir-disable-threading'?
+// WRONG: clang (MLIR option parsing): Unknown command line argument 
'-mlir-print-op-genericd'.  Try: 'clang (MLIR option parsing) --help'
+// WRONG: clang (MLIR option parsing): Did you mean '--mlir-print-op-generic'?
+
+// CC1: "-mmlir" "-mlir-disable-threading"
diff --git a/clang/test/CIR/mlprint.c b/clang/test/CIR/mlprint.c
new file mode 100644
index 0000000000000..51ebe526a02e6
--- /dev/null
+++ b/clang/test/CIR/mlprint.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir 
--mlir-print-ir-after-all %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm 
-mmlir --mlir-print-ir-after-all -mllvm -print-after-all  %s -o %t.ll 2>&1 | 
FileCheck %s -check-prefix=CIR -check-prefix=LLVM
+
+int foo(void) {
+  int i = 3;
+  return i;
+}
+
+// CIR:  IR Dump After CIRCanonicalize (cir-canonicalize)
+// CIR:  cir.func @foo() -> !cir.int<s, 32>
+// LLVM: IR Dump After cir::direct::ConvertCIRToLLVMPass (cir-flat-to-llvm)
+// LLVM: llvm.func @foo() -> i32
+// LLVM: IR Dump After
+// LLVM: define{{.*}} i32 @foo()
diff --git a/clang/tools/cir-opt/cir-opt.cpp b/clang/tools/cir-opt/cir-opt.cpp
index 8e9311c591a3e..79a26c7986f0b 100644
--- a/clang/tools/cir-opt/cir-opt.cpp
+++ b/clang/tools/cir-opt/cir-opt.cpp
@@ -34,6 +34,10 @@ int main(int argc, char **argv) {
   registry.insert<mlir::BuiltinDialect, cir::CIRDialect,
                   mlir::memref::MemRefDialect, mlir::LLVM::LLVMDialect>();
 
+  ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
+    return mlir::createCIRCanonicalizePass();
+  });
+
   mlir::PassPipelineRegistration<CIRToLLVMPipelineOptions> pipeline(
       "cir-to-llvm", "",
       [](mlir::OpPassManager &pm, const CIRToLLVMPipelineOptions &options) {
diff --git a/clang/tools/driver/cc1as_main.cpp 
b/clang/tools/driver/cc1as_main.cpp
index 7fe97cc6e6ace..d90fadcae5df3 100644
--- a/clang/tools/driver/cc1as_main.cpp
+++ b/clang/tools/driver/cc1as_main.cpp
@@ -326,6 +326,7 @@ bool 
AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
     }
   }
   Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm);
+
   Opts.OutputPath = std::string(Args.getLastArgValue(OPT_o));
   Opts.SplitDwarfOutput =
       std::string(Args.getLastArgValue(OPT_split_dwarf_output));

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to