Skip to content

Commit 73e2f1a

Browse files
seven-milelanza
authored andcommitted
[CIR][CIRGen][LowerToLLVM] Support address space casting (#652)
* New `CastKind::addrspace_cast` for `cir.cast` * `TargetCIRGenInfo::performAddrSpaceCast` helper for non-constant values only * CIRGen for address space casting of pointers and references * Lowering to LLVM
1 parent 1e3c001 commit 73e2f1a

File tree

13 files changed

+188
-5
lines changed

13 files changed

+188
-5
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

+11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
1717
#include "clang/CIR/Dialect/IR/CIRTypes.h"
1818
#include "clang/CIR/Dialect/IR/FPEnv.h"
19+
#include "clang/CIR/MissingFeatures.h"
1920

2021
#include "mlir/IR/Attributes.h"
2122
#include "mlir/IR/Builders.h"
@@ -328,6 +329,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
328329
return createBitcast(src, getPointerTo(newPointeeTy));
329330
}
330331

332+
mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src,
333+
mlir::Type newTy) {
334+
return createCast(loc, mlir::cir::CastKind::address_space, src, newTy);
335+
}
336+
337+
mlir::Value createAddrSpaceCast(mlir::Value src, mlir::Type newTy) {
338+
return createAddrSpaceCast(src.getLoc(), src, newTy);
339+
}
340+
331341
mlir::Value createPtrIsNull(mlir::Value ptr) {
332342
return createNot(createPtrToBoolCast(ptr));
333343
}
@@ -391,6 +401,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
391401

392402
// Creates constant nullptr for pointer type ty.
393403
mlir::cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) {
404+
assert(!MissingFeatures::targetCodeGenInfoGetNullPointer());
394405
return create<mlir::cir::ConstantOp>(loc, ty, getConstPtrAttr(ty, 0));
395406
}
396407

clang/include/clang/CIR/Dialect/IR/CIROps.td

+4-1
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,16 @@ def CK_FloatToBoolean : I32EnumAttrCase<"float_to_bool", 10>;
6969
def CK_BooleanToIntegral : I32EnumAttrCase<"bool_to_int", 11>;
7070
def CK_IntegralToFloat : I32EnumAttrCase<"int_to_float", 12>;
7171
def CK_BooleanToFloat : I32EnumAttrCase<"bool_to_float", 13>;
72+
def CK_AddressSpaceConversion : I32EnumAttrCase<"address_space", 14>;
7273

7374
def CastKind : I32EnumAttr<
7475
"CastKind",
7576
"cast kind",
7677
[CK_IntegralToBoolean, CK_ArrayToPointerDecay, CK_IntegralCast,
7778
CK_BitCast, CK_FloatingCast, CK_PtrToBoolean, CK_FloatToIntegral,
7879
CK_IntegralToPointer, CK_PointerToIntegral, CK_FloatToBoolean,
79-
CK_BooleanToIntegral, CK_IntegralToFloat, CK_BooleanToFloat]> {
80+
CK_BooleanToIntegral, CK_IntegralToFloat, CK_BooleanToFloat,
81+
CK_AddressSpaceConversion]> {
8082
let cppNamespace = "::mlir::cir";
8183
}
8284

@@ -98,6 +100,7 @@ def CastOp : CIR_Op<"cast", [Pure]> {
98100
- `ptr_to_bool`
99101
- `bool_to_int`
100102
- `bool_to_float`
103+
- `address_space`
101104

102105
This is effectively a subset of the rules from
103106
`llvm-project/clang/include/clang/AST/OperationKinds.def`; but note that some

clang/include/clang/CIR/MissingFeatures.h

+1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ struct MissingFeatures {
155155
static bool checkFunctionCallABI() { return false; }
156156
static bool zeroInitializer() { return false; }
157157
static bool targetCodeGenInfoIsProtoCallVariadic() { return false; }
158+
static bool targetCodeGenInfoGetNullPointer() { return false; }
158159
static bool chainCalls() { return false; }
159160
static bool operandBundles() { return false; }
160161
static bool exceptions() { return false; }

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "CIRGenModule.h"
1818
#include "CIRGenOpenMPRuntime.h"
1919
#include "CIRGenValue.h"
20+
#include "TargetInfo.h"
2021

2122
#include "clang/AST/ExprCXX.h"
2223
#include "clang/AST/GlobalDecl.h"
@@ -1953,7 +1954,15 @@ LValue CIRGenFunction::buildCastLValue(const CastExpr *E) {
19531954
assert(0 && "NYI");
19541955
}
19551956
case CK_AddressSpaceConversion: {
1956-
assert(0 && "NYI");
1957+
LValue LV = buildLValue(E->getSubExpr());
1958+
QualType DestTy = getContext().getPointerType(E->getType());
1959+
mlir::Value V = getTargetHooks().performAddrSpaceCast(
1960+
*this, LV.getPointer(), E->getSubExpr()->getType().getAddressSpace(),
1961+
E->getType().getAddressSpace(), ConvertType(DestTy));
1962+
assert(!MissingFeatures::tbaa());
1963+
return makeAddrLValue(Address(V, getTypes().convertTypeForMem(E->getType()),
1964+
LV.getAddress().getAlignment()),
1965+
E->getType(), LV.getBaseInfo());
19571966
}
19581967
case CK_ObjCObjectLValueCast: {
19591968
assert(0 && "NYI");

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

+19-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "CIRGenFunction.h"
1515
#include "CIRGenModule.h"
1616
#include "CIRGenOpenMPRuntime.h"
17+
#include "TargetInfo.h"
1718
#include "clang/CIR/MissingFeatures.h"
1819

1920
#include "clang/AST/StmtVisitor.h"
@@ -1514,8 +1515,24 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
15141515
return CGF.getBuilder().createBitcast(CGF.getLoc(E->getSourceRange()), Src,
15151516
DstTy);
15161517
}
1517-
case CK_AddressSpaceConversion:
1518-
llvm_unreachable("NYI");
1518+
case CK_AddressSpaceConversion: {
1519+
Expr::EvalResult Result;
1520+
if (E->EvaluateAsRValue(Result, CGF.getContext()) &&
1521+
Result.Val.isNullPointer()) {
1522+
// If E has side effect, it is emitted even if its final result is a
1523+
// null pointer. In that case, a DCE pass should be able to
1524+
// eliminate the useless instructions emitted during translating E.
1525+
if (Result.HasSideEffects) {
1526+
llvm_unreachable("NYI");
1527+
}
1528+
return CGF.CGM.buildNullConstant(DestTy, CGF.getLoc(E->getExprLoc()));
1529+
}
1530+
// Since target may map different address spaces in AST to the same address
1531+
// space, an address space conversion may end up as a bitcast.
1532+
return CGF.CGM.getTargetCIRGenInfo().performAddrSpaceCast(
1533+
CGF, Visit(E), E->getType()->getPointeeType().getAddressSpace(),
1534+
DestTy->getPointeeType().getAddressSpace(), ConvertType(DestTy));
1535+
}
15191536
case CK_AtomicToNonAtomic:
15201537
llvm_unreachable("NYI");
15211538
case CK_NonAtomicToAtomic:

clang/lib/CIR/CodeGen/TargetInfo.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,17 @@ ABIArgInfo X86_64ABIInfo::classifyReturnType(QualType RetTy) const {
419419
return ABIArgInfo::getDirect(ResType);
420420
}
421421

422+
mlir::Value TargetCIRGenInfo::performAddrSpaceCast(
423+
CIRGenFunction &CGF, mlir::Value Src, clang::LangAS SrcAddr,
424+
clang::LangAS DestAddr, mlir::Type DestTy, bool IsNonNull) const {
425+
// Since target may map different address spaces in AST to the same address
426+
// space, an address space conversion may end up as a bitcast.
427+
if (auto globalOp = Src.getDefiningOp<mlir::cir::GlobalOp>())
428+
llvm_unreachable("Global ops addrspace cast NYI");
429+
// Try to preserve the source's name to make IR more readable.
430+
return CGF.getBuilder().createAddrSpaceCast(Src, DestTy);
431+
}
432+
422433
const TargetCIRGenInfo &CIRGenModule::getTargetCIRGenInfo() {
423434
if (TheTargetCIRGenInfo)
424435
return *TheTargetCIRGenInfo;

clang/lib/CIR/CodeGen/TargetInfo.h

+13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
namespace cir {
2424

2525
class CIRGenFunction;
26+
class CIRGenModule;
2627

2728
/// This class organizes various target-specific codegeneration issues, like
2829
/// target-specific attributes, builtins and so on.
@@ -65,6 +66,18 @@ class TargetCIRGenInfo {
6566
return clang::LangAS::Default;
6667
}
6768

69+
/// Perform address space cast of an expression of pointer type.
70+
/// \param V is the value to be casted to another address space.
71+
/// \param SrcAddr is the language address space of \p V.
72+
/// \param DestAddr is the targeted language address space.
73+
/// \param DestTy is the destination pointer type.
74+
/// \param IsNonNull is the flag indicating \p V is known to be non null.
75+
virtual mlir::Value performAddrSpaceCast(CIRGenFunction &CGF, mlir::Value V,
76+
clang::LangAS SrcAddr,
77+
clang::LangAS DestAddr,
78+
mlir::Type DestTy,
79+
bool IsNonNull = false) const;
80+
6881
virtual ~TargetCIRGenInfo() {}
6982
};
7083

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,15 @@ LogicalResult CastOp::verify() {
496496
return emitOpError() << "requires !cir.float type for result";
497497
return success();
498498
}
499+
case cir::CastKind::address_space: {
500+
auto srcPtrTy = srcType.dyn_cast<mlir::cir::PointerType>();
501+
auto resPtrTy = resType.dyn_cast<mlir::cir::PointerType>();
502+
if (!srcPtrTy || !resPtrTy)
503+
return emitOpError() << "requires !cir.ptr type for source and result";
504+
if (srcPtrTy.getPointee() != resPtrTy.getPointee())
505+
return emitOpError() << "requires two types differ in addrspace only";
506+
return success();
507+
}
499508
}
500509

501510
llvm_unreachable("Unknown CastOp kind?");
@@ -514,7 +523,8 @@ OpFoldResult CastOp::fold(FoldAdaptor adaptor) {
514523
return foldResults[0].get<mlir::Attribute>();
515524
return {};
516525
}
517-
case mlir::cir::CastKind::bitcast: {
526+
case mlir::cir::CastKind::bitcast:
527+
case mlir::cir::CastKind::address_space: {
518528
return getSrc();
519529
}
520530
default:

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

+8
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,14 @@ class CIRCastOpLowering : public mlir::OpConversionPattern<mlir::cir::CastOp> {
753753
mlir::cir::CmpOpKind::ne, castOp.getSrc(), null);
754754
break;
755755
}
756+
case mlir::cir::CastKind::address_space: {
757+
auto dstTy = castOp.getType();
758+
auto llvmSrcVal = adaptor.getOperands().front();
759+
auto llvmDstTy = getTypeConverter()->convertType(dstTy);
760+
rewriter.replaceOpWithNewOp<mlir::LLVM::AddrSpaceCastOp>(
761+
castOp, llvmDstTy, llvmSrcVal);
762+
break;
763+
}
756764
}
757765

758766
return mlir::success();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
5+
6+
using pi1_t = int __attribute__((address_space(1))) *;
7+
using pi2_t = int __attribute__((address_space(2))) *;
8+
9+
using ri1_t = int __attribute__((address_space(1))) &;
10+
using ri2_t = int __attribute__((address_space(2))) &;
11+
12+
// CIR: cir.func @{{.*test_ptr.*}}
13+
// LLVM: define void @{{.*test_ptr.*}}
14+
void test_ptr() {
15+
pi1_t ptr1;
16+
pi2_t ptr2 = (pi2_t)ptr1;
17+
// CIR: %[[#PTR1:]] = cir.load %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, addrspace(1)>>, !cir.ptr<!s32i, addrspace(1)>
18+
// CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#PTR1]] : !cir.ptr<!s32i, addrspace(1)>), !cir.ptr<!s32i, addrspace(2)>
19+
// CIR-NEXT: cir.store %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(2)>, !cir.ptr<!cir.ptr<!s32i, addrspace(2)>>
20+
21+
// LLVM: %[[#PTR1:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
22+
// LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#PTR1]] to ptr addrspace(2)
23+
// LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
24+
}
25+
26+
// CIR: cir.func @{{.*test_ref.*}}
27+
// LLVM: define void @{{.*test_ref.*}}
28+
void test_ref() {
29+
pi1_t ptr;
30+
ri1_t ref1 = *ptr;
31+
ri2_t ref2 = (ri2_t)ref1;
32+
// CIR: %[[#DEREF:]] = cir.load deref %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, addrspace(1)>>, !cir.ptr<!s32i, addrspace(1)>
33+
// CIR-NEXT: cir.store %[[#DEREF]], %[[#ALLOCAREF1:]] : !cir.ptr<!s32i, addrspace(1)>, !cir.ptr<!cir.ptr<!s32i, addrspace(1)>>
34+
// CIR-NEXT: %[[#REF1:]] = cir.load %[[#ALLOCAREF1]] : !cir.ptr<!cir.ptr<!s32i, addrspace(1)>>, !cir.ptr<!s32i, addrspace(1)>
35+
// CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#REF1]] : !cir.ptr<!s32i, addrspace(1)>), !cir.ptr<!s32i, addrspace(2)>
36+
// CIR-NEXT: cir.store %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(2)>, !cir.ptr<!cir.ptr<!s32i, addrspace(2)>>
37+
38+
// LLVM: %[[#DEREF:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
39+
// LLVM-NEXT: store ptr addrspace(1) %[[#DEREF]], ptr %[[#ALLOCAREF1:]], align 8
40+
// LLVM-NEXT: %[[#REF1:]] = load ptr addrspace(1), ptr %[[#ALLOCAREF1]], align 8
41+
// LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#REF1]] to ptr addrspace(2)
42+
// LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
43+
}
44+
45+
// CIR: cir.func @{{.*test_nullptr.*}}
46+
// LLVM: define void @{{.*test_nullptr.*}}
47+
void test_nullptr() {
48+
constexpr pi1_t null1 = nullptr;
49+
pi2_t ptr = (pi2_t)null1;
50+
// CIR: %[[#NULL1:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(1)>
51+
// CIR-NEXT: cir.store %[[#NULL1]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(1)>, !cir.ptr<!cir.ptr<!s32i, addrspace(1)>>
52+
// CIR-NEXT: %[[#NULL2:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(2)>
53+
// CIR-NEXT: cir.store %[[#NULL2]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(2)>, !cir.ptr<!cir.ptr<!s32i, addrspace(2)>>
54+
55+
// LLVM: store ptr addrspace(1) null, ptr %{{[0-9]+}}, align 8
56+
// LLVM-NEXT: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
57+
}

clang/test/CIR/IR/cast.cir

+9
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,19 @@ module {
1515
%2 = cir.cast(bitcast, %p : !cir.ptr<!s32i>), !cir.ptr<f32>
1616
cir.return
1717
}
18+
19+
cir.func @addrspace_cast(%arg0: !cir.ptr<!s32i>) {
20+
%0 = cir.cast(address_space, %arg0 : !cir.ptr<!s32i>), !cir.ptr<!s32i, addrspace(2)>
21+
cir.return
22+
}
1823
}
1924

2025
// CHECK: cir.func @yolo(%arg0: !s32i)
2126
// CHECK: %1 = cir.cast(int_to_bool, %arg0 : !s32i), !cir.bool
2227
// CHECK: %2 = cir.cast(array_to_ptrdecay, %0 : !cir.ptr<!cir.array<!s32i x 10>>), !cir.ptr<!s32i>
28+
2329
// CHECK: cir.func @bitcast
2430
// CHECK: %0 = cir.cast(bitcast, %arg0 : !cir.ptr<!s32i>), !cir.ptr<f32>
31+
32+
// CHECK: cir.func @addrspace_cast
33+
// CHECK: %0 = cir.cast(address_space, %arg0 : !cir.ptr<!s32i>), !cir.ptr<!s32i, addrspace(2)>

clang/test/CIR/IR/invalid.cir

+25
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,31 @@ cir.func @cast24(%p : !u32i) {
300300

301301
// -----
302302

303+
!u32i = !cir.int<u, 32>
304+
!u64i = !cir.int<u, 64>
305+
cir.func @cast25(%p : !cir.ptr<!u32i, addrspace(1)>) {
306+
%0 = cir.cast(address_space, %p : !cir.ptr<!u32i, addrspace(1)>), !cir.ptr<!u64i, addrspace(2)> // expected-error {{requires two types differ in addrspace only}}
307+
cir.return
308+
}
309+
310+
// -----
311+
312+
!u64i = !cir.int<u, 64>
313+
cir.func @cast26(%p : !cir.ptr<!u64i, addrspace(1)>) {
314+
%0 = cir.cast(address_space, %p : !cir.ptr<!u64i, addrspace(1)>), !u64i // expected-error {{requires !cir.ptr type for source and result}}
315+
cir.return
316+
}
317+
318+
// -----
319+
320+
!u64i = !cir.int<u, 64>
321+
cir.func @cast27(%p : !u64i) {
322+
%0 = cir.cast(address_space, %p : !u64i), !cir.ptr<!u64i, addrspace(1)> // expected-error {{requires !cir.ptr type for source and result}}
323+
cir.return
324+
}
325+
326+
// -----
327+
303328
!u32i = !cir.int<u, 32>
304329
!u8i = !cir.int<u, 8>
305330
module {

clang/test/CIR/Transforms/merge-cleanups.cir

+9
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,13 @@ module {
126126
cir.return %0 : !cir.ptr<!s32i>
127127
}
128128

129+
// Should remove redundant address space casts.
130+
// CHECK-LABEL: @addrspacecastfold
131+
// CHECK: %[[ARG0:.+]]: !cir.ptr<!s32i, addrspace(2)>
132+
// CHECK: cir.return %[[ARG0]] : !cir.ptr<!s32i, addrspace(2)>
133+
cir.func @addrspacecastfold(%arg0: !cir.ptr<!s32i, addrspace(2)>) -> !cir.ptr<!s32i, addrspace(2)> {
134+
%0 = cir.cast(address_space, %arg0: !cir.ptr<!s32i, addrspace(2)>), !cir.ptr<!s32i, addrspace(2)>
135+
cir.return %0 : !cir.ptr<!s32i, addrspace(2)>
136+
}
137+
129138
}

0 commit comments

Comments
 (0)