[clang] [CIR] Upstream `AddressSpace` conversions support (PR #161212)
David Rivera via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 7 05:43:56 PDT 2025
https://github.com/RiverDave updated https://github.com/llvm/llvm-project/pull/161212
>From 4451464b74b54b7675f2adbf5d15544d81bad72f Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Mon, 29 Sep 2025 11:05:44 -0400
Subject: [PATCH 1/5] [CIR] Upstream AddressSpace casting support
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 9 +++
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 41 +++++++----
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 19 +++++-
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 22 ++++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 4 ++
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 17 +++++
clang/lib/CIR/CodeGen/CIRGenModule.h | 6 ++
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 2 +-
clang/lib/CIR/CodeGen/TargetInfo.cpp | 13 ++++
clang/lib/CIR/CodeGen/TargetInfo.h | 13 ++++
clang/test/CIR/address-space-conversion.cpp | 68 +++++++++++++++++++
11 files changed, 196 insertions(+), 18 deletions(-)
create mode 100644 clang/test/CIR/address-space-conversion.cpp
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 89b519e96a93e..e0a35a27e6891 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -445,6 +445,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return createBitcast(src, getPointerTo(newPointeeTy));
}
+ mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src,
+ mlir::Type newTy) {
+ return createCast(loc, cir::CastKind::address_space, src, newTy);
+ }
+
+ mlir::Value createAddrSpaceCast(mlir::Value src, mlir::Type newTy) {
+ return createAddrSpaceCast(src.getLoc(), src, newTy);
+ }
+
//===--------------------------------------------------------------------===//
// Binary Operators
//===--------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 4cfa91e09efb4..f97dff445873f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -58,6 +58,24 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e,
return RValue::get(result);
}
+// Initialize the alloca with the given size and alignment according to the lang
+// opts. Supporting only the trivial non-initialization for now.
+static void initializeAlloca(CIRGenFunction &CGF,
+ [[maybe_unused]] mlir::Value AllocaAddr,
+ [[maybe_unused]] mlir::Value Size,
+ [[maybe_unused]] CharUnits AlignmentInBytes) {
+
+ switch (CGF.getLangOpts().getTrivialAutoVarInit()) {
+ case LangOptions::TrivialAutoVarInitKind::Uninitialized:
+ // Nothing to initialize.
+ return;
+ case LangOptions::TrivialAutoVarInitKind::Zero:
+ case LangOptions::TrivialAutoVarInitKind::Pattern:
+ assert(false && "unexpected trivial auto var init kind NYI");
+ return;
+ }
+}
+
RValue CIRGenFunction::emitRotate(const CallExpr *e, bool isRotateLeft) {
mlir::Value input = emitScalarExpr(e->getArg(0));
mlir::Value amount = emitScalarExpr(e->getArg(1));
@@ -172,21 +190,8 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
builder.getUInt8Ty(), "bi_alloca", suitableAlignmentInBytes, size);
// Initialize the allocated buffer if required.
- if (builtinID != Builtin::BI__builtin_alloca_uninitialized) {
- // Initialize the alloca with the given size and alignment according to
- // the lang opts. Only the trivial non-initialization is supported for
- // now.
-
- switch (getLangOpts().getTrivialAutoVarInit()) {
- case LangOptions::TrivialAutoVarInitKind::Uninitialized:
- // Nothing to initialize.
- break;
- case LangOptions::TrivialAutoVarInitKind::Zero:
- case LangOptions::TrivialAutoVarInitKind::Pattern:
- cgm.errorNYI("trivial auto var init");
- break;
- }
- }
+ if (builtinID != Builtin::BI__builtin_alloca_uninitialized)
+ initializeAlloca(*this, allocaAddr, size, suitableAlignmentInBytes);
// An alloca will always return a pointer to the alloca (stack) address
// space. This address space need not be the same as the AST / Language
@@ -194,6 +199,12 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
// the AST level this is handled within CreateTempAlloca et al., but for the
// builtin / dynamic alloca we have to handle it here.
assert(!cir::MissingFeatures::addressSpace());
+ cir::AddressSpace aas = getCIRAllocaAddressSpace();
+ cir::AddressSpace eas = cir::toCIRAddressSpace(
+ e->getType()->getPointeeType().getAddressSpace());
+ if (eas != aas) {
+ assert(false && "Non-default address space for alloca NYI");
+ }
// Bitcast the alloca to the expected type.
return RValue::get(
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index be948908e18d5..9a3160cb65342 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1191,7 +1191,19 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
case CK_Dynamic:
case CK_ToUnion:
case CK_BaseToDerived:
- case CK_AddressSpaceConversion:
+ case CK_AddressSpaceConversion: {
+ LValue lv = emitLValue(e->getSubExpr());
+ QualType destTy = getContext().getPointerType(e->getType());
+ cir::AddressSpace srcAS =
+ cir::toCIRAddressSpace(e->getSubExpr()->getType().getAddressSpace());
+ cir::AddressSpace destAS =
+ cir::toCIRAddressSpace(e->getType().getAddressSpace());
+ mlir::Value V = getTargetHooks().performAddrSpaceCast(
+ *this, lv.getPointer(), srcAS, destAS, convertType(destTy));
+ return makeAddrLValue(Address(V, convertTypeForMem(e->getType()),
+ lv.getAddress().getAlignment()),
+ e->getType(), lv.getBaseInfo());
+ }
case CK_ObjCObjectLValueCast:
case CK_VectorSplat:
case CK_ConstructorConversion:
@@ -2283,7 +2295,10 @@ Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
// be different from the type defined by the language. For example,
// in C++ the auto variables are in the default address space. Therefore
// cast alloca to the default address space when necessary.
- assert(!cir::MissingFeatures::addressSpace());
+ if (auto astAS = cir::toCIRAddressSpace(cgm.getLangTempAllocaAddressSpace());
+ getCIRAllocaAddressSpace() != astAS) {
+ llvm_unreachable("Requires address space cast which is NYI");
+ }
return Address(v, ty, align);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 5d3496a637268..72106f5516064 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -87,6 +87,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
//===--------------------------------------------------------------------===//
// Utilities
//===--------------------------------------------------------------------===//
+ mlir::Type convertType(QualType ty) { return cgf.convertType(ty); }
mlir::Value emitComplexToScalarConversion(mlir::Location loc,
mlir::Value value, CastKind kind,
@@ -1871,6 +1872,27 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
return cgf.getBuilder().createBitcast(cgf.getLoc(subExpr->getSourceRange()),
src, dstTy);
}
+ case CK_AddressSpaceConversion: {
+ Expr::EvalResult result;
+ if (subExpr->EvaluateAsRValue(result, cgf.getContext()) &&
+ result.Val.isNullPointer()) {
+ // If E has side effect, it is emitted even if its final result is a
+ // null pointer. In that case, a DCE pass should be able to
+ // eliminate the useless instructions emitted during translating E.
+ if (result.HasSideEffects)
+ Visit(subExpr);
+ return cgf.cgm.emitNullConstant(destTy,
+ cgf.getLoc(subExpr->getExprLoc()));
+ }
+ // Since target may map different address spaces in AST to the same address
+ // space, an address space conversion may end up as a bitcast.
+ cir::AddressSpace srcAS = cir::toCIRAddressSpace(
+ subExpr->getType()->getPointeeType().getAddressSpace());
+ cir::AddressSpace destAS =
+ cir::toCIRAddressSpace(destTy->getPointeeType().getAddressSpace());
+ return cgf.cgm.getTargetCIRGenInfo().performAddrSpaceCast(
+ cgf, Visit(subExpr), srcAS, destAS, convertType(destTy));
+ }
case CK_AtomicToNonAtomic: {
cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(),
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index cbc0f4a068d7b..2280cd442d738 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -180,6 +180,10 @@ class CIRGenFunction : public CIRGenTypeCache {
const TargetInfo &getTarget() const { return cgm.getTarget(); }
mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); }
+ const TargetCIRGenInfo &getTargetHooks() const {
+ return cgm.getTargetCIRGenInfo();
+ }
+
// ---------------------
// Opaque value handling
// ---------------------
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 910c8a9b8f98a..f9c8c082b5ac0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1390,6 +1390,23 @@ CIRGenModule::getAddrOfConstantStringFromLiteral(const StringLiteral *s,
return builder.getGlobalViewAttr(ptrTy, gv);
}
+// TODO(cir): this could be a common AST helper for both CIR and LLVM codegen.
+LangAS CIRGenModule::getLangTempAllocaAddressSpace() const {
+ if (getLangOpts().OpenCL)
+ return LangAS::opencl_private;
+
+ // For temporaries inside functions, CUDA treats them as normal variables.
+ // LangAS::cuda_device, on the other hand, is reserved for those variables
+ // explicitly marked with __device__.
+ if (getLangOpts().CUDAIsDevice)
+ return LangAS::Default;
+
+ if (getLangOpts().SYCLIsDevice ||
+ (getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice))
+ llvm_unreachable("NYI");
+ return LangAS::Default;
+}
+
void CIRGenModule::emitExplicitCastExprType(const ExplicitCastExpr *e,
CIRGenFunction *cgf) {
if (cgf && e->getType()->isVariablyModifiedType())
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index c6a6681021d47..b79dd7ee8d1c1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -290,6 +290,12 @@ class CIRGenModule : public CIRGenTypeCache {
getAddrOfConstantStringFromLiteral(const StringLiteral *s,
llvm::StringRef name = ".str");
+ /// Returns the address space for temporary allocations in the language. This
+ /// ensures that the allocated variable's address space matches the
+ /// expectations of the AST, rather than using the target's allocation address
+ /// space, which may lead to type mismatches in other parts of the IR.
+ LangAS getLangTempAllocaAddressSpace() const;
+
/// Set attributes which are common to any form of a global definition (alias,
/// Objective-C method, function, global variable).
///
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index e65896a9ff109..c35143238a0f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -405,7 +405,7 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
const ReferenceType *refTy = cast<ReferenceType>(ty);
QualType elemTy = refTy->getPointeeType();
auto pointeeType = convertTypeForMem(elemTy);
- resultType = builder.getPointerTo(pointeeType);
+ resultType = builder.getPointerTo(pointeeType, elemTy.getAddressSpace());
assert(resultType && "Cannot get pointer type?");
break;
}
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp
index 62a8c59abe604..34a66da466dd7 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp
@@ -1,5 +1,7 @@
#include "TargetInfo.h"
#include "ABIInfo.h"
+#include "CIRGenFunction.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
using namespace clang;
using namespace clang::CIRGen;
@@ -68,3 +70,14 @@ bool TargetCIRGenInfo::isNoProtoCallVariadic(
// For everything else, we just prefer false unless we opt out.
return false;
}
+
+mlir::Value TargetCIRGenInfo::performAddrSpaceCast(
+ CIRGenFunction &cgf, mlir::Value src, cir::AddressSpace srcAS,
+ cir::AddressSpace destAS, mlir::Type destTy, bool isNonNull) const {
+ // Since target may map different address spaces in AST to the same address
+ // space, an address space conversion may end up as a bitcast.
+ if (cir::GlobalOp globalOp = src.getDefiningOp<cir::GlobalOp>())
+ llvm_unreachable("Global ops addrspace cast NYI");
+ // Try to preserve the source's name to make IR more readable.
+ return cgf.getBuilder().createAddrSpaceCast(src, destTy);
+}
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h
index dbb0312c76040..0052aae3388cd 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -33,6 +33,8 @@ bool isEmptyFieldForLayout(const ASTContext &context, const FieldDecl *fd);
/// if the [[no_unique_address]] attribute would have made them empty.
bool isEmptyRecordForLayout(const ASTContext &context, QualType t);
+class CIRGenFunction;
+
class TargetCIRGenInfo {
std::unique_ptr<ABIInfo> info;
@@ -48,6 +50,17 @@ class TargetCIRGenInfo {
virtual cir::TargetAddressSpaceAttr getCIRAllocaAddressSpace() const {
return {};
}
+ /// Perform address space cast of an expression of pointer type.
+ /// \param V is the value to be casted to another address space.
+ /// \param SrcAddr is the CIR address space of \p V.
+ /// \param DestAddr is the targeted CIR address space.
+ /// \param DestTy is the destination pointer type.
+ /// \param IsNonNull is the flag indicating \p V is known to be non null.
+ virtual mlir::Value performAddrSpaceCast(CIRGenFunction &cgf, mlir::Value v,
+ cir::AddressSpace srcAS,
+ cir::AddressSpace destAS,
+ mlir::Type destTy,
+ bool isNonNull = false) const;
/// Determine whether a call to an unprototyped functions under
/// the given calling convention should use the variadic
diff --git a/clang/test/CIR/address-space-conversion.cpp b/clang/test/CIR/address-space-conversion.cpp
new file mode 100644
index 0000000000000..0f600e52d24da
--- /dev/null
+++ b/clang/test/CIR/address-space-conversion.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+
+using pi1_t = int __attribute__((address_space(1))) *;
+using pi2_t = int __attribute__((address_space(2))) *;
+
+using ri1_t = int __attribute__((address_space(1))) &;
+using ri2_t = int __attribute__((address_space(2))) &;
+
+// CIR: cir.func dso_local @{{.*test_ptr.*}}
+// LLVM: define dso_local void @{{.*test_ptr.*}}
+void test_ptr() {
+ pi1_t ptr1;
+ pi2_t ptr2 = (pi2_t)ptr1;
+ // CIR: %[[#PTR1:]] = cir.load{{.*}} %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32
+ // CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#PTR1]] : !cir.ptr<!s32i, addrspace(target<1>)>), !cir.ptr<!s32i, addrspace(target<2>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+
+ // LLVM: %[[#PTR1:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
+ // LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#PTR1]] to ptr addrspace(2)
+ // LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
+}
+
+// CIR: cir.func dso_local @{{.*test_ref.*}}
+// LLVM: define dso_local void @{{.*test_ref.*}}
+void test_ref() {
+ pi1_t ptr;
+ ri1_t ref1 = *ptr;
+ ri2_t ref2 = (ri2_t)ref1;
+ // CIR: %[[#DEREF:]] = cir.load deref{{.*}} %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>, !cir.ptr<!s32i, addrspace(target<1>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#DEREF]], %[[#ALLOCAREF1:]] : !cir.ptr<!s32i, addrspace(target<1>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>
+ // CIR-NEXT: %[[#REF1:]] = cir.load{{.*}} %[[#ALLOCAREF1]] : !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>, !cir.ptr<!s32i, addrspace(target<1>)>
+ // CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#REF1]] : !cir.ptr<!s32i, addrspace(target<1>)>), !cir.ptr<!s32i, addrspace(target<2>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+
+ // LLVM: %[[#DEREF:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
+ // LLVM-NEXT: store ptr addrspace(1) %[[#DEREF]], ptr %[[#ALLOCAREF1:]], align 8
+ // LLVM-NEXT: %[[#REF1:]] = load ptr addrspace(1), ptr %[[#ALLOCAREF1]], align 8
+ // LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#REF1]] to ptr addrspace(2)
+ // LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
+}
+
+// CIR: cir.func dso_local @{{.*test_nullptr.*}}
+// LLVM: define dso_local void @{{.*test_nullptr.*}}
+void test_nullptr() {
+ constexpr pi1_t null1 = nullptr;
+ pi2_t ptr = (pi2_t)null1;
+ // CIR: %[[#NULL1:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<1>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#NULL1]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<1>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>
+ // CIR-NEXT: %[[#NULL2:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<2>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#NULL2]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+
+ // LLVM: store ptr addrspace(1) null, ptr %{{[0-9]+}}, align 8
+ // LLVM-NEXT: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
+}
+
+void test_side_effect(pi1_t b) {
+ pi2_t p = (pi2_t)(*b++, (int*)0);
+ // CIR: %{{[0-9]+}} = cir.ptr_stride(%{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<1>)>, %{{[0-9]+}} : !s32i), !cir.ptr<!s32i, addrspace(target<1>)>
+ // CIR: %[[#CAST:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<2>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+
+ // LLVM: %{{[0-9]+}} = getelementptr i32, ptr addrspace(1) %{{[0-9]+}}, i64 1
+ // LLVM: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
+
+}
>From a5aa8db4cbb975614c8b7cdbbdef71fbe9cce608 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Mon, 29 Sep 2025 11:45:27 -0400
Subject: [PATCH 2/5] Verify bitcast does not contain address space conversion
---
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 5 ++++-
clang/test/CIR/IR/invalid-addrspace.cir | 1 -
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index cdd4e3c96ca98..302447dfa6126 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -423,7 +423,10 @@ LogicalResult cir::CastOp::verify() {
auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
if (srcPtrTy && resPtrTy) {
- return success();
+ if (srcPtrTy.getAddrSpace() != resPtrTy.getAddrSpace()) {
+ return emitOpError() << "result type address space does not match the "
+ "address space of the operand";
+ }
}
return success();
diff --git a/clang/test/CIR/IR/invalid-addrspace.cir b/clang/test/CIR/IR/invalid-addrspace.cir
index 8f188b840bdec..4b6a388b1e4a8 100644
--- a/clang/test/CIR/IR/invalid-addrspace.cir
+++ b/clang/test/CIR/IR/invalid-addrspace.cir
@@ -24,4 +24,3 @@ cir.func @address_space2(%p : !cir.ptr<!u64i, target_address_space>) {
cir.func @address_space3(%p : !cir.ptr<!u64i, target_address_space()>) {
cir.return
}
-
>From 765cb3f6605e078bd91a085b3890f056147ec9f0 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Tue, 30 Sep 2025 16:05:06 -0400
Subject: [PATCH 3/5] address comments
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 17 ++--
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 2 +
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 2 +-
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 2 +-
clang/lib/CIR/CodeGen/TargetInfo.cpp | 2 +-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 15 +++-
.../CIR/CodeGen/address-space-conversion.cpp | 89 +++++++++++++++++++
7 files changed, 113 insertions(+), 16 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/address-space-conversion.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index f97dff445873f..0f6b7e0f02a1d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -60,18 +60,18 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e,
// Initialize the alloca with the given size and alignment according to the lang
// opts. Supporting only the trivial non-initialization for now.
-static void initializeAlloca(CIRGenFunction &CGF,
- [[maybe_unused]] mlir::Value AllocaAddr,
- [[maybe_unused]] mlir::Value Size,
- [[maybe_unused]] CharUnits AlignmentInBytes) {
+static void initializeAlloca(CIRGenFunction &cgf,
+ [[maybe_unused]] mlir::Value allocaAddr,
+ [[maybe_unused]] mlir::Value size,
+ [[maybe_unused]] CharUnits alignmentInBytes) {
- switch (CGF.getLangOpts().getTrivialAutoVarInit()) {
+ switch (cgf.getLangOpts().getTrivialAutoVarInit()) {
case LangOptions::TrivialAutoVarInitKind::Uninitialized:
// Nothing to initialize.
return;
case LangOptions::TrivialAutoVarInitKind::Zero:
case LangOptions::TrivialAutoVarInitKind::Pattern:
- assert(false && "unexpected trivial auto var init kind NYI");
+ cgf.cgm.errorNYI("trivial auto var init");
return;
}
}
@@ -198,17 +198,16 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
// default (e.g. in C / C++ auto vars are in the generic address space). At
// the AST level this is handled within CreateTempAlloca et al., but for the
// builtin / dynamic alloca we have to handle it here.
- assert(!cir::MissingFeatures::addressSpace());
cir::AddressSpace aas = getCIRAllocaAddressSpace();
cir::AddressSpace eas = cir::toCIRAddressSpace(
e->getType()->getPointeeType().getAddressSpace());
if (eas != aas) {
- assert(false && "Non-default address space for alloca NYI");
+ cgm.errorNYI(e->getSourceRange(), "Non-default address space for alloca");
}
// Bitcast the alloca to the expected type.
return RValue::get(
- builder.createBitcast(allocaAddr, builder.getVoidPtrTy()));
+ builder.createBitcast(allocaAddr, builder.getVoidPtrTy(aas)));
}
case Builtin::BIcos:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 9a3160cb65342..edd99812aeb71 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -2281,6 +2281,8 @@ Address CIRGenFunction::createTempAllocaWithoutCast(
/// This creates a alloca and inserts it into the entry block. The alloca is
/// casted to default address space if necessary.
+// TODO(cir): Implement address space casting to match classic codegen's
+// CreateTempAlloca behavior with DestLangAS parameter
Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
mlir::Location loc, const Twine &name,
mlir::Value arraySize,
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 72106f5516064..cde66533b2a6e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1876,7 +1876,7 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
Expr::EvalResult result;
if (subExpr->EvaluateAsRValue(result, cgf.getContext()) &&
result.Val.isNullPointer()) {
- // If E has side effect, it is emitted even if its final result is a
+ // If e has side effect, it is emitted even if its final result is a
// null pointer. In that case, a DCE pass should be able to
// eliminate the useless instructions emitted during translating E.
if (result.HasSideEffects)
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index f9c8c082b5ac0..fb81141eaf5ab 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1403,7 +1403,7 @@ LangAS CIRGenModule::getLangTempAllocaAddressSpace() const {
if (getLangOpts().SYCLIsDevice ||
(getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice))
- llvm_unreachable("NYI");
+ errorNYI("SYCL or OpenMP temp address space");
return LangAS::Default;
}
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp
index 34a66da466dd7..e622a5afa0185 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp
@@ -77,7 +77,7 @@ mlir::Value TargetCIRGenInfo::performAddrSpaceCast(
// Since target may map different address spaces in AST to the same address
// space, an address space conversion may end up as a bitcast.
if (cir::GlobalOp globalOp = src.getDefiningOp<cir::GlobalOp>())
- llvm_unreachable("Global ops addrspace cast NYI");
+ cgf.cgm.errorNYI("Global op addrspace cast");
// Try to preserve the source's name to make IR more readable.
return cgf.getBuilder().createAddrSpaceCast(src, destTy);
}
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 302447dfa6126..ddb2ac0276e76 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -378,6 +378,16 @@ LogicalResult cir::CastOp::verify() {
mlir::Type resType = getType();
mlir::Type srcType = getSrc().getType();
+ // Verify address space casts for pointer types. given that
+ // casts for within a different address space are illegal.
+ auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
+ auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
+ if (srcPtrTy && resPtrTy && (getKind() != cir::CastKind::address_space))
+ if (srcPtrTy.getAddrSpace() != resPtrTy.getAddrSpace()) {
+ return emitOpError() << "result type address space does not match the "
+ "address space of the operand";
+ }
+
if (mlir::isa<cir::VectorType>(srcType) &&
mlir::isa<cir::VectorType>(resType)) {
// Use the element type of the vector to verify the cast kind. (Except for
@@ -423,10 +433,7 @@ LogicalResult cir::CastOp::verify() {
auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
if (srcPtrTy && resPtrTy) {
- if (srcPtrTy.getAddrSpace() != resPtrTy.getAddrSpace()) {
- return emitOpError() << "result type address space does not match the "
- "address space of the operand";
- }
+ return success();
}
return success();
diff --git a/clang/test/CIR/CodeGen/address-space-conversion.cpp b/clang/test/CIR/CodeGen/address-space-conversion.cpp
new file mode 100644
index 0000000000000..ac635136cb7d6
--- /dev/null
+++ b/clang/test/CIR/CodeGen/address-space-conversion.cpp
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+using pi1_t = int __attribute__((address_space(1))) *;
+using pi2_t = int __attribute__((address_space(2))) *;
+
+using ri1_t = int __attribute__((address_space(1))) &;
+using ri2_t = int __attribute__((address_space(2))) &;
+
+// CIR: cir.func dso_local @{{.*test_ptr.*}}
+// LLVM: define dso_local void @{{.*test_ptr.*}}
+// OGCG: define dso_local void @{{.*test_ptr.*}}
+void test_ptr() {
+ pi1_t ptr1;
+ pi2_t ptr2 = (pi2_t)ptr1;
+ // CIR: %[[#PTR1:]] = cir.load{{.*}} %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32
+ // CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#PTR1]] : !cir.ptr<!s32i, addrspace(target<1>)>), !cir.ptr<!s32i, addrspace(target<2>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+
+ // LLVM: %[[#PTR1:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
+ // LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#PTR1]] to ptr addrspace(2)
+ // LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
+
+ // OGCG: %{{.*}} = load ptr addrspace(1), ptr %{{.*}}, align 8
+ // OGCG-NEXT: %{{.*}} = addrspacecast ptr addrspace(1) %{{.*}} to ptr addrspace(2)
+ // OGCG-NEXT: store ptr addrspace(2) %{{.*}}, ptr %{{.*}}, align 8
+}
+
+// CIR: cir.func dso_local @{{.*test_ref.*}}
+// LLVM: define dso_local void @{{.*test_ref.*}}
+// OGCG: define dso_local void @{{.*test_ref.*}}
+void test_ref() {
+ pi1_t ptr;
+ ri1_t ref1 = *ptr;
+ ri2_t ref2 = (ri2_t)ref1;
+ // CIR: %[[#DEREF:]] = cir.load deref{{.*}} %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>, !cir.ptr<!s32i, addrspace(target<1>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#DEREF]], %[[#ALLOCAREF1:]] : !cir.ptr<!s32i, addrspace(target<1>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>
+ // CIR-NEXT: %[[#REF1:]] = cir.load{{.*}} %[[#ALLOCAREF1]] : !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>, !cir.ptr<!s32i, addrspace(target<1>)>
+ // CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#REF1]] : !cir.ptr<!s32i, addrspace(target<1>)>), !cir.ptr<!s32i, addrspace(target<2>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+
+ // LLVM: %[[#DEREF:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
+ // LLVM-NEXT: store ptr addrspace(1) %[[#DEREF]], ptr %[[#ALLOCAREF1:]], align 8
+ // LLVM-NEXT: %[[#REF1:]] = load ptr addrspace(1), ptr %[[#ALLOCAREF1]], align 8
+ // LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#REF1]] to ptr addrspace(2)
+ // LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
+
+ // OGCG: %{{.*}} = load ptr addrspace(1), ptr %{{.*}}, align 8
+ // OGCG-NEXT: store ptr addrspace(1) %{{.*}}, ptr %{{.*}}, align 8
+ // OGCG-NEXT: %{{.*}} = load ptr addrspace(1), ptr %{{.*}}, align 8
+ // OGCG-NEXT: %{{.*}} = addrspacecast ptr addrspace(1) %{{.*}} to ptr addrspace(2)
+ // OGCG-NEXT: store ptr addrspace(2) %{{.*}}, ptr %{{.*}}, align 8
+}
+
+// CIR: cir.func dso_local @{{.*test_nullptr.*}}
+// LLVM: define dso_local void @{{.*test_nullptr.*}}
+// OGCG: define dso_local void @{{.*test_nullptr.*}}
+void test_nullptr() {
+ constexpr pi1_t null1 = nullptr;
+ pi2_t ptr = (pi2_t)null1;
+ // CIR: %[[#NULL1:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<1>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#NULL1]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<1>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>
+ // CIR-NEXT: %[[#NULL2:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<2>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#NULL2]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+
+ // LLVM: store ptr addrspace(1) null, ptr %{{[0-9]+}}, align 8
+ // LLVM-NEXT: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
+
+ // OGCG: store ptr addrspace(1) null, ptr %{{.*}}, align 8
+ // OGCG-NEXT: store ptr addrspace(2) null, ptr %{{.*}}, align 8
+}
+
+void test_side_effect(pi1_t b) {
+ pi2_t p = (pi2_t)(*b++, (int*)0);
+ // CIR: %{{[0-9]+}} = cir.ptr_stride(%{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<1>)>, %{{[0-9]+}} : !s32i), !cir.ptr<!s32i, addrspace(target<1>)>
+ // CIR: %[[#CAST:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<2>)>
+ // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+
+ // LLVM: %{{[0-9]+}} = getelementptr i32, ptr addrspace(1) %{{[0-9]+}}, i64 1
+ // LLVM: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
+
+ // OGCG: %{{.*}} = getelementptr{{.*}} i32, ptr addrspace(1) %{{.*}}, i32 1
+ // OGCG: store ptr addrspace(2) null, ptr %{{.*}}, align 8
+
+}
>From a6f709a97bb86c3c26416918d8ef4deca0dd81e3 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Thu, 2 Oct 2025 21:25:15 -0400
Subject: [PATCH 4/5] Refactor address space handling based on TargetSpaceAttr
and update performAddrSpaceCast
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 16 +++--
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 27 +++++---
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 6 +-
clang/lib/CIR/CodeGen/TargetInfo.cpp | 11 +--
clang/lib/CIR/CodeGen/TargetInfo.h | 4 --
.../CIR/CodeGen/address-space-conversion.cpp | 41 +++++------
clang/test/CIR/address-space-conversion.cpp | 68 -------------------
7 files changed, 56 insertions(+), 117 deletions(-)
delete mode 100644 clang/test/CIR/address-space-conversion.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 0f6b7e0f02a1d..f134412dbfde5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -198,16 +198,20 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
// default (e.g. in C / C++ auto vars are in the generic address space). At
// the AST level this is handled within CreateTempAlloca et al., but for the
// builtin / dynamic alloca we have to handle it here.
- cir::AddressSpace aas = getCIRAllocaAddressSpace();
- cir::AddressSpace eas = cir::toCIRAddressSpace(
- e->getType()->getPointeeType().getAddressSpace());
- if (eas != aas) {
+
+ LangAS allocaAddrSpace = clang::LangAS::Default;
+ if (getCIRAllocaAddressSpace()) {
+ allocaAddrSpace = clang::getLangASFromTargetAS(
+ getCIRAllocaAddressSpace().getValue().getUInt());
+ }
+ LangAS exprAddrSpace = e->getType()->getPointeeType().getAddressSpace();
+ if (exprAddrSpace != allocaAddrSpace) {
cgm.errorNYI(e->getSourceRange(), "Non-default address space for alloca");
}
// Bitcast the alloca to the expected type.
- return RValue::get(
- builder.createBitcast(allocaAddr, builder.getVoidPtrTy(aas)));
+ return RValue::get(builder.createBitcast(
+ allocaAddr, builder.getVoidPtrTy(allocaAddrSpace)));
}
case Builtin::BIcos:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index edd99812aeb71..33c4fa626fb5d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -22,6 +22,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/Basic/AddressSpaces.h"
+#include "clang/Basic/TargetInfo.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/MissingFeatures.h"
#include <optional>
@@ -1194,13 +1196,10 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
case CK_AddressSpaceConversion: {
LValue lv = emitLValue(e->getSubExpr());
QualType destTy = getContext().getPointerType(e->getType());
- cir::AddressSpace srcAS =
- cir::toCIRAddressSpace(e->getSubExpr()->getType().getAddressSpace());
- cir::AddressSpace destAS =
- cir::toCIRAddressSpace(e->getType().getAddressSpace());
- mlir::Value V = getTargetHooks().performAddrSpaceCast(
- *this, lv.getPointer(), srcAS, destAS, convertType(destTy));
- return makeAddrLValue(Address(V, convertTypeForMem(e->getType()),
+ mlir::Value v = getTargetHooks().performAddrSpaceCast(
+ *this, lv.getPointer(), convertType(destTy));
+
+ return makeAddrLValue(Address(v, convertTypeForMem(e->getType()),
lv.getAddress().getAlignment()),
e->getType(), lv.getBaseInfo());
}
@@ -2297,9 +2296,17 @@ Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
// be different from the type defined by the language. For example,
// in C++ the auto variables are in the default address space. Therefore
// cast alloca to the default address space when necessary.
- if (auto astAS = cir::toCIRAddressSpace(cgm.getLangTempAllocaAddressSpace());
- getCIRAllocaAddressSpace() != astAS) {
- llvm_unreachable("Requires address space cast which is NYI");
+
+ LangAS allocaAS = cgm.getLangTempAllocaAddressSpace();
+ LangAS dstTyAS = clang::LangAS::Default;
+ if (getCIRAllocaAddressSpace()) {
+ dstTyAS = clang::getLangASFromTargetAS(
+ getCIRAllocaAddressSpace().getValue().getUInt());
+ }
+
+ if (dstTyAS != allocaAS) {
+ getTargetHooks().performAddrSpaceCast(*this, v,
+ builder.getPointerTo(ty, dstTyAS));
}
return Address(v, ty, align);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index cde66533b2a6e..1ff3f95a39528 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1886,12 +1886,8 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
}
// Since target may map different address spaces in AST to the same address
// space, an address space conversion may end up as a bitcast.
- cir::AddressSpace srcAS = cir::toCIRAddressSpace(
- subExpr->getType()->getPointeeType().getAddressSpace());
- cir::AddressSpace destAS =
- cir::toCIRAddressSpace(destTy->getPointeeType().getAddressSpace());
return cgf.cgm.getTargetCIRGenInfo().performAddrSpaceCast(
- cgf, Visit(subExpr), srcAS, destAS, convertType(destTy));
+ cgf, Visit(subExpr), convertType(destTy));
}
case CK_AtomicToNonAtomic: {
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp
index e622a5afa0185..7379cb2128b8a 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp
@@ -71,13 +71,14 @@ bool TargetCIRGenInfo::isNoProtoCallVariadic(
return false;
}
-mlir::Value TargetCIRGenInfo::performAddrSpaceCast(
- CIRGenFunction &cgf, mlir::Value src, cir::AddressSpace srcAS,
- cir::AddressSpace destAS, mlir::Type destTy, bool isNonNull) const {
+mlir::Value TargetCIRGenInfo::performAddrSpaceCast(CIRGenFunction &cgf,
+ mlir::Value v,
+ mlir::Type destTy,
+ bool isNonNull) const {
// Since target may map different address spaces in AST to the same address
// space, an address space conversion may end up as a bitcast.
- if (cir::GlobalOp globalOp = src.getDefiningOp<cir::GlobalOp>())
+ if (cir::GlobalOp globalOp = v.getDefiningOp<cir::GlobalOp>())
cgf.cgm.errorNYI("Global op addrspace cast");
// Try to preserve the source's name to make IR more readable.
- return cgf.getBuilder().createAddrSpaceCast(src, destTy);
+ return cgf.getBuilder().createAddrSpaceCast(v, destTy);
}
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h
index 0052aae3388cd..c6855d484638b 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -52,13 +52,9 @@ class TargetCIRGenInfo {
}
/// Perform address space cast of an expression of pointer type.
/// \param V is the value to be casted to another address space.
- /// \param SrcAddr is the CIR address space of \p V.
- /// \param DestAddr is the targeted CIR address space.
/// \param DestTy is the destination pointer type.
/// \param IsNonNull is the flag indicating \p V is known to be non null.
virtual mlir::Value performAddrSpaceCast(CIRGenFunction &cgf, mlir::Value v,
- cir::AddressSpace srcAS,
- cir::AddressSpace destAS,
mlir::Type destTy,
bool isNonNull = false) const;
diff --git a/clang/test/CIR/CodeGen/address-space-conversion.cpp b/clang/test/CIR/CodeGen/address-space-conversion.cpp
index ac635136cb7d6..ed341024f8640 100644
--- a/clang/test/CIR/CodeGen/address-space-conversion.cpp
+++ b/clang/test/CIR/CodeGen/address-space-conversion.cpp
@@ -17,9 +17,9 @@ using ri2_t = int __attribute__((address_space(2))) &;
void test_ptr() {
pi1_t ptr1;
pi2_t ptr2 = (pi2_t)ptr1;
- // CIR: %[[#PTR1:]] = cir.load{{.*}} %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32
- // CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#PTR1]] : !cir.ptr<!s32i, addrspace(target<1>)>), !cir.ptr<!s32i, addrspace(target<2>)>
- // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+ // CIR: %[[#PTR1:]] = cir.load align(8) %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, target_address_space(1)>>, !cir.ptr<!s32i, target_address_space(1)>
+ // CIR-NEXT: %[[#CAST:]] = cir.cast address_space %[[#PTR1]] : !cir.ptr<!s32i, target_address_space(1)> -> !cir.ptr<!s32i, target_address_space(2)>
+ // CIR-NEXT: cir.store align(8) %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, target_address_space(2)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(2)>>
// LLVM: %[[#PTR1:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
// LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#PTR1]] to ptr addrspace(2)
@@ -37,15 +37,15 @@ void test_ref() {
pi1_t ptr;
ri1_t ref1 = *ptr;
ri2_t ref2 = (ri2_t)ref1;
- // CIR: %[[#DEREF:]] = cir.load deref{{.*}} %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>, !cir.ptr<!s32i, addrspace(target<1>)>
- // CIR-NEXT: cir.store{{.*}} %[[#DEREF]], %[[#ALLOCAREF1:]] : !cir.ptr<!s32i, addrspace(target<1>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>
- // CIR-NEXT: %[[#REF1:]] = cir.load{{.*}} %[[#ALLOCAREF1]] : !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>, !cir.ptr<!s32i, addrspace(target<1>)>
- // CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#REF1]] : !cir.ptr<!s32i, addrspace(target<1>)>), !cir.ptr<!s32i, addrspace(target<2>)>
- // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+ // CIR: %[[#DEREF:]] = cir.load deref align(8) %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, target_address_space(1)>>, !cir.ptr<!s32i, target_address_space(1)>
+ // CIR-NEXT: cir.store align(8) %[[#DEREF]], %{{[0-9]+}} : !cir.ptr<!s32i, target_address_space(1)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(1)>>
+ // CIR-NEXT: %[[#REF1:]] = cir.load %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, target_address_space(1)>>, !cir.ptr<!s32i, target_address_space(1)>
+ // CIR-NEXT: %[[#CAST:]] = cir.cast address_space %[[#REF1]] : !cir.ptr<!s32i, target_address_space(1)> -> !cir.ptr<!s32i, target_address_space(2)>
+ // CIR-NEXT: cir.store align(8) %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, target_address_space(2)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(2)>>
// LLVM: %[[#DEREF:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
- // LLVM-NEXT: store ptr addrspace(1) %[[#DEREF]], ptr %[[#ALLOCAREF1:]], align 8
- // LLVM-NEXT: %[[#REF1:]] = load ptr addrspace(1), ptr %[[#ALLOCAREF1]], align 8
+ // LLVM-NEXT: store ptr addrspace(1) %[[#DEREF]], ptr %{{[0-9]+}}, align 8
+ // LLVM-NEXT: %[[#REF1:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
// LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#REF1]] to ptr addrspace(2)
// LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
@@ -62,10 +62,10 @@ void test_ref() {
void test_nullptr() {
constexpr pi1_t null1 = nullptr;
pi2_t ptr = (pi2_t)null1;
- // CIR: %[[#NULL1:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<1>)>
- // CIR-NEXT: cir.store{{.*}} %[[#NULL1]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<1>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>
- // CIR-NEXT: %[[#NULL2:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<2>)>
- // CIR-NEXT: cir.store{{.*}} %[[#NULL2]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+ // CIR: %[[#NULL1:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, target_address_space(1)>
+ // CIR-NEXT: cir.store align(8) %[[#NULL1]], %{{[0-9]+}} : !cir.ptr<!s32i, target_address_space(1)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(1)>>
+ // CIR-NEXT: %[[#NULL2:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, target_address_space(2)>
+ // CIR-NEXT: cir.store align(8) %[[#NULL2]], %{{[0-9]+}} : !cir.ptr<!s32i, target_address_space(2)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(2)>>
// LLVM: store ptr addrspace(1) null, ptr %{{[0-9]+}}, align 8
// LLVM-NEXT: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
@@ -74,16 +74,19 @@ void test_nullptr() {
// OGCG-NEXT: store ptr addrspace(2) null, ptr %{{.*}}, align 8
}
+// CIR: cir.func dso_local @{{.*test_side_effect.*}}
+// LLVM: define dso_local void @{{.*test_side_effect.*}}
+// OGCG: define dso_local void @{{.*test_side_effect.*}}
void test_side_effect(pi1_t b) {
pi2_t p = (pi2_t)(*b++, (int*)0);
- // CIR: %{{[0-9]+}} = cir.ptr_stride(%{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<1>)>, %{{[0-9]+}} : !s32i), !cir.ptr<!s32i, addrspace(target<1>)>
- // CIR: %[[#CAST:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<2>)>
- // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
+ // CIR: %[[#DEREF:]] = cir.load deref align(8) %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, target_address_space(1)>>, !cir.ptr<!s32i, target_address_space(1)>
+ // CIR: %[[#STRIDE:]] = cir.ptr_stride(%[[#DEREF]] : !cir.ptr<!s32i, target_address_space(1)>, %{{[0-9]+}} : !s32i), !cir.ptr<!s32i, target_address_space(1)>
+ // CIR: %[[#NULL:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, target_address_space(2)>
+ // CIR-NEXT: cir.store align(8) %[[#NULL]], %{{[0-9]+}} : !cir.ptr<!s32i, target_address_space(2)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(2)>>
- // LLVM: %{{[0-9]+}} = getelementptr i32, ptr addrspace(1) %{{[0-9]+}}, i64 1
+ // LLVM: %{{[0-9]+}} = getelementptr {{.*}}i32, ptr addrspace(1) %{{[0-9]+}}, i{{32|64}} 1
// LLVM: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
// OGCG: %{{.*}} = getelementptr{{.*}} i32, ptr addrspace(1) %{{.*}}, i32 1
// OGCG: store ptr addrspace(2) null, ptr %{{.*}}, align 8
-
}
diff --git a/clang/test/CIR/address-space-conversion.cpp b/clang/test/CIR/address-space-conversion.cpp
deleted file mode 100644
index 0f600e52d24da..0000000000000
--- a/clang/test/CIR/address-space-conversion.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
-// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
-
-using pi1_t = int __attribute__((address_space(1))) *;
-using pi2_t = int __attribute__((address_space(2))) *;
-
-using ri1_t = int __attribute__((address_space(1))) &;
-using ri2_t = int __attribute__((address_space(2))) &;
-
-// CIR: cir.func dso_local @{{.*test_ptr.*}}
-// LLVM: define dso_local void @{{.*test_ptr.*}}
-void test_ptr() {
- pi1_t ptr1;
- pi2_t ptr2 = (pi2_t)ptr1;
- // CIR: %[[#PTR1:]] = cir.load{{.*}} %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32
- // CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#PTR1]] : !cir.ptr<!s32i, addrspace(target<1>)>), !cir.ptr<!s32i, addrspace(target<2>)>
- // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
-
- // LLVM: %[[#PTR1:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
- // LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#PTR1]] to ptr addrspace(2)
- // LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
-}
-
-// CIR: cir.func dso_local @{{.*test_ref.*}}
-// LLVM: define dso_local void @{{.*test_ref.*}}
-void test_ref() {
- pi1_t ptr;
- ri1_t ref1 = *ptr;
- ri2_t ref2 = (ri2_t)ref1;
- // CIR: %[[#DEREF:]] = cir.load deref{{.*}} %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>, !cir.ptr<!s32i, addrspace(target<1>)>
- // CIR-NEXT: cir.store{{.*}} %[[#DEREF]], %[[#ALLOCAREF1:]] : !cir.ptr<!s32i, addrspace(target<1>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>
- // CIR-NEXT: %[[#REF1:]] = cir.load{{.*}} %[[#ALLOCAREF1]] : !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>, !cir.ptr<!s32i, addrspace(target<1>)>
- // CIR-NEXT: %[[#CAST:]] = cir.cast(address_space, %[[#REF1]] : !cir.ptr<!s32i, addrspace(target<1>)>), !cir.ptr<!s32i, addrspace(target<2>)>
- // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
-
- // LLVM: %[[#DEREF:]] = load ptr addrspace(1), ptr %{{[0-9]+}}, align 8
- // LLVM-NEXT: store ptr addrspace(1) %[[#DEREF]], ptr %[[#ALLOCAREF1:]], align 8
- // LLVM-NEXT: %[[#REF1:]] = load ptr addrspace(1), ptr %[[#ALLOCAREF1]], align 8
- // LLVM-NEXT: %[[#CAST:]] = addrspacecast ptr addrspace(1) %[[#REF1]] to ptr addrspace(2)
- // LLVM-NEXT: store ptr addrspace(2) %[[#CAST]], ptr %{{[0-9]+}}, align 8
-}
-
-// CIR: cir.func dso_local @{{.*test_nullptr.*}}
-// LLVM: define dso_local void @{{.*test_nullptr.*}}
-void test_nullptr() {
- constexpr pi1_t null1 = nullptr;
- pi2_t ptr = (pi2_t)null1;
- // CIR: %[[#NULL1:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<1>)>
- // CIR-NEXT: cir.store{{.*}} %[[#NULL1]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<1>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<1>)>>
- // CIR-NEXT: %[[#NULL2:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<2>)>
- // CIR-NEXT: cir.store{{.*}} %[[#NULL2]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
-
- // LLVM: store ptr addrspace(1) null, ptr %{{[0-9]+}}, align 8
- // LLVM-NEXT: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
-}
-
-void test_side_effect(pi1_t b) {
- pi2_t p = (pi2_t)(*b++, (int*)0);
- // CIR: %{{[0-9]+}} = cir.ptr_stride(%{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<1>)>, %{{[0-9]+}} : !s32i), !cir.ptr<!s32i, addrspace(target<1>)>
- // CIR: %[[#CAST:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, addrspace(target<2>)>
- // CIR-NEXT: cir.store{{.*}} %[[#CAST]], %{{[0-9]+}} : !cir.ptr<!s32i, addrspace(target<2>)>, !cir.ptr<!cir.ptr<!s32i, addrspace(target<2>)>>
-
- // LLVM: %{{[0-9]+}} = getelementptr i32, ptr addrspace(1) %{{[0-9]+}}, i64 1
- // LLVM: store ptr addrspace(2) null, ptr %{{[0-9]+}}, align 8
-
-}
>From b9d636c991f812586b06f28077da64a87352d288 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Tue, 7 Oct 2025 08:43:35 -0400
Subject: [PATCH 5/5] Fix tests
---
clang/test/CIR/CodeGen/address-space-conversion.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/CIR/CodeGen/address-space-conversion.cpp b/clang/test/CIR/CodeGen/address-space-conversion.cpp
index ed341024f8640..9e7b45951ae5f 100644
--- a/clang/test/CIR/CodeGen/address-space-conversion.cpp
+++ b/clang/test/CIR/CodeGen/address-space-conversion.cpp
@@ -80,7 +80,7 @@ void test_nullptr() {
void test_side_effect(pi1_t b) {
pi2_t p = (pi2_t)(*b++, (int*)0);
// CIR: %[[#DEREF:]] = cir.load deref align(8) %{{[0-9]+}} : !cir.ptr<!cir.ptr<!s32i, target_address_space(1)>>, !cir.ptr<!s32i, target_address_space(1)>
- // CIR: %[[#STRIDE:]] = cir.ptr_stride(%[[#DEREF]] : !cir.ptr<!s32i, target_address_space(1)>, %{{[0-9]+}} : !s32i), !cir.ptr<!s32i, target_address_space(1)>
+ // CIR: %[[#STRIDE:]] = cir.ptr_stride %[[#DEREF]], %{{[0-9]+}} : (!cir.ptr<!s32i, target_address_space(1)>, !s32i) -> !cir.ptr<!s32i, target_address_space(1)>
// CIR: %[[#NULL:]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i, target_address_space(2)>
// CIR-NEXT: cir.store align(8) %[[#NULL]], %{{[0-9]+}} : !cir.ptr<!s32i, target_address_space(2)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(2)>>
More information about the cfe-commits
mailing list