[Mlir-commits] [clang] [mlir] [CIR] Preserve address space in base/derived class address casts (PR #230841)
Arseniy Obolenskiy
llvmlistbot at llvm.org
Sat Oct 10 12:57:36 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/230841
>From 589added47f8f6cb29b3da55c0fe6e1422dc8e26 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Sat, 10 Oct 2026 21:37:03 +0200
Subject: [PATCH 1/2] [CIR] Preserve address space in base/derived class
address casts
Propagate the source pointer address space to cir.base_class_addr, cir.derived_class_addr and the virtual base pointer adjustment
Fix invalid LLVM IR for class hierarchies in non-default address spaces
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 2 +
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 4 +-
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 7 +-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 20 +++++
.../test/CIR/CodeGen/base-class-addrspace.cpp | 90 +++++++++++++++++++
clang/test/CIR/IR/invalid-class-addr.cir | 27 ++++++
6 files changed, 145 insertions(+), 5 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/base-class-addrspace.cpp
create mode 100644 clang/test/CIR/IR/invalid-class-addr.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 6a7d8ca8f7fc9..6a377661d8241 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -6769,6 +6769,8 @@ class CIR_ClassCastOp<string mnemonic> : CIR_Op<mnemonic, [Pure]> {
`:` qualified(type($src_addr)) `->` qualified(type($result))
attr-dict
}];
+
+ let hasVerifier = 1;
}
def CIR_BaseClassAddrOp : CIR_ClassCastOp<"base_class_addr"> {
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 0d22c40072f63..2169bcd6bc6ac 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -482,7 +482,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
if (destType == addr.getElementType())
return addr;
- auto ptrTy = getPointerTo(destType);
+ auto ptrTy = getPointerTo(destType, addr.getAddressSpace());
auto baseAddr =
cir::BaseClassAddrOp::create(*this, loc, ptrTy, addr.getPointer(),
mlir::APInt(64, offset), assumeNotNull);
@@ -495,7 +495,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
if (destType == addr.getElementType())
return addr;
- cir::PointerType ptrTy = getPointerTo(destType);
+ cir::PointerType ptrTy = getPointerTo(destType, addr.getAddressSpace());
auto derivedAddr =
cir::DerivedClassAddrOp::create(*this, loc, ptrTy, addr.getPointer(),
mlir::APInt(64, offset), assumeNotNull);
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 679fe082ac37e..9e6ff99792500 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -393,7 +393,8 @@ static Address applyNonVirtualAndVirtualOffset(
// not bytes. So the pointer must be cast to a byte pointer and back.
mlir::Value ptr = addr.getPointer();
- mlir::Type charPtrType = cgf.cgm.uInt8PtrTy;
+ mlir::Type charPtrType =
+ cgf.getBuilder().getPointerTo(cgf.uInt8Ty, addr.getAddressSpace());
mlir::Value charPtr = cgf.getBuilder().createBitcast(ptr, charPtrType);
mlir::Value adjusted = cir::PtrStrideOp::create(
cgf.getBuilder(), loc, charPtrType, charPtr, baseOffset);
@@ -1256,7 +1257,6 @@ Address CIRGenFunction::getAddressOfBaseClass(
// Get the base pointer type.
mlir::Type baseValueTy = convertType((path.end()[-1])->getType());
- assert(!cir::MissingFeatures::addressSpace());
// If there is no virtual base, use cir.base_class_addr. It takes care of
// the adjustment and the null pointer check.
@@ -1277,7 +1277,8 @@ Address CIRGenFunction::getAddressOfBaseClass(
CharUnits alignment =
cgm.getVBaseAlignment(value.getAlignment(), derived, vBase)
.alignmentAtOffset(nonVirtualOffset);
- mlir::Type basePtrTy = builder.getPointerTo(baseValueTy);
+ mlir::Type basePtrTy =
+ builder.getPointerTo(baseValueTy, value.getAddressSpace());
mlir::Value ptrIsNull = builder.createPtrIsNull(value.getPointer());
mlir::Value result =
cir::TernaryOp::create(
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 80696e2f7e990..0c072536603de 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -3621,6 +3621,26 @@ OpFoldResult cir::NotOp::fold(FoldAdaptor adaptor) {
return {};
}
+//===----------------------------------------------------------------------===//
+// BaseClassAddrOp & DerivedClassAddrOp
+//===----------------------------------------------------------------------===//
+
+static LogicalResult verifyClassAddrCast(Operation *op, cir::PointerType srcTy,
+ cir::PointerType resultTy) {
+ if (srcTy.getAddrSpace() != resultTy.getAddrSpace())
+ return op->emitOpError() << "address space mismatch";
+
+ return mlir::success();
+}
+
+LogicalResult cir::BaseClassAddrOp::verify() {
+ return verifyClassAddrCast(getOperation(), getSrcAddr().getType(), getType());
+}
+
+LogicalResult cir::DerivedClassAddrOp::verify() {
+ return verifyClassAddrCast(getOperation(), getSrcAddr().getType(), getType());
+}
+
//===----------------------------------------------------------------------===//
// BaseDataMemberOp & DerivedDataMemberOp
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/CodeGen/base-class-addrspace.cpp b/clang/test/CIR/CodeGen/base-class-addrspace.cpp
new file mode 100644
index 0000000000000..1976c3232517a
--- /dev/null
+++ b/clang/test/CIR/CodeGen/base-class-addrspace.cpp
@@ -0,0 +1,90 @@
+// RUN: %clang_cc1 -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 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+// FIXME: Add LLVM and OGCG checks for the virtual base once cir.vtable.get_vptr
+// keeps the address space.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -DVBASE %s -o %t-vbase.cir
+// RUN: FileCheck --input-file=%t-vbase.cir %s -check-prefix=CIR-VBASE
+
+// A base or derived class subobject is in the address space of the object.
+
+#define AS1 __attribute__((address_space(1)))
+
+struct A { int a; };
+struct B { int b; };
+struct D : A, B { int d; };
+
+int base_member(AS1 D *d) { return d->a; }
+
+// CIR-LABEL: cir.func {{.*}}@_Z11base_memberPU3AS11D
+// CIR: cir.base_class_addr nonnull %{{.*}} [0] : !cir.ptr<!rec_D, target_address_space(1)> -> !cir.ptr<!rec_A, target_address_space(1)>
+
+// LLVM-LABEL: define {{.*}}@_Z11base_memberPU3AS11D
+// LLVM: %[[A:.*]] = getelementptr inbounds nuw %struct.A, ptr addrspace(1) %{{.*}}, i32 0, i32 0
+// LLVM: load i32, ptr addrspace(1) %[[A]]
+
+// OGCG-LABEL: define {{.*}}@_Z11base_memberPU3AS11D
+// OGCG: %[[A:.*]] = getelementptr inbounds nuw %struct.A, ptr addrspace(1) %{{.*}}, i32 0, i32 0
+// OGCG: load i32, ptr addrspace(1) %[[A]]
+
+int nonzero_base_member(AS1 D *d) { return d->b; }
+
+// CIR-LABEL: cir.func {{.*}}@_Z19nonzero_base_memberPU3AS11D
+// CIR: cir.base_class_addr nonnull %{{.*}} [4] : !cir.ptr<!rec_D, target_address_space(1)> -> !cir.ptr<!rec_B, target_address_space(1)>
+
+// LLVM-LABEL: define {{.*}}@_Z19nonzero_base_memberPU3AS11D
+// LLVM: %[[BASE:.*]] = getelementptr i8, ptr addrspace(1) %{{.*}}, i32 4
+// LLVM: %[[B:.*]] = getelementptr inbounds nuw %struct.B, ptr addrspace(1) %[[BASE]], i32 0, i32 0
+// LLVM: load i32, ptr addrspace(1) %[[B]]
+
+// OGCG-LABEL: define {{.*}}@_Z19nonzero_base_memberPU3AS11D
+// OGCG: %[[BASE:.*]] = getelementptr inbounds i8, ptr addrspace(1) %{{.*}}, i64 4
+// OGCG: %[[B:.*]] = getelementptr inbounds nuw %struct.B, ptr addrspace(1) %[[BASE]], i32 0, i32 0
+// OGCG: load i32, ptr addrspace(1) %[[B]]
+
+AS1 B *to_base(AS1 D *d) { return d; }
+
+// CIR-LABEL: cir.func {{.*}}@_Z7to_basePU3AS11D
+// CIR: cir.base_class_addr %{{.*}} [4] : !cir.ptr<!rec_D, target_address_space(1)> -> !cir.ptr<!rec_B, target_address_space(1)>
+
+// LLVM-LABEL: define {{.*}}@_Z7to_basePU3AS11D
+// LLVM: %[[ISNULL:.*]] = icmp eq ptr addrspace(1) %[[D:.*]], null
+// LLVM: %[[BASE:.*]] = getelementptr i8, ptr addrspace(1) %[[D]], i32 4
+// LLVM: select i1 %[[ISNULL]], ptr addrspace(1) %[[D]], ptr addrspace(1) %[[BASE]]
+
+// OGCG-LABEL: define {{.*}}@_Z7to_basePU3AS11D
+// OGCG: %[[BASE:.*]] = getelementptr inbounds i8, ptr addrspace(1) %{{.*}}, i64 4
+// OGCG: phi ptr addrspace(1) [ %[[BASE]], %{{.*}} ], [ null, %{{.*}} ]
+
+AS1 D *to_derived(AS1 B *b) { return static_cast<AS1 D *>(b); }
+
+// CIR-LABEL: cir.func {{.*}}@_Z10to_derivedPU3AS11B
+// CIR: cir.derived_class_addr %{{.*}} [4] : !cir.ptr<!rec_B, target_address_space(1)> -> !cir.ptr<!rec_D, target_address_space(1)>
+
+// LLVM-LABEL: define {{.*}}@_Z10to_derivedPU3AS11B
+// LLVM: %[[ISNULL:.*]] = icmp eq ptr addrspace(1) %[[B:.*]], null
+// LLVM: %[[DERIVED:.*]] = getelementptr inbounds i8, ptr addrspace(1) %[[B]], i32 -4
+// LLVM: select i1 %[[ISNULL]], ptr addrspace(1) %[[B]], ptr addrspace(1) %[[DERIVED]]
+
+// OGCG-LABEL: define {{.*}}@_Z10to_derivedPU3AS11B
+// OGCG: %[[DERIVED:.*]] = getelementptr inbounds i8, ptr addrspace(1) %{{.*}}, i64 -4
+// OGCG: phi ptr addrspace(1) [ %[[DERIVED]], %{{.*}} ], [ null, %{{.*}} ]
+
+#ifdef VBASE
+struct V { int v; };
+struct W : virtual V { int w; };
+
+AS1 V *to_virtual_base(AS1 W *w) { return w; }
+
+// CIR-VBASE-LABEL: cir.func {{.*}}@_Z15to_virtual_basePU3AS11W
+// CIR-VBASE: cir.ternary
+// CIR-VBASE: cir.const #cir.ptr<null> : !cir.ptr<!rec_V, target_address_space(1)>
+// CIR-VBASE: }, false {
+// CIR-VBASE: %[[BYTES:.*]] = cir.cast bitcast %{{.*}} : !cir.ptr<!rec_W, target_address_space(1)> -> !cir.ptr<!u8i, target_address_space(1)>
+// CIR-VBASE: cir.ptr_stride %[[BYTES]], %{{.*}} : (!cir.ptr<!u8i, target_address_space(1)>, !s64i) -> !cir.ptr<!u8i, target_address_space(1)>
+// CIR-VBASE: }) : (!cir.bool) -> !cir.ptr<!rec_V, target_address_space(1)>
+#endif
diff --git a/clang/test/CIR/IR/invalid-class-addr.cir b/clang/test/CIR/IR/invalid-class-addr.cir
new file mode 100644
index 0000000000000..833694cc3284d
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-class-addr.cir
@@ -0,0 +1,27 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s32i = !cir.int<s, 32>
+!rec_B = !cir.struct<"B" {data !s32i}>
+!rec_D = !cir.struct<"D" {data !rec_B}>
+
+module {
+ cir.func @base_class_addr_address_space_mismatch(%arg0: !cir.ptr<!rec_D, target_address_space(1)>) {
+ // expected-error @below {{address space mismatch}}
+ %0 = cir.base_class_addr %arg0 [0] : !cir.ptr<!rec_D, target_address_space(1)> -> !cir.ptr<!rec_B>
+ cir.return
+ }
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!rec_B = !cir.struct<"B" {data !s32i}>
+!rec_D = !cir.struct<"D" {data !rec_B}>
+
+module {
+ cir.func @derived_class_addr_address_space_mismatch(%arg0: !cir.ptr<!rec_B, target_address_space(1)>) {
+ // expected-error @below {{address space mismatch}}
+ %0 = cir.derived_class_addr %arg0 [0] : !cir.ptr<!rec_B, target_address_space(1)> -> !cir.ptr<!rec_D>
+ cir.return
+ }
+}
>From ded84d47a7647c3835841315b7ec60384990a2af Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Sat, 10 Oct 2026 21:57:18 +0200
Subject: [PATCH 2/2] [mlir][LLVM] Put personality pointer in program address
space
Avoid crashes on targets where functions are not in address space 0
---
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 3 ++-
mlir/test/Target/LLVMIR/llvmir.mlir | 12 ++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 4757a4ca43219..07b9d683d309d 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -1697,7 +1697,8 @@ LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
// Check the personality and set it.
if (func.getPersonality()) {
- llvm::Type *ty = llvm::PointerType::getUnqual(llvmFunc->getContext());
+ llvm::Type *ty = llvm::PointerType::get(
+ llvmContext, llvmModule->getDataLayout().getProgramAddressSpace());
if (llvm::Constant *pfunc = getLLVMConstant(ty, func.getPersonalityAttr(),
func.getLoc(), *this))
llvmFunc->setPersonalityFn(pfunc);
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index f29184beb6b4d..252e24d1da81b 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -1817,6 +1817,18 @@ llvm.func @invoke_phis() -> i32 attributes { personality = @__gxx_personality_v0
// -----
+module attributes {llvm.data_layout = "P4"} {
+ llvm.func @__gxx_personality_v0(...) -> i32
+
+ // CHECK-LABEL: @personality_program_as
+ // CHECK-SAME: personality ptr addrspace(4) @__gxx_personality_v0
+ llvm.func @personality_program_as() attributes { personality = @__gxx_personality_v0 } {
+ llvm.return
+ }
+}
+
+// -----
+
// CHECK-LABEL: @hasGCFunction
// CHECK-SAME: gc "statepoint-example"
llvm.func @hasGCFunction() attributes { garbageCollector = "statepoint-example" } {
More information about the Mlir-commits
mailing list