[clang] [CIR] Add cir-opt tool to exercise CIR dialect parsing (PR #128254)

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 24 09:33:31 PST 2025


https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/128254

>From eaa4ca8f45d1a50d5a84ba4f701e6ad1957b549b Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 21 Feb 2025 15:47:32 -0800
Subject: [PATCH 1/3] [CIR] Add cir-opt tool to exercise CIR dialect parsing

Parsing of text-based MLIR files is part of any MLIR dialect, so we
need to be able to read in files using the ClangIR dialect in order
to test this part of the functionality.

This change adds the minimum cir-opt tool needed to read and parse
cir files and write them back to text. This tool will later be
extended to add features for lowering from CIR to other MLIR dialects
and to run CIR passes as they are added.
---
 clang/include/clang/CIR/Passes.h              | 19 +++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  5 +-
 clang/test/CIR/IR/func.cir                    | 85 +++++++++++++++++++
 clang/test/CIR/IR/global.cir                  | 69 +++++++++++++++
 clang/test/CMakeLists.txt                     |  8 +-
 clang/test/lit.cfg.py                         |  2 +
 clang/tools/CMakeLists.txt                    |  3 +
 clang/tools/cir-opt/CMakeLists.txt            | 32 +++++++
 clang/tools/cir-opt/cir-opt.cpp               | 46 ++++++++++
 9 files changed, 266 insertions(+), 3 deletions(-)
 create mode 100644 clang/include/clang/CIR/Passes.h
 create mode 100644 clang/test/CIR/IR/func.cir
 create mode 100644 clang/test/CIR/IR/global.cir
 create mode 100644 clang/tools/cir-opt/CMakeLists.txt
 create mode 100644 clang/tools/cir-opt/cir-opt.cpp

diff --git a/clang/include/clang/CIR/Passes.h b/clang/include/clang/CIR/Passes.h
new file mode 100644
index 0000000000000..eec9819d584b4
--- /dev/null
+++ b/clang/include/clang/CIR/Passes.h
@@ -0,0 +1,19 @@
+#ifndef CLANG_CIR_PASSES_H
+#define CLANG_CIR_PASSES_H
+
+#include "mlir/Pass/Pass.h"
+
+#include <memory>
+
+namespace cir {
+namespace direct {
+/// Create a pass that fully lowers CIR to the LLVMIR dialect.
+std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass();
+
+/// Adds passes that fully lower CIR to the LLVMIR dialect.
+void populateCIRToLLVMPasses(mlir::OpPassManager &pm);
+
+} // namespace direct
+} // end namespace cir
+
+#endif // CLANG_CIR_PASSES_H
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 235b5a057852a..ba7fab2865116 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -27,6 +27,7 @@
 #include "clang/CIR/Dialect/IR/CIRAttrVisitor.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/MissingFeatures.h"
+#include "clang/CIR/Passes.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/TimeProfiler.h"
 
@@ -304,11 +305,11 @@ void ConvertCIRToLLVMPass::runOnOperation() {
     signalPassFailure();
 }
 
-static std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
+std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
   return std::make_unique<ConvertCIRToLLVMPass>();
 }
 
-static void populateCIRToLLVMPasses(mlir::OpPassManager &pm) {
+void populateCIRToLLVMPasses(mlir::OpPassManager &pm) {
   pm.addPass(createConvertCIRToLLVMPass());
 }
 
diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir
new file mode 100644
index 0000000000000..a32c3e697ed25
--- /dev/null
+++ b/clang/test/CIR/IR/func.cir
@@ -0,0 +1,85 @@
+// RUN: cir-opt %s | FileCheck %s
+
+module {
+// void empty() { }
+cir.func @empty() -> !cir.void {
+  cir.return
+}
+// CHECK: cir.func @empty() -> !cir.void {
+// CHECK:   cir.return
+// CHECK: }
+
+// void voidret() { return; }
+cir.func @voidret() -> !cir.void {
+  cir.return
+}
+// CHECK: cir.func @voidret() -> !cir.void {
+// CHECK:   cir.return
+// CHECK: }
+
+// int intfunc() { return 42; }
+cir.func @intfunc() -> !cir.int<s, 32> {
+  %0 = cir.const #cir.int<42> : !cir.int<s, 32>
+  cir.return %0 : !cir.int<s, 32>
+}
+// CHECK: cir.func @intfunc() -> !cir.int<s, 32> {
+// CHECK:   %[[VAL:.*]] = cir.const #cir.int<42> : !cir.int<s, 32>
+// CHECK:   cir.return %[[VAL]] : !cir.int<s, 32>
+// CHECK: }
+
+// int scopes() {
+//  {
+//    {
+//      return 99;
+//    }
+//  }
+//}
+cir.func @scopes() -> !cir.int<s, 32> {
+  cir.scope {
+    cir.scope {
+      %0 = cir.const #cir.int<99> : !cir.int<s, 32>
+      cir.return %0 : !cir.int<s, 32>
+    }
+  }
+  cir.trap
+}
+// CHECK: cir.func @scopes() -> !cir.int<s, 32> {
+// CHECK:   cir.scope {
+// CHECK:     cir.scope {
+// CHECK:       %[[VAL:.*]] = cir.const #cir.int<99> : !cir.int<s, 32>
+// CHECK:       cir.return %[[VAL]] : !cir.int<s, 32>
+// CHECK:     }
+// CHECK:   }
+// CHECK:   cir.trap
+// CHECK: }
+
+// long longfunc() { return 42l; }
+cir.func @longfunc() -> !cir.int<s, 64> {
+  %0 = cir.const #cir.int<42> : !cir.int<s, 64>
+  cir.return %0 : !cir.int<s, 64>
+}
+// CHECK: cir.func @longfunc() -> !cir.int<s, 64>
+// CHECK:   %0 = cir.const #cir.int<42> : !cir.int<s, 64>
+// CHECK:   cir.return %0 : !cir.int<s, 64>
+// CHECK: }
+
+// unsigned unsignedfunc() { return 42u; }
+cir.func @unsignedfunc() -> !cir.int<u, 32> {
+  %0 = cir.const #cir.int<42> : !cir.int<u, 32>
+  cir.return %0 : !cir.int<u, 32>
+}
+// CHECK: cir.func @unsignedfunc() -> !cir.int<u, 32>
+// CHECK:   %[[VAL:.*]] = cir.const #cir.int<42> : !cir.int<u, 32>
+// CHECK:   cir.return %[[VAL]] : !cir.int<u, 32>
+// CHECK: }
+
+// unsigned long long ullfunc() { return 42ull; }
+cir.func @ullfunc() -> !cir.int<u, 64> {
+  %0 = cir.const #cir.int<42> : !cir.int<u, 64>
+  cir.return %0 : !cir.int<u, 64>
+}
+// CHECK: cir.func @ullfunc() -> !cir.int<u, 64>
+// CHECK:   %[[VAL:.*]] = cir.const #cir.int<42> : !cir.int<u, 64>
+// CHECK:   cir.return %[[VAL:.*]] : !cir.int<u, 64>
+// CHECK: }
+}
diff --git a/clang/test/CIR/IR/global.cir b/clang/test/CIR/IR/global.cir
new file mode 100644
index 0000000000000..6c68ab0a501ff
--- /dev/null
+++ b/clang/test/CIR/IR/global.cir
@@ -0,0 +1,69 @@
+// RUN: cir-opt %s -o - | FileCheck %s
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+  cir.global @c : !cir.int<s, 8>
+  cir.global @sc : !cir.int<s, 8>
+  cir.global @uc : !cir.int<u, 8>
+  cir.global @ss : !cir.int<s, 16>
+  cir.global @us = #cir.int<100> : !cir.int<u, 16>
+  cir.global @si = #cir.int<42> : !cir.int<s, 32>
+  cir.global @ui : !cir.int<u, 32>
+  cir.global @sl : !cir.int<s, 64>
+  cir.global @ul : !cir.int<u, 64>
+  cir.global @sll : !cir.int<s, 64>
+  cir.global @ull = #cir.int<123456> : !cir.int<u, 64>
+  cir.global @s128 : !cir.int<s, 128>
+  cir.global @u128 : !cir.int<u, 128>
+  cir.global @wc : !cir.int<s, 32>
+  cir.global @c8 : !cir.int<u, 8>
+  cir.global @c16 : !cir.int<u, 16>
+  cir.global @c32 : !cir.int<u, 32>
+  cir.global @sb20 : !cir.int<s, 20>
+  cir.global @ub48 : !cir.int<u, 48>
+  cir.global @f16 : !cir.f16
+  cir.global @bf16 : !cir.bf16
+  cir.global @f : !cir.float
+  cir.global @d = #cir.fp<1.250000e+00> : !cir.double
+  cir.global @ld : !cir.long_double<!cir.f80>
+  cir.global @f128 : !cir.f128
+  cir.global @vp : !cir.ptr<!cir.void>
+  cir.global @ip = #cir.ptr<null> : !cir.ptr<!cir.int<s, 32>>
+  cir.global @dp : !cir.ptr<!cir.double>
+  cir.global @cpp : !cir.ptr<!cir.ptr<!cir.int<s, 8>>>
+  cir.global @fp : !cir.ptr<!cir.func<!cir.void ()>>
+  cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<!cir.int<s, 32> (!cir.int<s, 32>)>>
+  cir.global @fpvar : !cir.ptr<!cir.func<!cir.void (!cir.int<s, 32>, ...)>>
+}
+
+// CHECK: cir.global @c : !cir.int<s, 8>
+// CHECK: cir.global @sc : !cir.int<s, 8>
+// CHECK: cir.global @uc : !cir.int<u, 8>
+// CHECK: cir.global @ss : !cir.int<s, 16>
+// CHECK: cir.global @us = #cir.int<100>
+// CHECK: cir.global @si = #cir.int<42>
+// CHECK: cir.global @ui : !cir.int<u, 32>
+// CHECK: cir.global @sl : !cir.int<s, 64>
+// CHECK: cir.global @ul : !cir.int<u, 64>
+// CHECK: cir.global @sll : !cir.int<s, 64>
+// CHECK: cir.global @ull = #cir.int<123456> : !cir.int<u, 64>
+// CHECK: cir.global @s128 : !cir.int<s, 128>
+// CHECK: cir.global @u128 : !cir.int<u, 128>
+// CHECK: cir.global @wc : !cir.int<s, 32>
+// CHECK: cir.global @c8 : !cir.int<u, 8>
+// CHECK: cir.global @c16 : !cir.int<u, 16>
+// CHECK: cir.global @c32 : !cir.int<u, 32>
+// CHECK: cir.global @sb20 : !cir.int<s, 20>
+// CHECK: cir.global @ub48 : !cir.int<u, 48>
+// CHECK: cir.global @f16 : !cir.f16
+// CHECK: cir.global @bf16 : !cir.bf16
+// CHECK: cir.global @f : !cir.float
+// CHECK: cir.global @d = #cir.fp<1.250000e+00> : !cir.double
+// CHECK: cir.global @ld : !cir.long_double<!cir.f80>
+// CHECK: cir.global @f128 : !cir.f128
+// CHECK: cir.global @vp : !cir.ptr<!cir.void>
+// CHECK: cir.global @ip = #cir.ptr<null> : !cir.ptr<!cir.int<s, 32>>
+// CHECK: cir.global @dp : !cir.ptr<!cir.double>
+// CHECK: cir.global @cpp : !cir.ptr<!cir.ptr<!cir.int<s, 8>>>
+// CHECK: cir.global @fp : !cir.ptr<!cir.func<!cir.void ()>>
+// CHECK: cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<!cir.int<s, 32> (!cir.int<s, 32>)>>
+// CHECK: cir.global @fpvar : !cir.ptr<!cir.func<!cir.void (!cir.int<s, 32>, ...)>>
diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index 4ff81e7055c57..1c93e2b0d9844 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -85,7 +85,13 @@ list(APPEND CLANG_TEST_DEPS
   diagtool
   hmaptool
   )
-  
+
+if(CLANG_ENABLE_CIR)
+  list(APPEND CLANG_TEST_DEPS
+    cir-opt
+    )
+endif()
+
 if(CLANG_ENABLE_STATIC_ANALYZER)
   list(APPEND CLANG_TEST_DEPS
     clang-check
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index e4b39c4f71597..9820ddd1f14af 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -29,6 +29,7 @@
     ".c",
     ".cpp",
     ".i",
+    ".cir",
     ".cppm",
     ".m",
     ".mm",
@@ -85,6 +86,7 @@
 tools = [
     "apinotes-test",
     "c-index-test",
+    "cir-opt",
     "clang-diff",
     "clang-format",
     "clang-repl",
diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt
index de0b47f3751f1..ae3414e177192 100644
--- a/clang/tools/CMakeLists.txt
+++ b/clang/tools/CMakeLists.txt
@@ -3,6 +3,9 @@ create_subdirectory_options(CLANG TOOL)
 add_clang_subdirectory(diagtool)
 add_clang_subdirectory(driver)
 add_clang_subdirectory(apinotes-test)
+if(CLANG_ENABLE_CIR)
+  add_clang_subdirectory(cir-opt)
+endif()
 add_clang_subdirectory(clang-diff)
 add_clang_subdirectory(clang-format)
 add_clang_subdirectory(clang-fuzzer)
diff --git a/clang/tools/cir-opt/CMakeLists.txt b/clang/tools/cir-opt/CMakeLists.txt
new file mode 100644
index 0000000000000..75bec5f4e1b0b
--- /dev/null
+++ b/clang/tools/cir-opt/CMakeLists.txt
@@ -0,0 +1,32 @@
+get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
+get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS)
+
+include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include)
+include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include)
+
+add_clang_tool(cir-opt
+  cir-opt.cpp
+)
+
+clang_target_link_libraries(cir-opt
+  PRIVATE
+  clangCIR
+  clangCIRLoweringDirectToLLVM
+  MLIRCIR
+)
+
+target_link_libraries(cir-opt
+  PRIVATE
+  ${dialect_libs}
+  ${conversion_libs}
+  MLIRAnalysis
+  MLIRDialect
+  MLIRIR
+  MLIRMemRefDialect
+  MLIROptLib
+  MLIRParser
+  MLIRPass
+  MLIRSideEffectInterfaces
+  MLIRTransforms
+  MLIRTransformUtils
+)
diff --git a/clang/tools/cir-opt/cir-opt.cpp b/clang/tools/cir-opt/cir-opt.cpp
new file mode 100644
index 0000000000000..1dff915946bcd
--- /dev/null
+++ b/clang/tools/cir-opt/cir-opt.cpp
@@ -0,0 +1,46 @@
+//===- cir-opt.cpp - CIR optimization and analysis driver -----*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Similar to MLIR/LLVM's "opt" tools but also deals with analysis and custom
+// arguments. TODO: this is basically a copy from MlirOptMain.cpp, but capable
+// of module emission as specified by the user.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Conversion/ReconcileUnrealizedCasts/ReconcileUnrealizedCasts.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/Dialect/MemRef/IR/MemRef.h"
+#include "mlir/InitAllPasses.h"
+#include "mlir/Pass/PassManager.h"
+#include "mlir/Pass/PassOptions.h"
+#include "mlir/Pass/PassRegistry.h"
+#include "mlir/Tools/mlir-opt/MlirOptMain.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Passes.h"
+
+struct CIRToLLVMPipelineOptions
+    : public mlir::PassPipelineOptions<CIRToLLVMPipelineOptions> {};
+
+int main(int argc, char **argv) {
+  // TODO: register needed MLIR passes for CIR?
+  mlir::DialectRegistry registry;
+  registry.insert<mlir::BuiltinDialect, cir::CIRDialect,
+                  mlir::memref::MemRefDialect, mlir::LLVM::LLVMDialect>();
+
+  mlir::PassPipelineRegistration<CIRToLLVMPipelineOptions> pipeline(
+      "cir-to-llvm", "",
+      [](mlir::OpPassManager &pm, const CIRToLLVMPipelineOptions &options) {
+        cir::direct::populateCIRToLLVMPasses(pm);
+      });
+
+  mlir::registerTransformsPasses();
+
+  return failed(MlirOptMain(
+      argc, argv, "Clang IR analysis and optimization tool\n", registry));
+}

>From 945b7c527647be1f40947736f2fdf108478763ae Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 21 Feb 2025 17:54:42 -0800
Subject: [PATCH 2/3] Fix file headers

---
 clang/include/clang/CIR/Passes.h | 12 ++++++++++++
 clang/tools/cir-opt/cir-opt.cpp  |  2 +-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/CIR/Passes.h b/clang/include/clang/CIR/Passes.h
index eec9819d584b4..1d202ce5bfb52 100644
--- a/clang/include/clang/CIR/Passes.h
+++ b/clang/include/clang/CIR/Passes.h
@@ -1,3 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 exposes the entry points to create compiler passes for ClangIR.
+//
+//===----------------------------------------------------------------------===//
+
 #ifndef CLANG_CIR_PASSES_H
 #define CLANG_CIR_PASSES_H
 
diff --git a/clang/tools/cir-opt/cir-opt.cpp b/clang/tools/cir-opt/cir-opt.cpp
index 1dff915946bcd..b6efc97123513 100644
--- a/clang/tools/cir-opt/cir-opt.cpp
+++ b/clang/tools/cir-opt/cir-opt.cpp
@@ -1,4 +1,4 @@
-//===- cir-opt.cpp - CIR optimization and analysis driver -----*- C++ -*-===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.

>From 6eabc737bec54c7035b0af2c8979c030f42bb6ba Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Mon, 24 Feb 2025 09:32:24 -0800
Subject: [PATCH 3/3] Update cir-opt return code handling

---
 clang/tools/cir-opt/cir-opt.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/tools/cir-opt/cir-opt.cpp b/clang/tools/cir-opt/cir-opt.cpp
index b6efc97123513..0c0f6dcd9eecf 100644
--- a/clang/tools/cir-opt/cir-opt.cpp
+++ b/clang/tools/cir-opt/cir-opt.cpp
@@ -41,6 +41,6 @@ int main(int argc, char **argv) {
 
   mlir::registerTransformsPasses();
 
-  return failed(MlirOptMain(
+  return mlir::asMainReturnCode(MlirOptMain(
       argc, argv, "Clang IR analysis and optimization tool\n", registry));
 }



More information about the cfe-commits mailing list