[clang] [clang][bytecode] Add an `IsNull` bit to integral pointers (PR #202356)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 8 08:11:08 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
It isn't always the same as 'value == 0'.
---
Full diff: https://github.com/llvm/llvm-project/pull/202356.diff
6 Files Affected:
- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+30-2)
- (modified) clang/lib/AST/ByteCode/Interp.h (+21-6)
- (modified) clang/lib/AST/ByteCode/Opcodes.td (+5-1)
- (modified) clang/lib/AST/ByteCode/Pointer.cpp (+7-3)
- (modified) clang/lib/AST/ByteCode/Pointer.h (+7-4)
- (added) clang/test/AST/ByteCode/codegen.cl (+17)
``````````diff
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 76688e30a0acd..f35c745defab2 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -491,7 +491,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
Desc = P.createDescriptor(E, PtrType->getPointeeType().getTypePtr(),
Descriptor::InlineDescMD, /*IsConst=*/true);
- if (!this->emitGetIntPtr(T, Desc, E))
+ uint64_t Val = Ctx.getASTContext().getTargetNullPointerValue(E->getType());
+ if (!this->emitGetIntPtr(T, Desc, Val, E))
return false;
PrimType DestPtrT = classifyPrim(PtrType);
@@ -508,10 +509,37 @@ 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;
+
+ const Descriptor *Desc = nullptr;
+ const QualType PointeeType = E->getType()->getPointeeType();
+ uint64_t Val;
+ if (!PointeeType.isNull()) {
+ Val = Ctx.getASTContext().getTargetNullPointerValue(E->getType());
+ if (OptPrimType T = classify(PointeeType))
+ Desc = P.createDescriptor(SubExpr, *T);
+ else
+ Desc = P.createDescriptor(SubExpr, PointeeType.getTypePtr(),
+ std::nullopt, /*IsConst=*/true);
+ } else {
+ Val = 0;
+ }
+
+ if (!this->emitCastAddressSpace(Val, Desc, 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 d2ca122d0e805..a9b4b5f8ddfa8 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3099,7 +3099,20 @@ inline bool Null(InterpState &S, CodePtr OpPC, uint64_t Value,
const Descriptor *Desc) {
// 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, Desc);
+ if constexpr (std::is_same_v<T, Pointer>)
+ S.Stk.push<T>(Value, Desc, 0, true);
+ else
+ S.Stk.push<T>(Value, Desc);
+ return true;
+}
+
+inline bool CastAddressSpace(InterpState &S, CodePtr OpPC, uint64_t Value,
+ const Descriptor *Desc) {
+ const Pointer Ptr = S.Stk.pop<Pointer>();
+ if (Ptr.isZero())
+ S.Stk.push<Pointer>(Value, Desc);
+ else
+ S.Stk.push<Pointer>(Ptr);
return true;
}
@@ -3539,7 +3552,8 @@ inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) {
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
-inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
+inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
+ uint64_t NullValue) {
const T &IntVal = S.Stk.pop<T>();
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
@@ -3564,7 +3578,8 @@ inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
S.P.getFunction((const FunctionDecl *)IntVal.getPtr());
S.Stk.push<Pointer>(F, IntVal.getOffset());
} else {
- S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
+ S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc, 0,
+ static_cast<uint64_t>(IntVal) == NullValue);
}
} else {
S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
@@ -3848,7 +3863,7 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
return false;
// If this failed and is nothrow, just return a null ptr.
- S.Stk.push<Pointer>(0, nullptr);
+ S.Stk.push<Pointer>();
return true;
}
if (NumElements.isNegative()) {
@@ -3857,7 +3872,7 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
<< NumElements.toDiagnosticString(S.getASTContext());
return false;
}
- S.Stk.push<Pointer>(0, nullptr);
+ S.Stk.push<Pointer>();
return true;
}
@@ -3891,7 +3906,7 @@ 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);
+ S.Stk.push<Pointer>(0, ElementDesc, 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 4bd61cdce658d..035e16a161a4d 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -315,6 +315,10 @@ def Null : Opcode {
let HasGroup = 1;
}
+def CastAddressSpace : Opcode {
+ let Args = [ArgUint64, ArgDesc];
+}
+
//===----------------------------------------------------------------------===//
// Pointer generation
//===----------------------------------------------------------------------===//
@@ -596,7 +600,7 @@ def GetFnPtr : Opcode {
def GetIntPtr : Opcode {
let Types = [AluTypeClass];
- let Args = [ArgDesc];
+ let Args = [ArgDesc, ArgUint64];
let HasGroup = 1;
}
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 96409faeb6929..b27c17747e778 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -338,7 +338,8 @@ void Pointer::print(llvm::raw_ostream &OS) const {
} break;
case Storage::Int:
OS << "(Int) {";
- OS << Int.Value << " + " << Offset << ", " << Int.Desc;
+ OS << Int.Value << " + " << Offset << ", " << Int.Desc << ", "
+ << (Int.IsNull ? "null" : "nonnull");
OS << "}";
break;
case Storage::Fn:
@@ -1021,7 +1022,9 @@ std::optional<IntPointer> IntPointer::atOffset(const ASTContext &ASTCtx,
uint64_t FieldOffset =
ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))
.getQuantity();
- return IntPointer{F->Desc, this->Value + FieldOffset};
+
+ uint64_t NewValue = this->Value + FieldOffset;
+ return IntPointer{F->Desc, NewValue, NewValue == 0};
}
IntPointer IntPointer::baseCast(const ASTContext &ASTCtx,
@@ -1048,5 +1051,6 @@ IntPointer IntPointer::baseCast(const ASTContext &ASTCtx,
CharUnits BaseLayoutOffset =
Layout.getBaseClassOffset(cast<CXXRecordDecl>(BaseDesc->asDecl()));
- return {BaseDesc, Value + BaseLayoutOffset.getQuantity()};
+ uint64_t NewValue = Value + BaseLayoutOffset.getQuantity();
+ return {BaseDesc, NewValue, NewValue == 0};
}
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index a77918f667fd3..96cb667f02c3a 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -47,6 +47,7 @@ struct BlockPointer {
struct IntPointer {
const Descriptor *Desc;
uint64_t Value;
+ bool IsNull = false;
std::optional<IntPointer> atOffset(const ASTContext &ASTCtx,
unsigned Offset) const;
@@ -100,15 +101,17 @@ class Pointer {
static constexpr unsigned RootPtrMark = ~0u;
public:
- Pointer() : StorageKind(Storage::Int), Int{nullptr, 0} {}
+ Pointer() : StorageKind(Storage::Int), Int{nullptr, 0, true} {}
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 Descriptor *Desc, uint64_t Offset = 0)
- : Offset(Offset), StorageKind(Storage::Int), Int{Desc, Address} {}
+ Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset = 0,
+ std::optional<bool> IsNull = std::nullopt)
+ : Offset(Offset), StorageKind(Storage::Int),
+ Int{Desc, Address, IsNull.value_or(Address == 0)} {}
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)
@@ -264,7 +267,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;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/202356
More information about the cfe-commits
mailing list