[clang] [CIR] Upstream `AddressSpace` conversions support (PR #161212)

David Rivera via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 15 17:27:11 PDT 2025


https://github.com/RiverDave updated https://github.com/llvm/llvm-project/pull/161212

>From bda6bcfcf40dde33526e4a9dcb25393a5af1b5da 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/6] [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 b4c24d7e4a8aa..2eb66ad0512e3 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -456,6 +456,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return createCompare(ptr.getLoc(), cir::CmpOpKind::eq, ptr, nullPtr);
   }
 
+  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 4897c29b58a1f..27329ce9bd3cf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1197,7 +1197,19 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
   case CK_AtomicToNonAtomic:
   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:
@@ -2289,7 +2301,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 637f9ef65c88f..0be57dbfc84b4 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 0d64c31f01668..c4fbc26723b13 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -184,6 +184,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 57c7a440c8a2e..9f695cfe9c467 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1424,6 +1424,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 690f0ed0e9bde..672cd811acede 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -297,6 +297,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 d1b91d0c73c04..d493a10966e1f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -404,7 +404,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 1848705984416c8f931f4813a01824ba29315de7 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/6] 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 7af3dc1f84955..4289b26c97238 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -428,7 +428,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 4d2dc7651dc462ea2b4489fbeb2d222148c8bf5c 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/6] 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 27329ce9bd3cf..dd43e09984723 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -2287,6 +2287,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 0be57dbfc84b4..47b0f8e89c598 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 9f695cfe9c467..bd4adfaa2a9bf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1437,7 +1437,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 4289b26c97238..8875fd09f2016 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -383,6 +383,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
@@ -428,10 +438,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 2304ff47a0b8267e1961f59f1a6790d3ddb581cf 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/6] 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 dd43e09984723..6c6a4b8115cb2 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>
@@ -1200,13 +1202,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());
   }
@@ -2303,9 +2302,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 47b0f8e89c598..8b9d7f6a777fd 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 57443a97345d1c90fed285e2bdcddff905486fa8 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/6] 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)>>
 

>From 58514657618d005463d2e0d7ca0a6a60677dc253 Mon Sep 17 00:00:00 2001
From: David Rivera <davidriverg at gmail.com>
Date: Wed, 15 Oct 2025 18:31:34 -0400
Subject: [PATCH 6/6] Update AddrCast to match OG and address other nits

---
 clang/include/clang/CIR/Dialect/IR/CIRTypes.h |  7 +++
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp       | 53 ++++++++-----------
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          | 25 +++++----
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  4 +-
 clang/lib/CIR/CodeGen/TargetInfo.cpp          |  2 +
 clang/lib/CIR/CodeGen/TargetInfo.h            |  3 +-
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 17 ++++++
 7 files changed, 68 insertions(+), 43 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index 45f646f1c9dfa..281cbb995b157 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
@@ -38,6 +38,13 @@ bool isValidFundamentalIntWidth(unsigned width);
 /// void, or abstract types.
 bool isSized(mlir::Type ty);
 
+//===----------------------------------------------------------------------===//
+// AddressSpace helpers
+//===----------------------------------------------------------------------===//
+
+bool isMatchingAddressSpace(cir::TargetAddressSpaceAttr cirAS,
+                            clang::LangAS as);
+
 } // namespace cir
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index f134412dbfde5..54bb8dcbaee09 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -12,7 +12,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "CIRGenCall.h"
-#include "CIRGenConstantEmitter.h"
 #include "CIRGenFunction.h"
 #include "CIRGenModule.h"
 #include "CIRGenValue.h"
@@ -22,6 +21,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/GlobalDecl.h"
 #include "clang/Basic/Builtins.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
 #include "clang/CIR/MissingFeatures.h"
 #include "llvm/Support/ErrorHandling.h"
 
@@ -58,24 +58,6 @@ 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:
-    cgf.cgm.errorNYI("trivial auto var init");
-    return;
-  }
-}
-
 RValue CIRGenFunction::emitRotate(const CallExpr *e, bool isRotateLeft) {
   mlir::Value input = emitScalarExpr(e->getArg(0));
   mlir::Value amount = emitScalarExpr(e->getArg(1));
@@ -190,8 +172,21 @@ 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)
-      initializeAlloca(*this, allocaAddr, size, suitableAlignmentInBytes);
+    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;
+      }
+    }
 
     // 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
@@ -199,19 +194,17 @@ 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.
 
-    LangAS allocaAddrSpace = clang::LangAS::Default;
-    if (getCIRAllocaAddressSpace()) {
-      allocaAddrSpace = clang::getLangASFromTargetAS(
-          getCIRAllocaAddressSpace().getValue().getUInt());
-    }
-    LangAS exprAddrSpace = e->getType()->getPointeeType().getAddressSpace();
-    if (exprAddrSpace != allocaAddrSpace) {
+    if (!cir::isMatchingAddressSpace(
+            getCIRAllocaAddressSpace(),
+            e->getType()->getPointeeType().getAddressSpace())) {
       cgm.errorNYI(e->getSourceRange(), "Non-default address space for alloca");
     }
 
-    // Bitcast the alloca to the expected type.
+    // Bitcast the alloca to the expected type. Use the CIR alloca address
+    // space attribute when creating the void pointer type so that the
+    // resulting pointer has the correct address space.
     return RValue::get(builder.createBitcast(
-        allocaAddr, builder.getVoidPtrTy(allocaAddrSpace)));
+        allocaAddr, builder.getVoidPtrTy(getCIRAllocaAddressSpace())));
   }
 
   case Builtin::BIcos:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 6c6a4b8115cb2..9346b9dcfda39 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -24,7 +24,9 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/Basic/AddressSpaces.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/CIR/Dialect/IR/CIRAttrs.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
 #include "clang/CIR/MissingFeatures.h"
 #include <optional>
 
@@ -1199,16 +1201,6 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
   case CK_AtomicToNonAtomic:
   case CK_ToUnion:
   case CK_BaseToDerived:
-  case CK_AddressSpaceConversion: {
-    LValue lv = emitLValue(e->getSubExpr());
-    QualType destTy = getContext().getPointerType(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());
-  }
   case CK_ObjCObjectLValueCast:
   case CK_VectorSplat:
   case CK_ConstructorConversion:
@@ -1222,7 +1214,18 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
 
     return {};
   }
+  case CK_AddressSpaceConversion: {
+    LValue lv = emitLValue(e->getSubExpr());
+    QualType destTy = getContext().getPointerType(e->getType());
+    ;
+    mlir::Value v = getTargetHooks().performAddrSpaceCast(
+        *this, lv.getPointer(), e->getSubExpr()->getType().getAddressSpace(),
+        convertType(destTy));
 
+    return makeAddrLValue(Address(v, convertTypeForMem(e->getType()),
+                                  lv.getAddress().getAlignment()),
+                          e->getType(), lv.getBaseInfo());
+  }
   case CK_LValueBitCast: {
     // This must be a reinterpret_cast (or c-style equivalent).
     const auto *ce = cast<ExplicitCastExpr>(e);
@@ -2311,7 +2314,7 @@ Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
   }
 
   if (dstTyAS != allocaAS) {
-    getTargetHooks().performAddrSpaceCast(*this, v,
+    getTargetHooks().performAddrSpaceCast(*this, v, dstTyAS,
                                           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 8b9d7f6a777fd..36b62c8777651 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -15,6 +15,7 @@
 
 #include "clang/AST/Expr.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
 #include "clang/CIR/MissingFeatures.h"
 
 #include "mlir/IR/Location.h"
@@ -1887,7 +1888,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.
     return cgf.cgm.getTargetCIRGenInfo().performAddrSpaceCast(
-        cgf, Visit(subExpr), convertType(destTy));
+        cgf, Visit(subExpr), subExpr->getType().getAddressSpace(),
+        convertType(destTy));
   }
 
   case CK_AtomicToNonAtomic: {
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp
index 7379cb2128b8a..1926a3db3efa2 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp
@@ -1,6 +1,7 @@
 #include "TargetInfo.h"
 #include "ABIInfo.h"
 #include "CIRGenFunction.h"
+#include "clang/Basic/AddressSpaces.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 using namespace clang;
@@ -73,6 +74,7 @@ bool TargetCIRGenInfo::isNoProtoCallVariadic(
 
 mlir::Value TargetCIRGenInfo::performAddrSpaceCast(CIRGenFunction &cgf,
                                                    mlir::Value v,
+                                                   LangAS srcAddr,
                                                    mlir::Type destTy,
                                                    bool isNonNull) const {
   // Since target may map different address spaces in AST to the same address
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h
index c6855d484638b..0c846b915f674 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -53,9 +53,10 @@ 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 DestTy is the destination pointer type.
+  /// \param srcAS is theaddress space of \p V.
   /// \param IsNonNull is the flag indicating \p V is known to be non null.
   virtual mlir::Value performAddrSpaceCast(CIRGenFunction &cgf, mlir::Value v,
-                                           mlir::Type destTy,
+                                           LangAS srcAddr, mlir::Type destTy,
                                            bool isNonNull = false) const;
 
   /// Determine whether a call to an unprototyped functions under
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 5897352829891..b454fc70e6870 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -12,11 +12,15 @@
 
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
 
+#include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/IR/DialectImplementation.h"
+#include "mlir/IR/MLIRContext.h"
+#include "clang/Basic/AddressSpaces.h"
 #include "clang/CIR/Dialect/IR/CIRAttrs.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/Dialect/IR/CIRTypesDetails.h"
 #include "clang/CIR/MissingFeatures.h"
+#include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/TypeSwitch.h"
 
 //===----------------------------------------------------------------------===//
@@ -807,6 +811,19 @@ mlir::LogicalResult cir::VectorType::verify(
 // TargetAddressSpace definitions
 //===----------------------------------------------------------------------===//
 
+bool cir::isMatchingAddressSpace(cir::TargetAddressSpaceAttr cirAS,
+                                 clang::LangAS as) {
+  // If there is no CIR target attr, consider it "default" and only match
+  // when the AST address space is LangAS::Default.
+  if (!cirAS)
+    return as == clang::LangAS::Default;
+
+  if (!isTargetAddressSpace(as))
+    return false;
+
+  return cirAS.getValue().getUInt() == toTargetAddressSpace(as);
+}
+
 mlir::ParseResult parseTargetAddressSpace(mlir::AsmParser &p,
                                           cir::TargetAddressSpaceAttr &attr) {
   if (failed(p.parseKeyword("target_address_space")))



More information about the cfe-commits mailing list