[clang] 5e5e63d - [clang][bytecode] Add an `IsNull` bit to integral pointers (#202356)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 23 00:29:43 PDT 2026
Author: Timm Baeder
Date: 2026-09-23T09:29:34+02:00
New Revision: 5e5e63dfc728c38b5aab46b8cd61325fedc562ea
URL: https://github.com/llvm/llvm-project/commit/5e5e63dfc728c38b5aab46b8cd61325fedc562ea
DIFF: https://github.com/llvm/llvm-project/commit/5e5e63dfc728c38b5aab46b8cd61325fedc562ea.diff
LOG: [clang][bytecode] Add an `IsNull` bit to integral pointers (#202356)
It isn't always the same as 'value == 0'.
Added:
clang/test/AST/ByteCode/codegen.cl
Modified:
clang/lib/AST/ByteCode/Compiler.cpp
clang/lib/AST/ByteCode/Interp.h
clang/lib/AST/ByteCode/Opcodes.td
clang/lib/AST/ByteCode/Pointer.cpp
clang/lib/AST/ByteCode/Pointer.h
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index ecece6f08f3ab..99ad69562e314 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -734,10 +734,29 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
case CK_NonAtomicToAtomic:
case CK_NoOp:
case CK_UserDefinedConversion:
- case CK_AddressSpaceConversion:
case CK_CPointerToObjCPointerCast:
return this->delegate(SubExpr);
+ case CK_AddressSpaceConversion: {
+ if (E->containsErrors())
+ return false;
+
+ if (!this->visit(SubExpr))
+ return false;
+
+ uint64_t Val;
+ if (E->getType()->isPointerType())
+ Val = Ctx.getASTContext().getTargetNullPointerValue(E->getType());
+ else
+ Val = 0;
+
+ if (!this->emitCastAddressSpace(Val, E->getType().getTypePtr(), E))
+ return false;
+ if (DiscardResult)
+ return this->emitPopPtr(E);
+ return true;
+ }
+
case CK_BitCast: {
if (E->containsErrors())
return false;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index d95d799288938..2eab69431c94c 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -2581,9 +2581,9 @@ std::optional<Pointer> OffsetHelper(InterpState &S, CodePtr OpPC,
: S.getASTContext().getTypeSizeInChars(ElemType).getQuantity();
uint64_t O = static_cast<uint64_t>(Offset) * ElemSize;
if constexpr (Op == ArithOp::Add) {
- return Pointer(V + O, Ptr.asIntPointer().Ty);
+ return Pointer(V + O, Ptr.asIntPointer().getType());
} else
- return Pointer(V - O, Ptr.asIntPointer().Ty);
+ return Pointer(V - O, Ptr.asIntPointer().getType());
} else if (Ptr.isFunctionPointer()) {
uint64_t O = static_cast<uint64_t>(Offset);
uint64_t N;
@@ -3202,7 +3202,20 @@ template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool Null(InterpState &S, uint64_t Value, const Type *Ty) {
// FIXME(perf): This is a somewhat often-used function and the value of a
// null pointer is almost always 0.
- S.Stk.push<T>(Value, Ty);
+ if constexpr (std::is_same_v<T, Pointer>)
+ S.Stk.push<T>(Value, Ty, /*Offset=*/0, /*IsNull=*/true);
+ else
+ S.Stk.push<T>(Value, Ty);
+ return true;
+}
+
+inline bool CastAddressSpace(InterpState &S, CodePtr OpPC, uint64_t Value,
+ const Type *Ty) {
+ const Pointer Ptr = S.Stk.pop<Pointer>();
+ if (Ptr.isZero())
+ S.Stk.push<Pointer>(Value, Ty);
+ else
+ S.Stk.push<Pointer>(Ptr);
return true;
}
@@ -3673,7 +3686,10 @@ inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Type *Ty) {
S.P.getFunction((const FunctionDecl *)IntVal.getPtr());
S.Stk.push<Pointer>(F, IntVal.getOffset());
} else {
- S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Ty);
+ uint64_t NullValue =
+ S.getASTContext().getTargetNullPointerValue(QualType(Ty, 0));
+ S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Ty, 0,
+ static_cast<uint64_t>(IntVal) == NullValue);
}
} else {
S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Ty);
@@ -4009,7 +4025,8 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc,
return false;
// If this failed and is nothrow, just return a null ptr.
- S.Stk.push<Pointer>(0, ElementDesc->getType().getTypePtr());
+ S.Stk.push<Pointer>(0, ElementDesc->getType().getTypePtr(), 0,
+ /*IsNull=*/true);
return true;
}
if (NumElements.isNegative()) {
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 78d99bf3c433f..fcbd7461b6b5b 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -327,6 +327,10 @@ def Null : SuccessOpcode {
let HasGroup = 1;
}
+def CastAddressSpace : Opcode {
+ let Args = [ArgUint64, ArgTypePtr];
+}
+
//===----------------------------------------------------------------------===//
// Pointer generation
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index fb05efe5a62c3..bf9900cdab21f 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -212,7 +212,8 @@ bool Pointer::operator==(const Pointer &P) const {
switch (StorageKind) {
case Storage::Int:
- return P.Int.Value == Int.Value && P.Int.Ty == Int.Ty && P.Offset == Offset;
+ return P.Int.Value == Int.Value && P.Int.getType() == Int.getType() &&
+ P.Offset == Offset;
case Storage::Block:
return P.view() == view();
case Storage::Fn:
@@ -477,7 +478,8 @@ void Pointer::print(llvm::raw_ostream &OS) const {
OS << "}";
} break;
case Storage::Int:
- OS << "(Int) {" << Int.Value << " + " << Offset << ", " << Int.Ty << "}";
+ OS << "(Int) {" << Int.Value << " + " << Offset << ", " << Int.getType()
+ << ", " << (Int.isNull() ? "null" : "nonnull") << '}';
break;
case Storage::Fn:
OS << "(Fn) { " << Fn.Func << " + " << Offset << " }";
@@ -1361,12 +1363,13 @@ std::optional<IntPointer> IntPointer::atOffset(const interp::Context &Ctx,
ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))
.getQuantity();
- return IntPointer{FD->getType().getTypePtr(), this->Value + FieldOffset};
+ uint64_t NewValue = this->Value + FieldOffset;
+ return IntPointer{{FD->getType().getTypePtr(), NewValue == 0}, NewValue};
}
IntPointer IntPointer::baseCast(const interp::Context &Ctx,
unsigned BaseOffset) const {
- if (!Ty)
+ if (!getType())
return *this;
QualType CurType = getPointeeType();
@@ -1395,7 +1398,8 @@ IntPointer IntPointer::baseCast(const interp::Context &Ctx,
const RecordDecl *RD = BaseDesc->ElemRecord->getDecl();
QualType T = RD->getASTContext().getTagType(ElaboratedTypeKeyword::None,
std::nullopt, RD, false);
- return {T.getTypePtr(), Value + BaseLayoutOffset.getQuantity()};
+ uint64_t NewValue = Value + BaseLayoutOffset.getQuantity();
+ return {{T.getTypePtr(), NewValue == 0}, NewValue};
}
std::optional<size_t>
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 54e0f858b4fa0..be59580577d14 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -353,17 +353,20 @@ struct BlockPointer {
};
struct IntPointer {
- const Type *Ty;
+ llvm::PointerIntPair<const Type *, 1, bool> TypeAndIsNull;
uint64_t Value;
std::optional<IntPointer> atOffset(const Context &Ctx, unsigned Offset) const;
IntPointer baseCast(const Context &Ctx, unsigned BaseOffset) const;
+ const Type *getType() const { return TypeAndIsNull.getPointer(); }
+ bool isNull() const { return TypeAndIsNull.getInt(); }
+
QualType getPointeeType() const {
- if (!Ty)
+ if (!getType())
return QualType();
- QualType QT(Ty, 0);
+ QualType QT(getType(), 0);
if (QT->isPointerOrReferenceType())
QT = QT->getPointeeType();
else if (QT->isArrayType())
@@ -535,15 +538,17 @@ enum class Storage { Int, Block, Fn, Typeid, String, Opaque };
/// \endverbatim
class Pointer {
public:
- Pointer() : StorageKind(Storage::Int), Int{nullptr, 0} {}
+ Pointer() : StorageKind(Storage::Int), Int{{nullptr, true}, 0} {}
Pointer(IntPointer &&IntPtr)
: StorageKind(Storage::Int), Int(std::move(IntPtr)) {}
Pointer(Block *B);
Pointer(Block *B, uint64_t BaseAndOffset);
Pointer(const Pointer &P);
Pointer(Pointer &&P);
- Pointer(uint64_t Address, const Type *Ty, uint64_t Offset = 0)
- : Offset(Offset), StorageKind(Storage::Int), Int{Ty, Address} {}
+ Pointer(uint64_t Address, const Type *Ty, uint64_t Offset = 0,
+ std::optional<bool> IsNull = std::nullopt)
+ : Offset(Offset), StorageKind(Storage::Int),
+ Int{{Ty, IsNull.value_or(Address == 0)}, Address} {}
Pointer(const Function *F, uint64_t Offset = 0)
: Offset(Offset), StorageKind(Storage::Fn), Fn{F} {}
Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0)
@@ -603,7 +608,7 @@ class Pointer {
[[nodiscard]] Pointer atIndex(uint64_t Idx) const {
switch (StorageKind) {
case Storage::Int:
- return Pointer(Int.Value, Int.Ty, Idx);
+ return Pointer(Int.Value, Int.getType(), Idx);
case Storage::Block:
return Pointer(view().atIndex(Idx));
case Storage::Fn:
@@ -646,7 +651,7 @@ class Pointer {
bool isZero() const {
switch (StorageKind) {
case Storage::Int:
- return Int.Value == 0 && Offset == 0;
+ return Int.isNull();
case Storage::Block:
return BS.Pointee == nullptr;
case Storage::Fn:
diff --git a/clang/test/AST/ByteCode/codegen.cl b/clang/test/AST/ByteCode/codegen.cl
new file mode 100644
index 0000000000000..d2e331905e02d
--- /dev/null
+++ b/clang/test/AST/ByteCode/codegen.cl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -no-enable-noundef-analysis %s -cl-std=CL2.0 -triple amdgcn -fcommon -O0 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -no-enable-noundef-analysis %s -cl-std=CL2.0 -triple amdgcn -fcommon -O0 -emit-llvm -o - -fexperimental-new-constant-interpreter | FileCheck %s
+
+// CHECK: @fold_int_local ={{.*}} addrspace(1) global i32 13, align 4
+int fold_int_local = (int)(local void*)(generic char*)(global int*)0 + 14;
+
+// CHECK: @fold_int ={{.*}} addrspace(1) global i32 13, align 4
+int fold_int = (int)(private void*)(generic char*)(global int*)0 + 14;
+
+// CHECK: @test_static_var_private.sp4 = internal addrspace(1) global ptr addrspace(5) null, align 4
+// CHECK: @test_static_var_private.sp5 = internal addrspace(1) global ptr addrspace(5) addrspacecast (ptr null to ptr addrspace(5)), align 4
+
+void test_static_var_private(void) {
+ static private char *sp4 = (private char*)((void)0, 0);
+ const int x = 0;
+ static private char *sp5 = (private char*)x;
+}
More information about the cfe-commits
mailing list