Skip to content

Commit 622ee03

Browse files
authored
[CIR] Initial implementation of CIR-to-LLVM IR lowering pass (llvm#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.
1 parent 89001d1 commit 622ee03

File tree

7 files changed

+342
-11
lines changed

7 files changed

+342
-11
lines changed

clang/include/clang/CIR/LowerToLLVM.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
#ifndef CLANG_CIR_LOWERTOLLVM_H
1313
#define CLANG_CIR_LOWERTOLLVM_H
1414

15-
#include "mlir/Pass/Pass.h"
16-
1715
#include <memory>
1816

1917
namespace llvm {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===---- MissingFeatures.h - Checks for unimplemented features -*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file introduces some helper classes to guard against features that
10+
// CIR dialect supports that we do not have and also do not have great ways to
11+
// assert against.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef CLANG_CIR_MISSINGFEATURES_H
16+
#define CLANG_CIR_MISSINGFEATURES_H
17+
18+
namespace cir {
19+
20+
// As a way to track features that haven't yet been implemented this class
21+
// explicitly contains a list of static fns that will return false that you
22+
// can guard against. If and when a feature becomes implemented simply changing
23+
// this return to true will cause compilation to fail at all the points in which
24+
// we noted that we needed to address. This is a much more explicit way to
25+
// handle "TODO"s.
26+
struct MissingFeatures {
27+
// Address space related
28+
static bool addressSpace() { return false; }
29+
30+
// Unhandled global/linkage information.
31+
static bool opGlobalDSOLocal() { return false; }
32+
static bool opGlobalThreadLocal() { return false; }
33+
static bool opGlobalConstant() { return false; }
34+
static bool opGlobalAlignment() { return false; }
35+
static bool opGlobalLinkage() { return false; }
36+
};
37+
38+
} // namespace cir
39+
40+
#endif // CLANG_CIR_MISSINGFEATURES_H

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ 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
810

911
LINK_LIBS
1012
MLIRIR
13+
${dialect_libs}
14+
MLIRCIR
15+
MLIRBuiltinToLLVMIRTranslation
16+
MLIRLLVMToLLVMIRTranslation
1117
)

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

Lines changed: 167 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,22 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "clang/CIR/LowerToLLVM.h"
13+
#include "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"
28+
#include "clang/CIR/MissingFeatures.h"
1629
#include "llvm/IR/Module.h"
1730
#include "llvm/Support/TimeProfiler.h"
1831

@@ -22,16 +35,165 @@ using namespace llvm;
2235
namespace cir {
2336
namespace direct {
2437

38+
// This pass requires the CIR to be in a "flat" state. All blocks in each
39+
// function must belong to the parent region. Once scopes and control flow
40+
// are implemented in CIR, a pass will be run before this one to flatten
41+
// the CIR and get it into the state that this pass requires.
42+
struct ConvertCIRToLLVMPass
43+
: public mlir::PassWrapper<ConvertCIRToLLVMPass,
44+
mlir::OperationPass<mlir::ModuleOp>> {
45+
void getDependentDialects(mlir::DialectRegistry &registry) const override {
46+
registry.insert<mlir::BuiltinDialect, mlir::DLTIDialect,
47+
mlir::LLVM::LLVMDialect, mlir::func::FuncDialect>();
48+
}
49+
void runOnOperation() final;
50+
51+
StringRef getDescription() const override {
52+
return "Convert the prepared CIR dialect module to LLVM dialect";
53+
}
54+
55+
StringRef getArgument() const override { return "cir-flat-to-llvm"; }
56+
};
57+
58+
mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
59+
cir::GlobalOp op, OpAdaptor adaptor,
60+
mlir::ConversionPatternRewriter &rewriter) const {
61+
62+
// Fetch required values to create LLVM op.
63+
const mlir::Type cirSymType = op.getSymType();
64+
65+
// This is the LLVM dialect type.
66+
const mlir::Type llvmType = getTypeConverter()->convertType(cirSymType);
67+
// FIXME: These default values are placeholders until the the equivalent
68+
// attributes are available on cir.global ops.
69+
assert(!cir::MissingFeatures::opGlobalConstant());
70+
const bool isConst = false;
71+
assert(!cir::MissingFeatures::addressSpace());
72+
const unsigned addrSpace = 0;
73+
assert(!cir::MissingFeatures::opGlobalDSOLocal());
74+
const bool isDsoLocal = true;
75+
assert(!cir::MissingFeatures::opGlobalThreadLocal());
76+
const bool isThreadLocal = false;
77+
assert(!cir::MissingFeatures::opGlobalAlignment());
78+
const uint64_t alignment = 0;
79+
assert(!cir::MissingFeatures::opGlobalLinkage());
80+
const mlir::LLVM::Linkage linkage = mlir::LLVM::Linkage::External;
81+
const StringRef symbol = op.getSymName();
82+
std::optional<mlir::Attribute> init = op.getInitialValue();
83+
84+
SmallVector<mlir::NamedAttribute> attributes;
85+
86+
if (init.has_value()) {
87+
if (const auto fltAttr = mlir::dyn_cast<cir::FPAttr>(init.value())) {
88+
// Initializer is a constant floating-point number: convert to MLIR
89+
// builtin constant.
90+
init = rewriter.getFloatAttr(llvmType, fltAttr.getValue());
91+
} else if (const auto intAttr =
92+
mlir::dyn_cast<cir::IntAttr>(init.value())) {
93+
// Initializer is a constant array: convert it to a compatible llvm init.
94+
init = rewriter.getIntegerAttr(llvmType, intAttr.getValue());
95+
} else {
96+
op.emitError() << "unsupported initializer '" << init.value() << "'";
97+
return mlir::failure();
98+
}
99+
}
100+
101+
// Rewrite op.
102+
rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
103+
op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()),
104+
alignment, addrSpace, isDsoLocal, isThreadLocal,
105+
/*comdat=*/mlir::SymbolRefAttr(), attributes);
106+
107+
return mlir::success();
108+
}
109+
110+
static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
111+
mlir::DataLayout &dataLayout) {
112+
converter.addConversion([&](cir::IntType type) -> mlir::Type {
113+
// LLVM doesn't work with signed types, so we drop the CIR signs here.
114+
return mlir::IntegerType::get(type.getContext(), type.getWidth());
115+
});
116+
converter.addConversion([&](cir::SingleType type) -> mlir::Type {
117+
return mlir::Float32Type::get(type.getContext());
118+
});
119+
converter.addConversion([&](cir::DoubleType type) -> mlir::Type {
120+
return mlir::Float64Type::get(type.getContext());
121+
});
122+
converter.addConversion([&](cir::FP80Type type) -> mlir::Type {
123+
return mlir::Float80Type::get(type.getContext());
124+
});
125+
converter.addConversion([&](cir::FP128Type type) -> mlir::Type {
126+
return mlir::Float128Type::get(type.getContext());
127+
});
128+
converter.addConversion([&](cir::LongDoubleType type) -> mlir::Type {
129+
return converter.convertType(type.getUnderlying());
130+
});
131+
converter.addConversion([&](cir::FP16Type type) -> mlir::Type {
132+
return mlir::Float16Type::get(type.getContext());
133+
});
134+
converter.addConversion([&](cir::BF16Type type) -> mlir::Type {
135+
return mlir::BFloat16Type::get(type.getContext());
136+
});
137+
}
138+
139+
void ConvertCIRToLLVMPass::runOnOperation() {
140+
llvm::TimeTraceScope scope("Convert CIR to LLVM Pass");
141+
142+
mlir::ModuleOp module = getOperation();
143+
mlir::DataLayout dl(module);
144+
mlir::LLVMTypeConverter converter(&getContext());
145+
prepareTypeConverter(converter, dl);
146+
147+
mlir::RewritePatternSet patterns(&getContext());
148+
149+
patterns.add<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl);
150+
151+
mlir::ConversionTarget target(getContext());
152+
target.addLegalOp<mlir::ModuleOp>();
153+
target.addLegalDialect<mlir::LLVM::LLVMDialect>();
154+
target.addIllegalDialect<mlir::BuiltinDialect, cir::CIRDialect,
155+
mlir::func::FuncDialect>();
156+
157+
if (failed(applyPartialConversion(module, target, std::move(patterns))))
158+
signalPassFailure();
159+
}
160+
161+
static std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
162+
return std::make_unique<ConvertCIRToLLVMPass>();
163+
}
164+
165+
static void populateCIRToLLVMPasses(mlir::OpPassManager &pm) {
166+
pm.addPass(createConvertCIRToLLVMPass());
167+
}
168+
25169
std::unique_ptr<llvm::Module>
26170
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx) {
27171
llvm::TimeTraceScope scope("lower from CIR to LLVM directly");
28172

29-
std::optional<StringRef> moduleName = mlirModule.getName();
30-
auto llvmModule = std::make_unique<llvm::Module>(
31-
moduleName ? *moduleName : "CIRToLLVMModule", llvmCtx);
173+
mlir::MLIRContext *mlirCtx = mlirModule.getContext();
174+
175+
mlir::PassManager pm(mlirCtx);
176+
populateCIRToLLVMPasses(pm);
177+
178+
if (mlir::failed(pm.run(mlirModule))) {
179+
// FIXME: Handle any errors where they occurs and return a nullptr here.
180+
report_fatal_error(
181+
"The pass manager failed to lower CIR to LLVMIR dialect!");
182+
}
183+
184+
mlir::registerBuiltinDialectTranslation(*mlirCtx);
185+
mlir::registerLLVMDialectTranslation(*mlirCtx);
186+
187+
llvm::TimeTraceScope translateScope("translateModuleToLLVMIR");
188+
189+
StringRef moduleName = mlirModule.getName().value_or("CIRToLLVMModule");
190+
std::unique_ptr<llvm::Module> llvmModule =
191+
mlir::translateModuleToLLVMIR(mlirModule, llvmCtx, moduleName);
32192

33-
if (!llvmModule)
193+
if (!llvmModule) {
194+
// FIXME: Handle any errors where they occurs and return a nullptr here.
34195
report_fatal_error("Lowering from LLVMIR dialect to llvm IR failed!");
196+
}
35197

36198
return llvmModule;
37199
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//====- LowerToLLVM.h- Lowering from CIR to LLVM --------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file declares an interface for converting CIR modules to LLVM IR.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
#ifndef CLANG_CIR_LOWERTOLLVM_H
13+
#define CLANG_CIR_LOWERTOLLVM_H
14+
15+
#include "mlir/Transforms/DialectConversion.h"
16+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
17+
18+
namespace cir {
19+
20+
namespace direct {
21+
22+
class CIRToLLVMGlobalOpLowering
23+
: public mlir::OpConversionPattern<cir::GlobalOp> {
24+
const mlir::DataLayout &dataLayout;
25+
26+
public:
27+
CIRToLLVMGlobalOpLowering(const mlir::TypeConverter &typeConverter,
28+
mlir::MLIRContext *context,
29+
const mlir::DataLayout &dataLayout)
30+
: OpConversionPattern(typeConverter, context), dataLayout(dataLayout) {
31+
setHasBoundedRewriteRecursion();
32+
}
33+
34+
mlir::LogicalResult
35+
matchAndRewrite(cir::GlobalOp op, OpAdaptor adaptor,
36+
mlir::ConversionPatternRewriter &rewriter) const override;
37+
};
38+
39+
} // namespace direct
40+
} // namespace cir
41+
42+
#endif // CLANG_CIR_LOWERTOLLVM_H
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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
64+
65+
_Float16 f16;
66+
// CHECK: @f16 = external dso_local global half
67+
68+
__bf16 bf16;
69+
// CHECK: @bf16 = external dso_local global bfloat
70+
71+
float f;
72+
// CHECK: @f = external dso_local global float
73+
74+
double d = 1.25;
75+
// CHECK: @d = dso_local global double 1.250000e+00
76+
77+
long double ld;
78+
// CHECK: @ld = external dso_local global x86_fp80
79+
80+
__float128 f128;
81+
// CHECK: @f128 = external dso_local global fp128

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)