[clang] 622ee03 - [CIR] Initial implementation of CIR-to-LLVM IR lowering pass (#125260)

via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 3 13:35:46 PST 2025


Author: Andy Kaylor
Date: 2025-02-03T13:35:43-08:00
New Revision: 622ee03e262469aecbad9d7f2b4b968576e005f0

URL: https://github.com/llvm/llvm-project/commit/622ee03e262469aecbad9d7f2b4b968576e005f0
DIFF: https://github.com/llvm/llvm-project/commit/622ee03e262469aecbad9d7f2b4b968576e005f0.diff

LOG: [CIR] Initial implementation of CIR-to-LLVM IR lowering pass (#125260)

This change introduces lowering from CIR to LLVM IR of global integer
and floating-point variables, using defaults for attributes that aren't yet
implemented.

Added: 
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
    clang/test/CIR/Lowering/global-var-simple.cpp

Modified: 
    clang/include/clang/CIR/LowerToLLVM.h
    clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
    clang/test/CIR/Lowering/hello.c

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/LowerToLLVM.h b/clang/include/clang/CIR/LowerToLLVM.h
index afa1c1923ed516..6e1b0270fcd2b0 100644
--- a/clang/include/clang/CIR/LowerToLLVM.h
+++ b/clang/include/clang/CIR/LowerToLLVM.h
@@ -12,8 +12,6 @@
 #ifndef CLANG_CIR_LOWERTOLLVM_H
 #define CLANG_CIR_LOWERTOLLVM_H
 
-#include "mlir/Pass/Pass.h"
-
 #include <memory>
 
 namespace llvm {

diff  --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
new file mode 100644
index 00000000000000..3c018aeea65014
--- /dev/null
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -0,0 +1,40 @@
+//===---- MissingFeatures.h - Checks for unimplemented features -*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file introduces some helper classes to guard against features that
+// CIR dialect supports that we do not have and also do not have great ways to
+// assert against.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_MISSINGFEATURES_H
+#define CLANG_CIR_MISSINGFEATURES_H
+
+namespace cir {
+
+// As a way to track features that haven't yet been implemented this class
+// explicitly contains a list of static fns that will return false that you
+// can guard against. If and when a feature becomes implemented simply changing
+// this return to true will cause compilation to fail at all the points in which
+// we noted that we needed to address. This is a much more explicit way to
+// handle "TODO"s.
+struct MissingFeatures {
+  // Address space related
+  static bool addressSpace() { return false; }
+
+  // Unhandled global/linkage information.
+  static bool opGlobalDSOLocal() { return false; }
+  static bool opGlobalThreadLocal() { return false; }
+  static bool opGlobalConstant() { return false; }
+  static bool opGlobalAlignment() { return false; }
+  static bool opGlobalLinkage() { return false; }
+};
+
+} // namespace cir
+
+#endif // CLANG_CIR_MISSINGFEATURES_H

diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
index 99afb196784ecf..3f74c79249a272 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
@@ -3,9 +3,15 @@ set(LLVM_LINK_COMPONENTS
   Support
   )
 
+get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
+
 add_clang_library(clangCIRLoweringDirectToLLVM
   LowerToLLVM.cpp
 
   LINK_LIBS
   MLIRIR
+  ${dialect_libs}
+  MLIRCIR
+  MLIRBuiltinToLLVMIRTranslation
+  MLIRLLVMToLLVMIRTranslation
   )

diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 63d2b51b428357..af8ca7d0b89e68 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -10,9 +10,22 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/CIR/LowerToLLVM.h"
+#include "LowerToLLVM.h"
 
+#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
+#include "mlir/Dialect/DLTI/DLTI.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/BuiltinDialect.h"
 #include "mlir/IR/BuiltinOps.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Pass/PassManager.h"
+#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h"
+#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
+#include "mlir/Target/LLVMIR/Export.h"
+#include "mlir/Transforms/DialectConversion.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/MissingFeatures.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/TimeProfiler.h"
 
@@ -22,16 +35,165 @@ using namespace llvm;
 namespace cir {
 namespace direct {
 
+// This pass requires the CIR to be in a "flat" state. All blocks in each
+// function must belong to the parent region. Once scopes and control flow
+// are implemented in CIR, a pass will be run before this one to flatten
+// the CIR and get it into the state that this pass requires.
+struct ConvertCIRToLLVMPass
+    : public mlir::PassWrapper<ConvertCIRToLLVMPass,
+                               mlir::OperationPass<mlir::ModuleOp>> {
+  void getDependentDialects(mlir::DialectRegistry &registry) const override {
+    registry.insert<mlir::BuiltinDialect, mlir::DLTIDialect,
+                    mlir::LLVM::LLVMDialect, mlir::func::FuncDialect>();
+  }
+  void runOnOperation() final;
+
+  StringRef getDescription() const override {
+    return "Convert the prepared CIR dialect module to LLVM dialect";
+  }
+
+  StringRef getArgument() const override { return "cir-flat-to-llvm"; }
+};
+
+mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
+    cir::GlobalOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+
+  // Fetch required values to create LLVM op.
+  const mlir::Type cirSymType = op.getSymType();
+
+  // This is the LLVM dialect type.
+  const mlir::Type llvmType = getTypeConverter()->convertType(cirSymType);
+  // FIXME: These default values are placeholders until the the equivalent
+  //        attributes are available on cir.global ops.
+  assert(!cir::MissingFeatures::opGlobalConstant());
+  const bool isConst = false;
+  assert(!cir::MissingFeatures::addressSpace());
+  const unsigned addrSpace = 0;
+  assert(!cir::MissingFeatures::opGlobalDSOLocal());
+  const bool isDsoLocal = true;
+  assert(!cir::MissingFeatures::opGlobalThreadLocal());
+  const bool isThreadLocal = false;
+  assert(!cir::MissingFeatures::opGlobalAlignment());
+  const uint64_t alignment = 0;
+  assert(!cir::MissingFeatures::opGlobalLinkage());
+  const mlir::LLVM::Linkage linkage = mlir::LLVM::Linkage::External;
+  const StringRef symbol = op.getSymName();
+  std::optional<mlir::Attribute> init = op.getInitialValue();
+
+  SmallVector<mlir::NamedAttribute> attributes;
+
+  if (init.has_value()) {
+    if (const auto fltAttr = mlir::dyn_cast<cir::FPAttr>(init.value())) {
+      // Initializer is a constant floating-point number: convert to MLIR
+      // builtin constant.
+      init = rewriter.getFloatAttr(llvmType, fltAttr.getValue());
+    } else if (const auto intAttr =
+                   mlir::dyn_cast<cir::IntAttr>(init.value())) {
+      // Initializer is a constant array: convert it to a compatible llvm init.
+      init = rewriter.getIntegerAttr(llvmType, intAttr.getValue());
+    } else {
+      op.emitError() << "unsupported initializer '" << init.value() << "'";
+      return mlir::failure();
+    }
+  }
+
+  // Rewrite op.
+  rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
+      op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()),
+      alignment, addrSpace, isDsoLocal, isThreadLocal,
+      /*comdat=*/mlir::SymbolRefAttr(), attributes);
+
+  return mlir::success();
+}
+
+static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
+                                 mlir::DataLayout &dataLayout) {
+  converter.addConversion([&](cir::IntType type) -> mlir::Type {
+    // LLVM doesn't work with signed types, so we drop the CIR signs here.
+    return mlir::IntegerType::get(type.getContext(), type.getWidth());
+  });
+  converter.addConversion([&](cir::SingleType type) -> mlir::Type {
+    return mlir::Float32Type::get(type.getContext());
+  });
+  converter.addConversion([&](cir::DoubleType type) -> mlir::Type {
+    return mlir::Float64Type::get(type.getContext());
+  });
+  converter.addConversion([&](cir::FP80Type type) -> mlir::Type {
+    return mlir::Float80Type::get(type.getContext());
+  });
+  converter.addConversion([&](cir::FP128Type type) -> mlir::Type {
+    return mlir::Float128Type::get(type.getContext());
+  });
+  converter.addConversion([&](cir::LongDoubleType type) -> mlir::Type {
+    return converter.convertType(type.getUnderlying());
+  });
+  converter.addConversion([&](cir::FP16Type type) -> mlir::Type {
+    return mlir::Float16Type::get(type.getContext());
+  });
+  converter.addConversion([&](cir::BF16Type type) -> mlir::Type {
+    return mlir::BFloat16Type::get(type.getContext());
+  });
+}
+
+void ConvertCIRToLLVMPass::runOnOperation() {
+  llvm::TimeTraceScope scope("Convert CIR to LLVM Pass");
+
+  mlir::ModuleOp module = getOperation();
+  mlir::DataLayout dl(module);
+  mlir::LLVMTypeConverter converter(&getContext());
+  prepareTypeConverter(converter, dl);
+
+  mlir::RewritePatternSet patterns(&getContext());
+
+  patterns.add<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl);
+
+  mlir::ConversionTarget target(getContext());
+  target.addLegalOp<mlir::ModuleOp>();
+  target.addLegalDialect<mlir::LLVM::LLVMDialect>();
+  target.addIllegalDialect<mlir::BuiltinDialect, cir::CIRDialect,
+                           mlir::func::FuncDialect>();
+
+  if (failed(applyPartialConversion(module, target, std::move(patterns))))
+    signalPassFailure();
+}
+
+static std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
+  return std::make_unique<ConvertCIRToLLVMPass>();
+}
+
+static void populateCIRToLLVMPasses(mlir::OpPassManager &pm) {
+  pm.addPass(createConvertCIRToLLVMPass());
+}
+
 std::unique_ptr<llvm::Module>
 lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx) {
   llvm::TimeTraceScope scope("lower from CIR to LLVM directly");
 
-  std::optional<StringRef> moduleName = mlirModule.getName();
-  auto llvmModule = std::make_unique<llvm::Module>(
-      moduleName ? *moduleName : "CIRToLLVMModule", llvmCtx);
+  mlir::MLIRContext *mlirCtx = mlirModule.getContext();
+
+  mlir::PassManager pm(mlirCtx);
+  populateCIRToLLVMPasses(pm);
+
+  if (mlir::failed(pm.run(mlirModule))) {
+    // FIXME: Handle any errors where they occurs and return a nullptr here.
+    report_fatal_error(
+        "The pass manager failed to lower CIR to LLVMIR dialect!");
+  }
+
+  mlir::registerBuiltinDialectTranslation(*mlirCtx);
+  mlir::registerLLVMDialectTranslation(*mlirCtx);
+
+  llvm::TimeTraceScope translateScope("translateModuleToLLVMIR");
+
+  StringRef moduleName = mlirModule.getName().value_or("CIRToLLVMModule");
+  std::unique_ptr<llvm::Module> llvmModule =
+      mlir::translateModuleToLLVMIR(mlirModule, llvmCtx, moduleName);
 
-  if (!llvmModule)
+  if (!llvmModule) {
+    // FIXME: Handle any errors where they occurs and return a nullptr here.
     report_fatal_error("Lowering from LLVMIR dialect to llvm IR failed!");
+  }
 
   return llvmModule;
 }

diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
new file mode 100644
index 00000000000000..6167ff39b5ad6a
--- /dev/null
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -0,0 +1,42 @@
+//====- LowerToLLVM.h- Lowering from CIR to LLVM --------------------------===//
+//
+// 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 converting CIR modules to LLVM IR.
+//
+//===----------------------------------------------------------------------===//
+#ifndef CLANG_CIR_LOWERTOLLVM_H
+#define CLANG_CIR_LOWERTOLLVM_H
+
+#include "mlir/Transforms/DialectConversion.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+
+namespace cir {
+
+namespace direct {
+
+class CIRToLLVMGlobalOpLowering
+    : public mlir::OpConversionPattern<cir::GlobalOp> {
+  const mlir::DataLayout &dataLayout;
+
+public:
+  CIRToLLVMGlobalOpLowering(const mlir::TypeConverter &typeConverter,
+                            mlir::MLIRContext *context,
+                            const mlir::DataLayout &dataLayout)
+      : OpConversionPattern(typeConverter, context), dataLayout(dataLayout) {
+    setHasBoundedRewriteRecursion();
+  }
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::GlobalOp op, OpAdaptor adaptor,
+                  mlir::ConversionPatternRewriter &rewriter) const override;
+};
+
+} // namespace direct
+} // namespace cir
+
+#endif // CLANG_CIR_LOWERTOLLVM_H

diff  --git a/clang/test/CIR/Lowering/global-var-simple.cpp b/clang/test/CIR/Lowering/global-var-simple.cpp
new file mode 100644
index 00000000000000..06050e409d5440
--- /dev/null
+++ b/clang/test/CIR/Lowering/global-var-simple.cpp
@@ -0,0 +1,81 @@
+// Global variables of intergal types
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o -  | FileCheck %s
+
+// Note: Currently unsupported features include default zero-initialization
+//       and alignment. The fact that "external" is only printed for globals
+//       without an initializer is a quirk of the LLVM AsmWriter.
+
+char c;
+// CHECK: @c = external dso_local global i8
+
+signed char sc;
+// CHECK: @sc = external dso_local global i8
+
+unsigned char uc;
+// CHECK: @uc = external dso_local global i8
+
+short ss;
+// CHECK: @ss = external dso_local global i16
+
+unsigned short us = 100;
+// CHECK: @us = dso_local global i16 100
+
+int si = 42;
+// CHECK: @si = dso_local global i32 42
+
+unsigned ui;
+// CHECK: @ui = external dso_local global i32
+
+long sl;
+// CHECK: @sl = external dso_local global i64
+
+unsigned long ul;
+// CHECK: @ul = external dso_local global i64
+
+long long sll;
+// CHECK: @sll = external dso_local global i64
+
+unsigned long long ull = 123456;
+// CHECK: @ull = dso_local global i64 123456
+
+__int128 s128;
+// CHECK: @s128 = external dso_local global i128
+
+unsigned __int128 u128;
+// CHECK: @u128 = external dso_local global i128
+
+wchar_t wc;
+// CHECK: @wc = external dso_local global i32
+
+char8_t c8;
+// CHECK: @c8 = external dso_local global i8
+
+char16_t c16;
+// CHECK: @c16 = external dso_local global i16
+
+char32_t c32;
+// CHECK: @c32 = external dso_local global i32
+
+_BitInt(20) sb20;
+// CHECK: @sb20 = external dso_local global i20
+
+unsigned _BitInt(48) ub48;
+// CHECK: @ub48 = external dso_local global i48
+
+_Float16 f16;
+// CHECK: @f16 = external dso_local global half
+
+__bf16 bf16;
+// CHECK: @bf16 = external dso_local global bfloat
+
+float f;
+// CHECK: @f = external dso_local global float
+
+double d = 1.25;
+// CHECK: @d = dso_local global double 1.250000e+00
+
+long double ld;
+// CHECK: @ld = external dso_local global x86_fp80
+
+__float128 f128;
+// CHECK: @f128 = external dso_local global fp128

diff  --git a/clang/test/CIR/Lowering/hello.c b/clang/test/CIR/Lowering/hello.c
index 320041f0ab7dc9..ff78b6e6f6a5e2 100644
--- a/clang/test/CIR/Lowering/hello.c
+++ b/clang/test/CIR/Lowering/hello.c
@@ -1,8 +1,10 @@
 // Smoke test for ClangIR-to-LLVM IR code generation
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o -  | FileCheck %s
 
-// TODO: Add checks when proper lowering is implemented.
-//       For now, we're just creating an empty module.
-// CHECK: ModuleID
+int a;
 
-void foo() {}
+// CHECK: @a = external dso_local global i32
+
+int b = 2;
+
+// CHECK: @b = dso_local global i32 2


        


More information about the cfe-commits mailing list