[clang] 2380272 - [CodeGen] Fix emission of function pointer casts with non-zero program AS (#186210)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 13 08:16:12 PDT 2026
Author: Nick Sarnie
Date: 2026-03-13T15:16:07Z
New Revision: 2380272f76c97f84d7efe2cd3db2b319e4654903
URL: https://github.com/llvm/llvm-project/commit/2380272f76c97f84d7efe2cd3db2b319e4654903
DIFF: https://github.com/llvm/llvm-project/commit/2380272f76c97f84d7efe2cd3db2b319e4654903.diff
LOG: [CodeGen] Fix emission of function pointer casts with non-zero program AS (#186210)
Imagine we have the following code:
```c++
void foo() {}
void bar() {
void *ptr = reinterpret_cast<void*>(foo);
}
```
Usually clang would treat this as a simple `bitcast`, but in the case
that the target has a non-default program address space, this needs to
be an `addrspacecast`.
Today, if we try to codegen this, we get an assert due to the two types
not being valid for a `bitcast`.
```
clang-23: /tmp/llvm/clang/lib/CodeGen/CGExprScalar.cpp:2661: llvm::Value* {anonymous}::ScalarExprEmitter::VisitCastExpr(clang::CastExpr*): Assertion `(!SrcTy->isPtrOrPtrVectorTy() || !DstTy->isPtrOrPtrVectorTy() || SrcTy->getPointerAddressSpace() == DstTy->getPointerAddressSpace()) && "Address-space cast must be used to convert address spaces"' failed.
```
The complicating issue is that Sema has no idea about the target's
default program AS, that's part of the LLVM target data layout which is
only known to CodeGen, so there I don't see a way we could represent
this as a `AddressSpaceConversion` -type `CastExpr` in Sema, the only
place we have the required info is during codegen for the `CastExpr`.
Modify the codegen of `BitCast`-type `CastExpr` to generate an LLVM
`addrspacecast` if necessary.
---------
Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
Added:
clang/test/CodeGenSPIRV/function-pointer-cast.cpp
Modified:
clang/lib/CodeGen/CGExprScalar.cpp
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index fd8db6f0dcfc2..100982fefc3b0 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2658,6 +2658,22 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
if (auto A = dyn_cast<llvm::Argument>(Src); A && A->hasStructRetAttr())
return CGF.performAddrSpaceCast(Src, DstTy);
+ // FIXME: Similarly to the sret case above, we need to handle BitCasts that
+ // involve implicit address space conversions. This arises when the source
+ // language lacks explicit address spaces, but the target's data layout
+ // assigns
diff erent address spaces (e.g., program address space for
+ // function pointers). Since Sema operates on Clang types (which don't carry
+ // this information) and selects CK_BitCast, we must detect the address
+ // space mismatch here in CodeGen when lowering to LLVM types. The most
+ // common case is casting function pointers (which get the program AS from
+ // the data layout) to/from object pointers (which use the default AS).
+ // Ideally, this would be resolved at a higher level, but that would require
+ // exposing data layout details to Sema.
+ if (SrcTy->isPtrOrPtrVectorTy() && DstTy->isPtrOrPtrVectorTy() &&
+ SrcTy->getPointerAddressSpace() != DstTy->getPointerAddressSpace()) {
+ return CGF.performAddrSpaceCast(Src, DstTy);
+ }
+
assert(
(!SrcTy->isPtrOrPtrVectorTy() || !DstTy->isPtrOrPtrVectorTy() ||
SrcTy->getPointerAddressSpace() == DstTy->getPointerAddressSpace()) &&
diff --git a/clang/test/CodeGenSPIRV/function-pointer-cast.cpp b/clang/test/CodeGenSPIRV/function-pointer-cast.cpp
new file mode 100644
index 0000000000000..5db711a1b377c
--- /dev/null
+++ b/clang/test/CodeGenSPIRV/function-pointer-cast.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple spirv64-intel %s -emit-llvm -o - | FileCheck %s
+
+// Test that function pointer casts properly handle address space conversions
+// on targets like spirv64-intel that use a non-default program address space.
+
+void foo() {}
+
+// CHECK-LABEL: define spir_func void @_Z21test_func_to_void_ptrv() addrspace(9)
+void test_func_to_void_ptr() {
+ void *ptr = (void*)foo;
+ // CHECK: store ptr addrspace(4) addrspacecast (ptr addrspace(9) @_Z3foov to ptr addrspace(4))
+}
+
+// CHECK-LABEL: define spir_func void @_Z21test_void_ptr_to_funcv() addrspace(9)
+void test_void_ptr_to_func() {
+ void *ptr = (void*)foo;
+ void (*fptr)() = (void (*)())ptr;
+ // CHECK: addrspacecast ptr addrspace(4) %{{.*}} to ptr addrspace(9)
+ fptr();
+}
+
+// CHECK-LABEL: define spir_func void @_Z25cxx_test_func_to_void_ptrv() addrspace(9)
+void cxx_test_func_to_void_ptr() {
+ void *ptr = reinterpret_cast<void*>(foo);
+ // CHECK: store ptr addrspace(4) addrspacecast (ptr addrspace(9) @_Z3foov to ptr addrspace(4))
+}
+
+// CHECK-LABEL: define spir_func void @_Z25cxx_test_void_ptr_to_funcv() addrspace(9)
+void cxx_test_void_ptr_to_func() {
+ void *ptr = reinterpret_cast<void*>(foo);
+ void (*fptr)() = reinterpret_cast<void (*)()>(ptr);
+ // CHECK: addrspacecast ptr addrspace(4) %{{.*}} to ptr addrspace(9)
+ fptr();
+}
More information about the cfe-commits
mailing list