Skip to content

Commit 2b5cc89

Browse files
authored
[CIR] Lowering to LLVM for global pointers (#125619)
Add support for lowering global variables of any pointer type to LLVM IR.
1 parent dd04dc6 commit 2b5cc89

File tree

5 files changed

+232
-11
lines changed

5 files changed

+232
-11
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===- CIRAttrVisitor.h - Visitor for CIR attributes ------------*- 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 defines the CirAttrVisitor interface.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_CIR_DIALECT_IR_CIRATTRVISITOR_H
14+
#define LLVM_CLANG_CIR_DIALECT_IR_CIRATTRVISITOR_H
15+
16+
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
17+
18+
namespace cir {
19+
20+
template <typename ImplClass, typename RetTy> class CirAttrVisitor {
21+
public:
22+
// FIXME: Create a TableGen list to automatically handle new attributes
23+
RetTy visit(mlir::Attribute attr) {
24+
if (const auto intAttr = mlir::dyn_cast<cir::IntAttr>(attr))
25+
return getImpl().visitCirIntAttr(intAttr);
26+
if (const auto fltAttr = mlir::dyn_cast<cir::FPAttr>(attr))
27+
return getImpl().visitCirFPAttr(fltAttr);
28+
if (const auto ptrAttr = mlir::dyn_cast<cir::ConstPtrAttr>(attr))
29+
return getImpl().visitCirConstPtrAttr(ptrAttr);
30+
llvm_unreachable("unhandled attribute type");
31+
}
32+
33+
// If the implementation chooses not to implement a certain visit
34+
// method, fall back to the parent.
35+
RetTy visitCirIntAttr(cir::IntAttr attr) {
36+
return getImpl().visitCirAttr(attr);
37+
}
38+
RetTy visitCirFPAttr(cir::FPAttr attr) {
39+
return getImpl().visitCirAttr(attr);
40+
}
41+
RetTy visitCirConstPtrAttr(cir::ConstPtrAttr attr) {
42+
return getImpl().visitCirAttr(attr);
43+
}
44+
45+
RetTy visitCirAttr(mlir::Attribute attr) { return RetTy(); }
46+
47+
ImplClass &getImpl() { return *static_cast<ImplClass *>(this); }
48+
};
49+
50+
} // namespace cir
51+
52+
#endif // LLVM_CLANG_CIR_DIALECT_IR_CIRATTRVISITOR_H

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ struct MissingFeatures {
2727
// Address space related
2828
static bool addressSpace() { return false; }
2929

30+
// This isn't needed until we add support for bools.
31+
static bool convertTypeForMemory() { return false; }
32+
3033
// Unhandled global/linkage information.
3134
static bool opGlobalDSOLocal() { return false; }
3235
static bool opGlobalThreadLocal() { return false; }

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

Lines changed: 146 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
2525
#include "mlir/Target/LLVMIR/Export.h"
2626
#include "mlir/Transforms/DialectConversion.h"
27+
#include "clang/CIR/Dialect/IR/CIRAttrVisitor.h"
2728
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2829
#include "clang/CIR/MissingFeatures.h"
2930
#include "llvm/IR/Module.h"
@@ -35,6 +36,71 @@ using namespace llvm;
3536
namespace cir {
3637
namespace direct {
3738

39+
class CIRAttrToValue : public CirAttrVisitor<CIRAttrToValue, mlir::Value> {
40+
public:
41+
CIRAttrToValue(mlir::Operation *parentOp,
42+
mlir::ConversionPatternRewriter &rewriter,
43+
const mlir::TypeConverter *converter)
44+
: parentOp(parentOp), rewriter(rewriter), converter(converter) {}
45+
46+
mlir::Value lowerCirAttrAsValue(mlir::Attribute attr) { return visit(attr); }
47+
48+
mlir::Value visitCirIntAttr(cir::IntAttr intAttr) {
49+
mlir::Location loc = parentOp->getLoc();
50+
return rewriter.create<mlir::LLVM::ConstantOp>(
51+
loc, converter->convertType(intAttr.getType()), intAttr.getValue());
52+
}
53+
54+
mlir::Value visitCirFPAttr(cir::FPAttr fltAttr) {
55+
mlir::Location loc = parentOp->getLoc();
56+
return rewriter.create<mlir::LLVM::ConstantOp>(
57+
loc, converter->convertType(fltAttr.getType()), fltAttr.getValue());
58+
}
59+
60+
mlir::Value visitCirConstPtrAttr(cir::ConstPtrAttr ptrAttr) {
61+
mlir::Location loc = parentOp->getLoc();
62+
if (ptrAttr.isNullValue()) {
63+
return rewriter.create<mlir::LLVM::ZeroOp>(
64+
loc, converter->convertType(ptrAttr.getType()));
65+
}
66+
mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
67+
mlir::Value ptrVal = rewriter.create<mlir::LLVM::ConstantOp>(
68+
loc,
69+
rewriter.getIntegerType(layout.getTypeSizeInBits(ptrAttr.getType())),
70+
ptrAttr.getValue().getInt());
71+
return rewriter.create<mlir::LLVM::IntToPtrOp>(
72+
loc, converter->convertType(ptrAttr.getType()), ptrVal);
73+
}
74+
75+
private:
76+
mlir::Operation *parentOp;
77+
mlir::ConversionPatternRewriter &rewriter;
78+
const mlir::TypeConverter *converter;
79+
};
80+
81+
// This class handles rewriting initializer attributes for types that do not
82+
// require region initialization.
83+
class GlobalInitAttrRewriter
84+
: public CirAttrVisitor<GlobalInitAttrRewriter, mlir::Attribute> {
85+
public:
86+
GlobalInitAttrRewriter(mlir::Type type,
87+
mlir::ConversionPatternRewriter &rewriter)
88+
: llvmType(type), rewriter(rewriter) {}
89+
90+
mlir::Attribute rewriteInitAttr(mlir::Attribute attr) { return visit(attr); }
91+
92+
mlir::Attribute visitCirIntAttr(cir::IntAttr attr) {
93+
return rewriter.getIntegerAttr(llvmType, attr.getValue());
94+
}
95+
mlir::Attribute visitCirFPAttr(cir::FPAttr attr) {
96+
return rewriter.getFloatAttr(llvmType, attr.getValue());
97+
}
98+
99+
private:
100+
mlir::Type llvmType;
101+
mlir::ConversionPatternRewriter &rewriter;
102+
};
103+
38104
// This pass requires the CIR to be in a "flat" state. All blocks in each
39105
// function must belong to the parent region. Once scopes and control flow
40106
// are implemented in CIR, a pass will be run before this one to flatten
@@ -55,14 +121,81 @@ struct ConvertCIRToLLVMPass
55121
StringRef getArgument() const override { return "cir-flat-to-llvm"; }
56122
};
57123

124+
bool CIRToLLVMGlobalOpLowering::attrRequiresRegionInitialization(
125+
mlir::Attribute attr) const {
126+
// There will be more cases added later.
127+
return isa<cir::ConstPtrAttr>(attr);
128+
}
129+
130+
/// Replace CIR global with a region initialized LLVM global and update
131+
/// insertion point to the end of the initializer block.
132+
void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp(
133+
cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const {
134+
assert(!cir::MissingFeatures::convertTypeForMemory());
135+
const mlir::Type llvmType = getTypeConverter()->convertType(op.getSymType());
136+
137+
// FIXME: These default values are placeholders until the the equivalent
138+
// attributes are available on cir.global ops. This duplicates code
139+
// in CIRToLLVMGlobalOpLowering::matchAndRewrite() but that will go
140+
// away when the placeholders are no longer needed.
141+
assert(!cir::MissingFeatures::opGlobalConstant());
142+
const bool isConst = false;
143+
assert(!cir::MissingFeatures::addressSpace());
144+
const unsigned addrSpace = 0;
145+
assert(!cir::MissingFeatures::opGlobalDSOLocal());
146+
const bool isDsoLocal = true;
147+
assert(!cir::MissingFeatures::opGlobalThreadLocal());
148+
const bool isThreadLocal = false;
149+
assert(!cir::MissingFeatures::opGlobalAlignment());
150+
const uint64_t alignment = 0;
151+
assert(!cir::MissingFeatures::opGlobalLinkage());
152+
const mlir::LLVM::Linkage linkage = mlir::LLVM::Linkage::External;
153+
const StringRef symbol = op.getSymName();
154+
155+
SmallVector<mlir::NamedAttribute> attributes;
156+
auto newGlobalOp = rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
157+
op, llvmType, isConst, linkage, symbol, nullptr, alignment, addrSpace,
158+
isDsoLocal, isThreadLocal,
159+
/*comdat=*/mlir::SymbolRefAttr(), attributes);
160+
newGlobalOp.getRegion().push_back(new mlir::Block());
161+
rewriter.setInsertionPointToEnd(newGlobalOp.getInitializerBlock());
162+
};
163+
164+
mlir::LogicalResult
165+
CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
166+
cir::GlobalOp op, mlir::Attribute init,
167+
mlir::ConversionPatternRewriter &rewriter) const {
168+
// TODO: Generalize this handling when more types are needed here.
169+
assert(isa<cir::ConstPtrAttr>(init));
170+
171+
// TODO(cir): once LLVM's dialect has proper equivalent attributes this
172+
// should be updated. For now, we use a custom op to initialize globals
173+
// to the appropriate value.
174+
const mlir::Location loc = op.getLoc();
175+
setupRegionInitializedLLVMGlobalOp(op, rewriter);
176+
CIRAttrToValue attrVisitor(op, rewriter, typeConverter);
177+
mlir::Value value = attrVisitor.lowerCirAttrAsValue(init);
178+
rewriter.create<mlir::LLVM::ReturnOp>(loc, value);
179+
return mlir::success();
180+
}
181+
58182
mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
59183
cir::GlobalOp op, OpAdaptor adaptor,
60184
mlir::ConversionPatternRewriter &rewriter) const {
61185

186+
std::optional<mlir::Attribute> init = op.getInitialValue();
187+
188+
// If we have an initializer and it requires region initialization, handle
189+
// that separately
190+
if (init.has_value() && attrRequiresRegionInitialization(init.value())) {
191+
return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter);
192+
}
193+
62194
// Fetch required values to create LLVM op.
63195
const mlir::Type cirSymType = op.getSymType();
64196

65197
// This is the LLVM dialect type.
198+
assert(!cir::MissingFeatures::convertTypeForMemory());
66199
const mlir::Type llvmType = getTypeConverter()->convertType(cirSymType);
67200
// FIXME: These default values are placeholders until the the equivalent
68201
// attributes are available on cir.global ops.
@@ -79,20 +212,15 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
79212
assert(!cir::MissingFeatures::opGlobalLinkage());
80213
const mlir::LLVM::Linkage linkage = mlir::LLVM::Linkage::External;
81214
const StringRef symbol = op.getSymName();
82-
std::optional<mlir::Attribute> init = op.getInitialValue();
83-
84215
SmallVector<mlir::NamedAttribute> attributes;
85216

86217
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 {
218+
GlobalInitAttrRewriter initRewriter(llvmType, rewriter);
219+
init = initRewriter.rewriteInitAttr(init.value());
220+
// If initRewriter returned a null attribute, init will have a value but
221+
// the value will be null. If that happens, initRewriter didn't handle the
222+
// attribute type. It probably needs to be added to GlobalInitAttrRewriter.
223+
if (!init.value()) {
96224
op.emitError() << "unsupported initializer '" << init.value() << "'";
97225
return mlir::failure();
98226
}
@@ -109,6 +237,13 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
109237

110238
static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
111239
mlir::DataLayout &dataLayout) {
240+
converter.addConversion([&](cir::PointerType type) -> mlir::Type {
241+
// Drop pointee type since LLVM dialect only allows opaque pointers.
242+
assert(!cir::MissingFeatures::addressSpace());
243+
unsigned targetAS = 0;
244+
245+
return mlir::LLVM::LLVMPointerType::get(type.getContext(), targetAS);
246+
});
112247
converter.addConversion([&](cir::IntType type) -> mlir::Type {
113248
// LLVM doesn't work with signed types, so we drop the CIR signs here.
114249
return mlir::IntegerType::get(type.getContext(), type.getWidth());

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ class CIRToLLVMGlobalOpLowering
3434
mlir::LogicalResult
3535
matchAndRewrite(cir::GlobalOp op, OpAdaptor adaptor,
3636
mlir::ConversionPatternRewriter &rewriter) const override;
37+
38+
private:
39+
bool attrRequiresRegionInitialization(mlir::Attribute attr) const;
40+
41+
mlir::LogicalResult matchAndRewriteRegionInitializedGlobal(
42+
cir::GlobalOp op, mlir::Attribute init,
43+
mlir::ConversionPatternRewriter &rewriter) const;
44+
45+
void setupRegionInitializedLLVMGlobalOp(
46+
cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const;
3747
};
3848

3949
} // namespace direct

clang/test/CIR/Lowering/global-var-simple.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,24 @@ long double ld;
7979

8080
__float128 f128;
8181
// CHECK: @f128 = external dso_local global fp128
82+
83+
void *vp;
84+
// CHECK: @vp = external dso_local global ptr{{$}}
85+
86+
int *ip = 0;
87+
// CHECK: @ip = dso_local global ptr null
88+
89+
double *dp;
90+
// CHECK: @dp = external dso_local global ptr{{$}}
91+
92+
char **cpp;
93+
// CHECK: @cpp = external dso_local global ptr{{$}}
94+
95+
void (*fp)();
96+
// CHECK: @fp = external dso_local global ptr{{$}}
97+
98+
int (*fpii)(int) = 0;
99+
// CHECK: @fpii = dso_local global ptr null
100+
101+
void (*fpvar)(int, ...);
102+
// CHECK: @fpvar = external dso_local global ptr{{$}}

0 commit comments

Comments
 (0)