Skip to content

Commit 4801886

Browse files
committed
[CIR] Initial implementation of CIR-to-LLVM IR lowering pass
This change introduces lowering from CIR to LLVM IR of global integer variables, using defaults for attributes that aren't yet implemented.
1 parent b2447a2 commit 4801886

File tree

5 files changed

+225
-7
lines changed

5 files changed

+225
-7
lines changed

clang/include/clang/CIR/LowerToLLVM.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
#ifndef CLANG_CIR_LOWERTOLLVM_H
1313
#define CLANG_CIR_LOWERTOLLVM_H
1414

15-
#include "mlir/Pass/Pass.h"
15+
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
16+
#include "mlir/Transforms/DialectConversion.h"
17+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
1618

1719
#include <memory>
1820

@@ -31,6 +33,24 @@ namespace direct {
3133
std::unique_ptr<llvm::Module>
3234
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule,
3335
llvm::LLVMContext &llvmCtx);
36+
37+
class CIRToLLVMGlobalOpLowering
38+
: public mlir::OpConversionPattern<cir::GlobalOp> {
39+
mlir::DataLayout const &dataLayout;
40+
41+
public:
42+
CIRToLLVMGlobalOpLowering(const mlir::TypeConverter &typeConverter,
43+
mlir::MLIRContext *context,
44+
mlir::DataLayout const &dataLayout)
45+
: OpConversionPattern(typeConverter, context), dataLayout(dataLayout) {
46+
setHasBoundedRewriteRecursion();
47+
}
48+
49+
mlir::LogicalResult
50+
matchAndRewrite(cir::GlobalOp op, OpAdaptor adaptor,
51+
mlir::ConversionPatternRewriter &rewriter) const override;
52+
};
53+
3454
} // namespace direct
3555
} // namespace cir
3656

clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ set(LLVM_LINK_COMPONENTS
33
Support
44
)
55

6+
get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
7+
68
add_clang_library(clangCIRLoweringDirectToLLVM
79
LowerToLLVM.cpp
10+
11+
LINK_LIBS
12+
${dialect_libs}
13+
MLIRBuiltinToLLVMIRTranslation
14+
MLIRLLVMToLLVMIRTranslation
815
)

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,19 @@
1212

1313
#include "clang/CIR/LowerToLLVM.h"
1414

15+
#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
16+
#include "mlir/Dialect/DLTI/DLTI.h"
17+
#include "mlir/Dialect/Func/IR/FuncOps.h"
18+
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
19+
#include "mlir/IR/BuiltinDialect.h"
1520
#include "mlir/IR/BuiltinOps.h"
21+
#include "mlir/Pass/Pass.h"
22+
#include "mlir/Pass/PassManager.h"
23+
#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h"
24+
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
25+
#include "mlir/Target/LLVMIR/Export.h"
26+
#include "mlir/Transforms/DialectConversion.h"
27+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
1628
#include "llvm/IR/Module.h"
1729
#include "llvm/Support/TimeProfiler.h"
1830

@@ -22,13 +34,127 @@ using namespace llvm;
2234
namespace cir {
2335
namespace direct {
2436

37+
struct ConvertCIRToLLVMPass
38+
: public mlir::PassWrapper<ConvertCIRToLLVMPass,
39+
mlir::OperationPass<mlir::ModuleOp>> {
40+
void getDependentDialects(mlir::DialectRegistry &registry) const override {
41+
registry.insert<mlir::BuiltinDialect, mlir::DLTIDialect,
42+
mlir::LLVM::LLVMDialect, mlir::func::FuncDialect>();
43+
}
44+
void runOnOperation() final;
45+
46+
StringRef getDescription() const override {
47+
return "Convert the prepared CIR dialect module to LLVM dialect";
48+
}
49+
50+
StringRef getArgument() const override { return "cir-flat-to-llvm"; }
51+
};
52+
53+
mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
54+
cir::GlobalOp op, OpAdaptor adaptor,
55+
mlir::ConversionPatternRewriter &rewriter) const {
56+
57+
// Fetch required values to create LLVM op.
58+
const mlir::Type cirSymType = op.getSymType();
59+
60+
// This is the LLVM dialect type
61+
const mlir::Type llvmType = getTypeConverter()->convertType(cirSymType);
62+
// These defaults are just here until the equivalent attributes are
63+
// available on cir.global ops.
64+
const bool isConst = false;
65+
const bool isDsoLocal = true;
66+
const mlir::LLVM::Linkage linkage = mlir::LLVM::Linkage::External;
67+
const StringRef symbol = op.getSymName();
68+
std::optional<mlir::Attribute> init = op.getInitialValue();
69+
70+
SmallVector<mlir::NamedAttribute> attributes;
71+
72+
// Check for missing funcionalities.
73+
if (!init.has_value()) {
74+
rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
75+
op, llvmType, isConst, linkage, symbol, mlir::Attribute(),
76+
/*alignment*/ 0, /*addrSpace*/ 0, /*dsoLocal*/ isDsoLocal,
77+
/*threadLocal*/ false, /*comdat*/ mlir::SymbolRefAttr(), attributes);
78+
return mlir::success();
79+
}
80+
81+
// Initializer is a constant array: convert it to a compatible llvm init.
82+
if (auto intAttr = mlir::dyn_cast<cir::IntAttr>(init.value())) {
83+
init = rewriter.getIntegerAttr(llvmType, intAttr.getValue());
84+
} else {
85+
op.emitError() << "unsupported initializer '" << init.value() << "'";
86+
return mlir::failure();
87+
}
88+
89+
// Rewrite op.
90+
rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
91+
op, llvmType, isConst, linkage, symbol, init.value(), /*alignment*/ 0,
92+
/*addrSpace*/ 0, /*dsoLocal*/ isDsoLocal, /*threadLocal*/ false,
93+
/*comdat*/ mlir::SymbolRefAttr(), attributes);
94+
95+
return mlir::success();
96+
}
97+
98+
static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
99+
mlir::DataLayout &dataLayout) {
100+
converter.addConversion([&](cir::IntType type) -> mlir::Type {
101+
// LLVM doesn't work with signed types, so we drop the CIR signs here.
102+
return mlir::IntegerType::get(type.getContext(), type.getWidth());
103+
});
104+
}
105+
106+
void ConvertCIRToLLVMPass::runOnOperation() {
107+
llvm::TimeTraceScope scope("Convert CIR to LLVM Pass");
108+
109+
mlir::ModuleOp module = getOperation();
110+
mlir::DataLayout dl(module);
111+
mlir::LLVMTypeConverter converter(&getContext());
112+
prepareTypeConverter(converter, dl); // , lowerModule.get());
113+
114+
mlir::RewritePatternSet patterns(&getContext());
115+
116+
patterns.add<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl);
117+
118+
mlir::ConversionTarget target(getContext());
119+
target.addLegalOp<mlir::ModuleOp>();
120+
target.addLegalDialect<mlir::LLVM::LLVMDialect>();
121+
target.addIllegalDialect<mlir::BuiltinDialect, cir::CIRDialect,
122+
mlir::func::FuncDialect>();
123+
124+
if (failed(applyPartialConversion(module, target, std::move(patterns))))
125+
signalPassFailure();
126+
}
127+
128+
static std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
129+
return std::make_unique<ConvertCIRToLLVMPass>();
130+
}
131+
132+
static void populateCIRToLLVMPasses(mlir::OpPassManager &pm) {
133+
pm.addPass(createConvertCIRToLLVMPass());
134+
}
135+
25136
std::unique_ptr<llvm::Module>
26137
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx) {
27138
llvm::TimeTraceScope scope("lower from CIR to LLVM directly");
28139

140+
mlir::MLIRContext *mlirCtx = mlirModule.getContext();
141+
142+
mlir::PassManager pm(mlirCtx);
143+
populateCIRToLLVMPasses(pm);
144+
145+
bool result = !mlir::failed(pm.run(mlirModule));
146+
if (!result)
147+
report_fatal_error(
148+
"The pass manager failed to lower CIR to LLVMIR dialect!");
149+
150+
mlir::registerBuiltinDialectTranslation(*mlirCtx);
151+
mlir::registerLLVMDialectTranslation(*mlirCtx);
152+
153+
llvm::TimeTraceScope translateScope("translateModuleToLLVMIR");
154+
29155
std::optional<StringRef> moduleName = mlirModule.getName();
30-
auto llvmModule = std::make_unique<llvm::Module>(
31-
moduleName ? *moduleName : "CIRToLLVMModule", llvmCtx);
156+
std::unique_ptr<llvm::Module> llvmModule = mlir::translateModuleToLLVMIR(
157+
mlirModule, llvmCtx, moduleName ? *moduleName : "CIRToLLVMModule");
32158

33159
if (!llvmModule)
34160
report_fatal_error("Lowering from LLVMIR dialect to llvm IR failed!");
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Global variables of intergal types
2+
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s
3+
4+
// Note: Currently unsupported features include default zero-initialization
5+
// and alignment. The fact that "external" is only printed for globals
6+
// without an initializer is a quirk of the LLVM AsmWriter.
7+
8+
char c;
9+
// CHECK: @c = external dso_local global i8
10+
11+
signed char sc;
12+
// CHECK: @sc = external dso_local global i8
13+
14+
unsigned char uc;
15+
// CHECK: @uc = external dso_local global i8
16+
17+
short ss;
18+
// CHECK: @ss = external dso_local global i16
19+
20+
unsigned short us = 100;
21+
// CHECK: @us = dso_local global i16 100
22+
23+
int si = 42;
24+
// CHECK: @si = dso_local global i32 42
25+
26+
unsigned ui;
27+
// CHECK: @ui = external dso_local global i32
28+
29+
long sl;
30+
// CHECK: @sl = external dso_local global i64
31+
32+
unsigned long ul;
33+
// CHECK: @ul = external dso_local global i64
34+
35+
long long sll;
36+
// CHECK: @sll = external dso_local global i64
37+
38+
unsigned long long ull = 123456;
39+
// CHECK: @ull = dso_local global i64 123456
40+
41+
__int128 s128;
42+
// CHECK: @s128 = external dso_local global i128
43+
44+
unsigned __int128 u128;
45+
// CHECK: @u128 = external dso_local global i128
46+
47+
wchar_t wc;
48+
// CHECK: @wc = external dso_local global i32
49+
50+
char8_t c8;
51+
// CHECK: @c8 = external dso_local global i8
52+
53+
char16_t c16;
54+
// CHECK: @c16 = external dso_local global i16
55+
56+
char32_t c32;
57+
// CHECK: @c32 = external dso_local global i32
58+
59+
_BitInt(20) sb20;
60+
// CHECK: @sb20 = external dso_local global i20
61+
62+
unsigned _BitInt(48) ub48;
63+
// CHECK: @ub48 = external dso_local global i48

clang/test/CIR/Lowering/hello.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Smoke test for ClangIR-to-LLVM IR code generation
22
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s
33

4-
// TODO: Add checks when proper lowering is implemented.
5-
// For now, we're just creating an empty module.
6-
// CHECK: ModuleID
4+
int a;
75

8-
void foo() {}
6+
// CHECK: @a = external dso_local global i32
7+
8+
int b = 2;
9+
10+
// CHECK: @b = dso_local global i32 2

0 commit comments

Comments
 (0)