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 ®istry) 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits