-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[CIR] Upstream AddressSpace
conversions support
#161212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
✅ With the latest revision this PR passed the C/C++ code formatter. |
52f8f0f
to
a631340
Compare
AddressSpace
conversions support
@llvm/pr-subscribers-clang Author: David Rivera (RiverDave) ChangesAdd support for address space conversions in CIR.
Full diff: https://github.com/llvm/llvm-project/pull/161212.diff 11 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index cef8624e65d57..bf4a9b8438982 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -424,6 +424,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 cf17de144f4d9..95e392d860518 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 44626bbdd1dfa..f65d502a12318 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1190,7 +1190,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 bd09d78cd0eb6..48ffaf9770e7c 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,
@@ -1862,6 +1863,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 166435f9e7e9e..f1158c7488f14 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -160,6 +160,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 eef23a0ebda7f..5ff7c6cf25f80 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1363,6 +1363,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 073e8d96b773b..d8b6f90d37b39 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -270,6 +270,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..26caa4a576abb 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);
+}
\ No newline at end of file
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h
index 1c3ba0b9971b3..0ba12ef7bb1c8 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -45,6 +45,18 @@ class TargetCIRGenInfo {
/// Returns ABI info helper for the target.
const ABIInfo &getABIInfo() const { return *info; }
+ /// 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
/// convention or the non-variadic convention.
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
+
+}
|
@llvm/pr-subscribers-clangir Author: David Rivera (RiverDave) ChangesAdd support for address space conversions in CIR.
Full diff: https://github.com/llvm/llvm-project/pull/161212.diff 11 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index cef8624e65d57..bf4a9b8438982 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -424,6 +424,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 cf17de144f4d9..95e392d860518 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 44626bbdd1dfa..f65d502a12318 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1190,7 +1190,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 bd09d78cd0eb6..48ffaf9770e7c 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,
@@ -1862,6 +1863,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 166435f9e7e9e..f1158c7488f14 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -160,6 +160,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 eef23a0ebda7f..5ff7c6cf25f80 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1363,6 +1363,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 073e8d96b773b..d8b6f90d37b39 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -270,6 +270,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..26caa4a576abb 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);
+}
\ No newline at end of file
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h
index 1c3ba0b9971b3..0ba12ef7bb1c8 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -45,6 +45,18 @@ class TargetCIRGenInfo {
/// Returns ABI info helper for the target.
const ABIInfo &getABIInfo() const { return *info; }
+ /// 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
/// convention or the non-variadic convention.
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
+
+}
|
61674c7
to
765a44e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good, nothing else to add once Andy is happy
fc85a21
to
df23476
Compare
9cd2c54
to
533e322
Compare
2dc195f
to
a438d04
Compare
I updated this based on the recent feedback on #161028 I made a change tn the function:
|
Sounds legit, thanks! |
a438d04
to
a6f709a
Compare
cc @andykaylor. Before merging and later back-porting these recent changes, I'd like your approval first, thanks. |
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Outdated
case CK_AddressSpaceConversion: { | ||
LValue lv = emitLValue(e->getSubExpr()); | ||
QualType destTy = getContext().getPointerType(e->getType()); | ||
mlir::Value v = getTargetHooks().performAddrSpaceCast( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change in function signature for perfromAddrSpaceCast
relative to the incubator and classic codegen makes me nervous.
Both the incubator and classic codegen get the source address space here from E->getSubExpr()->getType().getAddressSpace()
. I would expect you'll get the same address space, though much less directly, with the way you've implemented this. However, there are other places where performAddrSpaceCast
is called with an address space that didn't come directly from a value, which could be a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I get your point, I suppose the ideal would be to do the attr -> langAs conversion directly from the type in that way we match the og structure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main concern here is that we should not stray too far from the function signature used in OGCG. If my source grep is to be trusted, this function is currently called in only two places in the incubator, but it's called in 24 places in OGCG, so changing the signature now could cause us headaches as we implement more pieces that are currently missing from the incubator.
I also found this comment from a recent code review to provide relevant perspective from the OGCG code owner:
#138866 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx Andy, I understand. While my original intend was to avoid unused/redundant parameters within our implementation, the code owner's argument is strong enough to follow your approach. (btw, this PR is turning into a mess. Sorry to get you through that :p)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly done addressing this. AS Attributes are now passed to performAddrSpaceCasting
.
Introduced toCIRTargetAddressSpace
to convert AST addresses. we could later make this smoother and unify it with the other lang specific attribute in a potential toCIRAddressSpace
function.
5851465
to
41f7f5c
Compare
41f7f5c
to
979c034
Compare
related: #160386
Add support for address space conversions in CIR.
createAddrSpaceCast
methods toCIRBaseBuilderTy
to handle address space conversionsemitCastLValue
andVisitCastExpr
performAddrSpaceCast
method toTargetCIRGenInfo
for target-specific address space castinggetLangTempAllocaAddressSpace
toCIRGenModule
to get the language-specific address space for temporary allocationsaddress-space-conversion.cpp
to verify address space conversion functionality