[clang] [Clang][Sema] Promote pointers to common address space in pointer subtraction (PR #217341)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 28 23:25:58 PDT 2026
https://github.com/addmisol updated https://github.com/llvm/llvm-project/pull/217341
>From e1c96a3cc3e5527c02d63419290c0f7da5732d8e Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Wed, 19 Aug 2026 19:01:57 +0530
Subject: [PATCH 1/4] [Clang][Sema] Promote pointers to common address space in
pointer subtraction
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
clang/lib/Sema/SemaExpr.cpp | 31 +++++++++++++++++++++++++++++-
clang/test/CodeGenOpenCL/size_t.cl | 11 ++++++-----
2 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d06079a43e23a..1cf38a52f5932 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5765,7 +5765,7 @@ struct EnsureImmediateInvocationInDefaultArgs
// is not always done in the context of the template instantiator,
// we run the risk of producing a dependent source location
// that would never be rebuilt.
- // This usually happens during overloadĀ resolution, or in contexts
+ // This usually happens during overload resolution, or in contexts
// where the value of the source location does not matter.
// However, we should find a better way to deal with source location
// of function templates.
@@ -11902,6 +11902,35 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
LHS.get(), RHS.get()))
return QualType();
+ // For pointer subtraction, if the address spaces differ but overlap,
+ // convert both pointers to the composite (superset) address space.
+ // This is needed because pointer subtraction compares addresses, and
+ // addresses in different spaces may have different sizes.
+ LangAS LAddrSpace = lpointee.getAddressSpace();
+ LangAS RAddrSpace = rpointee.getAddressSpace();
+ if (LAddrSpace != RAddrSpace) {
+ Qualifiers LQual = lpointee.getQualifiers();
+ Qualifiers RQual = rpointee.getQualifiers();
+ LangAS ResultAddrSpace;
+ if (LQual.isAddressSpaceSupersetOf(RQual, Context))
+ ResultAddrSpace = LAddrSpace;
+ else
+ ResultAddrSpace = RAddrSpace;
+
+ if (LAddrSpace != ResultAddrSpace) {
+ QualType NewPteTy = Context.getAddrSpaceQualType(
+ lpointee.getUnqualifiedType(), ResultAddrSpace);
+ QualType NewPtrTy = Context.getPointerType(NewPteTy);
+ LHS = ImpCastExprToType(LHS.get(), NewPtrTy, CK_AddressSpaceConversion);
+ }
+ if (RAddrSpace != ResultAddrSpace) {
+ QualType NewPteTy = Context.getAddrSpaceQualType(
+ rpointee.getUnqualifiedType(), ResultAddrSpace);
+ QualType NewPtrTy = Context.getPointerType(NewPteTy);
+ RHS = ImpCastExprToType(RHS.get(), NewPtrTy, CK_AddressSpaceConversion);
+ }
+ }
+
bool LHSIsNullPtr = LHS.get()->IgnoreParenCasts()->isNullPointerConstant(
Context, Expr::NPC_ValueDependentIsNotNull);
bool RHSIsNullPtr = RHS.get()->IgnoreParenCasts()->isNullPointerConstant(
diff --git a/clang/test/CodeGenOpenCL/size_t.cl b/clang/test/CodeGenOpenCL/size_t.cl
index c6fee33ac6298..7504fd69be5e8 100644
--- a/clang/test/CodeGenOpenCL/size_t.cl
+++ b/clang/test/CodeGenOpenCL/size_t.cl
@@ -117,16 +117,17 @@ ptrdiff_t test_sub_private(private char* x, private char *y) {
}
//SZ32: define{{.*}} i32 @test_sub_mix(ptr noundef %x, ptr addrspace(4) noundef %y)
-//SZ32: ptrtoaddr ptr %{{.*}} to i32
+//SZ32: addrspacecast ptr %{{.*}} to ptr addrspace(4)
+//SZ32: ptrtoaddr ptr addrspace(4) %{{.*}} to i32
//SZ32: ptrtoaddr ptr addrspace(4) %{{.*}} to i32
//SZ64ONLY: define{{.*}} i64 @test_sub_mix(ptr noundef %x, ptr addrspace(4) noundef %y)
-//SZ64ONLY: ptrtoaddr ptr %{{.*}} to i64
+//SZ64ONLY: addrspacecast ptr %{{.*}} to ptr addrspace(4)
+//SZ64ONLY: ptrtoaddr ptr addrspace(4) %{{.*}} to i64
//SZ64ONLY: ptrtoaddr ptr addrspace(4) %{{.*}} to i64
//AMDGCN: define{{.*}} i64 @test_sub_mix(ptr addrspace(5) noundef %x, ptr noundef %y)
-//AMDGCN: ptrtoaddr ptr addrspace(5) %{{.*}} to i32
+//AMDGCN: addrspacecast ptr addrspace(5) %{{.*}} to ptr
+//AMDGCN: ptrtoaddr ptr %{{.*}} to i64
//AMDGCN: ptrtoaddr ptr %{{.*}} to i64
-//AMDGCN: zext i32 %{{.*}} to i64
ptrdiff_t test_sub_mix(private char* x, generic char *y) {
return x - y;
}
-
>From ebeb028f552d52e648137a2e2873455be48440ec Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Thu, 20 Aug 2026 17:50:32 +0530
Subject: [PATCH 2/4] Fix issue
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
clang/lib/Sema/SemaExpr.cpp | 6 ++++--
clang/test/CodeGenOpenCL/address-spaces-conversions.cl | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 1cf38a52f5932..1608a285ef33e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -11921,13 +11921,15 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
QualType NewPteTy = Context.getAddrSpaceQualType(
lpointee.getUnqualifiedType(), ResultAddrSpace);
QualType NewPtrTy = Context.getPointerType(NewPteTy);
- LHS = ImpCastExprToType(LHS.get(), NewPtrTy, CK_AddressSpaceConversion);
+ LHS = ImpCastExprToType(LHS.get(), NewPtrTy,
+ CK_AddressSpaceConversion);
}
if (RAddrSpace != ResultAddrSpace) {
QualType NewPteTy = Context.getAddrSpaceQualType(
rpointee.getUnqualifiedType(), ResultAddrSpace);
QualType NewPtrTy = Context.getPointerType(NewPteTy);
- RHS = ImpCastExprToType(RHS.get(), NewPtrTy, CK_AddressSpaceConversion);
+ RHS = ImpCastExprToType(RHS.get(), NewPtrTy,
+ CK_AddressSpaceConversion);
}
}
diff --git a/clang/test/CodeGenOpenCL/address-spaces-conversions.cl b/clang/test/CodeGenOpenCL/address-spaces-conversions.cl
index 15ad3b8ba48ea..20f7bbfbf451f 100644
--- a/clang/test/CodeGenOpenCL/address-spaces-conversions.cl
+++ b/clang/test/CodeGenOpenCL/address-spaces-conversions.cl
@@ -30,8 +30,9 @@ void test(global int *arg_glob, generic int *arg_gen,
// CHECK-NOFAKE-NOT: addrspacecast
var_priv = arg_gen - arg_glob; // arithmetic operation
+ // CHECK: %{{.*}} = addrspacecast ptr addrspace(1) %{{.*}} to ptr addrspace(4)
+ // CHECK: %{{.*}} = ptrtoaddr ptr addrspace(4) %{{.*}} to i64
// CHECK: %{{.*}} = ptrtoaddr ptr addrspace(4) %{{.*}} to i64
- // CHECK: %{{.*}} = ptrtoaddr ptr addrspace(1) %{{.*}} to i64
// CHECK-NOFAKE: %{{.*}} = ptrtoaddr ptr %{{.*}} to i64
// CHECK-NOFAKE: %{{.*}} = ptrtoaddr ptr %{{.*}} to i64
>From 5a5efed240d193ef4d39479a41299cb070f603e8 Mon Sep 17 00:00:00 2001
From: addmisol <addmisol9 at gmail.com>
Date: Fri, 21 Aug 2026 21:06:20 +0530
Subject: [PATCH 3/4] fix: Address comments
Signed-off-by: addmisol <addmisol9 at gmail.com>
---
clang/lib/Sema/SemaExpr.cpp | 20 +++---
.../address-spaces-conversions.cl | 6 +-
clang/test/CodeGenOpenCL/size_t.cl | 63 +++++++++++++++++--
3 files changed, 69 insertions(+), 20 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 89fcdc6fc49d6..e8db231740a6a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -11904,32 +11904,30 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
// For pointer subtraction, if the address spaces differ but overlap,
// convert both pointers to the composite (superset) address space.
- // This is needed because pointer subtraction compares addresses, and
- // addresses in different spaces may have different sizes.
+ // This is needed because address spaces may use different
+ // representations, such as a private offset vs a flat address.
LangAS LAddrSpace = lpointee.getAddressSpace();
LangAS RAddrSpace = rpointee.getAddressSpace();
if (LAddrSpace != RAddrSpace) {
Qualifiers LQual = lpointee.getQualifiers();
Qualifiers RQual = rpointee.getQualifiers();
- LangAS ResultAddrSpace;
- if (LQual.isAddressSpaceSupersetOf(RQual, Context))
- ResultAddrSpace = LAddrSpace;
- else
- ResultAddrSpace = RAddrSpace;
+ LangAS ResultAddrSpace = LQual.isAddressSpaceSupersetOf(RQual, Context)
+ ? LAddrSpace
+ : RAddrSpace;
if (LAddrSpace != ResultAddrSpace) {
QualType NewPteTy = Context.getAddrSpaceQualType(
lpointee.getUnqualifiedType(), ResultAddrSpace);
QualType NewPtrTy = Context.getPointerType(NewPteTy);
- LHS = ImpCastExprToType(LHS.get(), NewPtrTy,
- CK_AddressSpaceConversion);
+ LHS =
+ ImpCastExprToType(LHS.get(), NewPtrTy, CK_AddressSpaceConversion);
}
if (RAddrSpace != ResultAddrSpace) {
QualType NewPteTy = Context.getAddrSpaceQualType(
rpointee.getUnqualifiedType(), ResultAddrSpace);
QualType NewPtrTy = Context.getPointerType(NewPteTy);
- RHS = ImpCastExprToType(RHS.get(), NewPtrTy,
- CK_AddressSpaceConversion);
+ RHS =
+ ImpCastExprToType(RHS.get(), NewPtrTy, CK_AddressSpaceConversion);
}
}
diff --git a/clang/test/CodeGenOpenCL/address-spaces-conversions.cl b/clang/test/CodeGenOpenCL/address-spaces-conversions.cl
index 20f7bbfbf451f..04d38a62f4f65 100644
--- a/clang/test/CodeGenOpenCL/address-spaces-conversions.cl
+++ b/clang/test/CodeGenOpenCL/address-spaces-conversions.cl
@@ -30,9 +30,9 @@ void test(global int *arg_glob, generic int *arg_gen,
// CHECK-NOFAKE-NOT: addrspacecast
var_priv = arg_gen - arg_glob; // arithmetic operation
- // CHECK: %{{.*}} = addrspacecast ptr addrspace(1) %{{.*}} to ptr addrspace(4)
- // CHECK: %{{.*}} = ptrtoaddr ptr addrspace(4) %{{.*}} to i64
- // CHECK: %{{.*}} = ptrtoaddr ptr addrspace(4) %{{.*}} to i64
+ // CHECK: [[SUBCAST:%.*]] = addrspacecast ptr addrspace(1) %{{.*}} to ptr addrspace(4)
+ // CHECK: ptrtoaddr ptr addrspace(4) %{{.*}} to i64
+ // CHECK: ptrtoaddr ptr addrspace(4) [[SUBCAST]] to i64
// CHECK-NOFAKE: %{{.*}} = ptrtoaddr ptr %{{.*}} to i64
// CHECK-NOFAKE: %{{.*}} = ptrtoaddr ptr %{{.*}} to i64
diff --git a/clang/test/CodeGenOpenCL/size_t.cl b/clang/test/CodeGenOpenCL/size_t.cl
index 7504fd69be5e8..2fbef1b81daa4 100644
--- a/clang/test/CodeGenOpenCL/size_t.cl
+++ b/clang/test/CodeGenOpenCL/size_t.cl
@@ -117,17 +117,68 @@ ptrdiff_t test_sub_private(private char* x, private char *y) {
}
//SZ32: define{{.*}} i32 @test_sub_mix(ptr noundef %x, ptr addrspace(4) noundef %y)
-//SZ32: addrspacecast ptr %{{.*}} to ptr addrspace(4)
-//SZ32: ptrtoaddr ptr addrspace(4) %{{.*}} to i32
+//SZ32: [[CAST1:%.*]] = addrspacecast ptr %{{.*}} to ptr addrspace(4)
+//SZ32: ptrtoaddr ptr addrspace(4) [[CAST1]] to i32
//SZ32: ptrtoaddr ptr addrspace(4) %{{.*}} to i32
//SZ64ONLY: define{{.*}} i64 @test_sub_mix(ptr noundef %x, ptr addrspace(4) noundef %y)
-//SZ64ONLY: addrspacecast ptr %{{.*}} to ptr addrspace(4)
-//SZ64ONLY: ptrtoaddr ptr addrspace(4) %{{.*}} to i64
+//SZ64ONLY: [[CAST2:%.*]] = addrspacecast ptr %{{.*}} to ptr addrspace(4)
+//SZ64ONLY: ptrtoaddr ptr addrspace(4) [[CAST2]] to i64
//SZ64ONLY: ptrtoaddr ptr addrspace(4) %{{.*}} to i64
//AMDGCN: define{{.*}} i64 @test_sub_mix(ptr addrspace(5) noundef %x, ptr noundef %y)
-//AMDGCN: addrspacecast ptr addrspace(5) %{{.*}} to ptr
-//AMDGCN: ptrtoaddr ptr %{{.*}} to i64
+//AMDGCN: [[CAST3:%.*]] = addrspacecast ptr addrspace(5) %{{.*}} to ptr
+//AMDGCN: ptrtoaddr ptr [[CAST3]] to i64
//AMDGCN: ptrtoaddr ptr %{{.*}} to i64
ptrdiff_t test_sub_mix(private char* x, generic char *y) {
return x - y;
}
+
+// Test commuted case: generic - private
+//SZ32: define{{.*}} i32 @test_sub_mix_commute(ptr addrspace(4) noundef %x, ptr noundef %y)
+//SZ32: [[CASTC1:%.*]] = addrspacecast ptr %{{.*}} to ptr addrspace(4)
+//SZ32: ptrtoaddr ptr addrspace(4) %{{.*}} to i32
+//SZ32: ptrtoaddr ptr addrspace(4) [[CASTC1]] to i32
+//SZ64ONLY: define{{.*}} i64 @test_sub_mix_commute(ptr addrspace(4) noundef %x, ptr noundef %y)
+//SZ64ONLY: [[CASTC2:%.*]] = addrspacecast ptr %{{.*}} to ptr addrspace(4)
+//SZ64ONLY: ptrtoaddr ptr addrspace(4) %{{.*}} to i64
+//SZ64ONLY: ptrtoaddr ptr addrspace(4) [[CASTC2]] to i64
+//AMDGCN: define{{.*}} i64 @test_sub_mix_commute(ptr noundef %x, ptr addrspace(5) noundef %y)
+//AMDGCN: [[CASTC3:%.*]] = addrspacecast ptr addrspace(5) %{{.*}} to ptr
+//AMDGCN: ptrtoaddr ptr %{{.*}} to i64
+//AMDGCN: ptrtoaddr ptr [[CASTC3]] to i64
+ptrdiff_t test_sub_mix_commute(generic char* x, private char *y) {
+ return x - y;
+}
+
+// Test local - generic subtraction
+//SZ32: define{{.*}} i32 @test_sub_local_generic(ptr addrspace(3) noundef %x, ptr addrspace(4) noundef %y)
+//SZ32: [[CASTLG1:%.*]] = addrspacecast ptr addrspace(3) %{{.*}} to ptr addrspace(4)
+//SZ32: ptrtoaddr ptr addrspace(4) [[CASTLG1]] to i32
+//SZ32: ptrtoaddr ptr addrspace(4) %{{.*}} to i32
+//SZ64ONLY: define{{.*}} i64 @test_sub_local_generic(ptr addrspace(3) noundef %x, ptr addrspace(4) noundef %y)
+//SZ64ONLY: [[CASTLG2:%.*]] = addrspacecast ptr addrspace(3) %{{.*}} to ptr addrspace(4)
+//SZ64ONLY: ptrtoaddr ptr addrspace(4) [[CASTLG2]] to i64
+//SZ64ONLY: ptrtoaddr ptr addrspace(4) %{{.*}} to i64
+//AMDGCN: define{{.*}} i64 @test_sub_local_generic(ptr addrspace(3) noundef %x, ptr noundef %y)
+//AMDGCN: [[CASTLG3:%.*]] = addrspacecast ptr addrspace(3) %{{.*}} to ptr
+//AMDGCN: ptrtoaddr ptr [[CASTLG3]] to i64
+//AMDGCN: ptrtoaddr ptr %{{.*}} to i64
+ptrdiff_t test_sub_local_generic(local char* x, generic char *y) {
+ return x - y;
+}
+
+// Test generic - local subtraction (commuted)
+//SZ32: define{{.*}} i32 @test_sub_generic_local(ptr addrspace(4) noundef %x, ptr addrspace(3) noundef %y)
+//SZ32: [[CASTGL1:%.*]] = addrspacecast ptr addrspace(3) %{{.*}} to ptr addrspace(4)
+//SZ32: ptrtoaddr ptr addrspace(4) %{{.*}} to i32
+//SZ32: ptrtoaddr ptr addrspace(4) [[CASTGL1]] to i32
+//SZ64ONLY: define{{.*}} i64 @test_sub_generic_local(ptr addrspace(4) noundef %x, ptr addrspace(3) noundef %y)
+//SZ64ONLY: [[CASTGL2:%.*]] = addrspacecast ptr addrspace(3) %{{.*}} to ptr addrspace(4)
+//SZ64ONLY: ptrtoaddr ptr addrspace(4) %{{.*}} to i64
+//SZ64ONLY: ptrtoaddr ptr addrspace(4) [[CASTGL2]] to i64
+//AMDGCN: define{{.*}} i64 @test_sub_generic_local(ptr noundef %x, ptr addrspace(3) noundef %y)
+//AMDGCN: [[CASTGL3:%.*]] = addrspacecast ptr addrspace(3) %{{.*}} to ptr
+//AMDGCN: ptrtoaddr ptr %{{.*}} to i64
+//AMDGCN: ptrtoaddr ptr [[CASTGL3]] to i64
+ptrdiff_t test_sub_generic_local(generic char* x, local char *y) {
+ return x - y;
+}
>From 5632314393a541cfbeb311e53aed7f66de0b0490 Mon Sep 17 00:00:00 2001
From: Addmisol <addmisol9 at gmail.com>
Date: Fri, 21 Aug 2026 21:10:30 +0530
Subject: [PATCH 4/4] fix: Remove unnecessary change
---
clang/lib/Sema/SemaExpr.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e8db231740a6a..a74ab9b04af56 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5765,7 +5765,7 @@ struct EnsureImmediateInvocationInDefaultArgs
// is not always done in the context of the template instantiator,
// we run the risk of producing a dependent source location
// that would never be rebuilt.
- // This usually happens during overload resolution, or in contexts
+ // This usually happens during overloadĀ resolution, or in contexts
// where the value of the source location does not matter.
// However, we should find a better way to deal with source location
// of function templates.
More information about the cfe-commits
mailing list