Author: Timm Baeder Date: 2026-06-30T08:13:47+02:00 New Revision: 06cac128adafc6c93120db7a3ac44e97c5e2c23b
URL: https://github.com/llvm/llvm-project/commit/06cac128adafc6c93120db7a3ac44e97c5e2c23b DIFF: https://github.com/llvm/llvm-project/commit/06cac128adafc6c93120db7a3ac44e97c5e2c23b.diff LOG: [clang][bytecode] Use ASTRecordLayout offsets when subtracting pointers (#206496) What we did here didn't work properly for pointers casted to bases. Add `Pointer::computeLayoutOffset()` and use that to return the proper values. Added: Modified: clang/lib/AST/ByteCode/Compiler.cpp clang/lib/AST/ByteCode/Descriptor.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 clang/test/AST/ByteCode/cxx20.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 398c2997ca4da..95f9a2f507335 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -1250,7 +1250,7 @@ bool Compiler<Emitter>::VisitPointerArithBinOp(const BinaryOperator *E) { ElemTypeSize = Ctx.getASTContext().getTypeSizeInChars(ElemType); PrimType IntT = classifyPrim(E->getType()); - if (!this->emitSubPtr(IntT, ElemTypeSize.isZero(), E)) + if (!this->emitSubPtr(IntT, ElemTypeSize.getQuantity(), E)) return false; return DiscardResult ? this->emitPop(IntT, E) : true; } diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index 8d9b87107e65d..fb41c98dd68cb 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -402,7 +402,26 @@ QualType Descriptor::getType() const { QualType Descriptor::getElemQualType() const { assert(isArray()); - QualType T = getType(); + QualType T; + + if (SourceType) { + T = QualType(SourceType, 0); + } else if (const auto *TDecl = dyn_cast_if_present<TypeDecl>(asDecl())) { + T = TDecl->getASTContext().getTypeDeclType(TDecl); + } else if (isRecord()) { + const RecordDecl *RD = ElemRecord->getDecl(); + T = RD->getASTContext().getTagType(ElaboratedTypeKeyword::None, + std::nullopt, RD, false); + if (IsConst) + T.addConst(); + } else if (const auto *E = asExpr()) { + T = E->getType(); + } else if (const auto *D = asValueDecl()) { + T = D->getType(); + } + + assert(!T.isNull()); + if (const auto *AT = T->getAs<AtomicType>()) T = AT->getValueType(); if (T->isPointerOrReferenceType()) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index aa2dffc2b982a..97d9e7cc14e32 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -2702,7 +2702,7 @@ static inline bool DecPtr(InterpState &S, CodePtr OpPC) { /// 2) Pops another Pointer from the stack. /// 3) Pushes the diff erence of the indices of the two pointers on the stack. template <PrimType Name, class T = typename PrimConv<Name>::T> -inline bool SubPtr(InterpState &S, CodePtr OpPC, bool ElemSizeIsZero) { +inline bool SubPtr(InterpState &S, CodePtr OpPC, uint32_t ElemSize) { const Pointer &LHS = S.Stk.pop<Pointer>().expand(); const Pointer &RHS = S.Stk.pop<Pointer>().expand(); @@ -2737,11 +2737,8 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC, bool ElemSizeIsZero) { return false; } - if (ElemSizeIsZero) { - QualType PtrT = LHS.getType(); - while (auto *AT = dyn_cast<ArrayType>(PtrT)) - PtrT = AT->getElementType(); - + if (ElemSize == 0) { + QualType PtrT = S.getASTContext().getBaseElementType(LHS.getType()); QualType ArrayTy = S.getASTContext().getConstantArrayType( PtrT, APInt::getZero(1), nullptr, ArraySizeModifier::Normal, 0); S.FFDiag(S.Current->getSource(OpPC), @@ -2756,17 +2753,16 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC, bool ElemSizeIsZero) { return true; } - int64_t A64 = - LHS.isBlockPointer() - ? (LHS.isElementPastEnd() ? LHS.getNumElems() : LHS.getIndex()) - : LHS.getIntegerRepresentation(); - - int64_t B64 = - RHS.isBlockPointer() - ? (RHS.isElementPastEnd() ? RHS.getNumElems() : RHS.getIndex()) - : RHS.getIntegerRepresentation(); + std::optional<size_t> VL = LHS.computeLayoutOffset(S.getASTContext()); + if (!VL) + return false; + std::optional<size_t> VR = RHS.computeLayoutOffset(S.getASTContext()); + if (!VR) + return false; - int64_t R64 = A64 - B64; + assert(((int64_t)*VL - (int64_t)*VR) % ElemSize == 0); + int64_t R64 = + (static_cast<int64_t>(*VL) - static_cast<int64_t>(*VR)) / ElemSize; if (static_cast<int64_t>(T::from(R64)) != R64) return handleOverflow(S, OpPC, R64); diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index e350d7b2e547d..b6190554178f3 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -594,7 +594,7 @@ def SubOffset : Opcode { // [Pointer, Pointer] -> [Integral] def SubPtr : Opcode { let Types = [IntegerTypeClass]; - let Args = [ArgBool]; + let Args = [ArgUint32]; let HasGroup = 1; } diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index de2b3421f404b..37659eb79e1df 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -437,6 +437,104 @@ Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const { return Result; } +std::optional<size_t> +Pointer::computeLayoutOffset(const ASTContext &ASTCtx) const { + switch (StorageKind) { + case Storage::Int: + return Int.Value + Offset; + case Storage::Block: + // See below. + break; + case Storage::Fn: + return getIntegerRepresentation(); + case Storage::Typeid: + return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset; + } + + auto getTypeSize = [&](QualType T) -> std::optional<size_t> { + if (const RecordType *RT = T->getAs<RecordType>()) { + // We cannot get the type size of a forward declaration. + if (!RT->getDecl()->getDefinition()) + return std::nullopt; + } + return ASTCtx.getTypeSizeInChars(T).getQuantity(); + }; + + auto getRecordDecl = [&](PtrView P) -> const CXXRecordDecl * { + if (const Record *R = P.getRecord()) + return cast<CXXRecordDecl>(R->getDecl()); + return cast<CXXRecordDecl>(P.getFieldDesc()->asDecl()); + }; + + auto getRecordSize = [&](const RecordDecl *RD) -> unsigned { + CanQualType RecordTy = ASTCtx.getCanonicalTagType(RD); + return ASTCtx.getTypeSizeInChars(RecordTy).getQuantity(); + }; + + size_t Result = 0; + PtrView P = view(); + while (true) { + if (P.isBaseClass()) { + const ASTRecordLayout &Layout = + ASTCtx.getASTRecordLayout(getRecordDecl(P.getBase())); + const CXXRecordDecl *RD = getRecordDecl(P); + if (P.isVirtualBaseClass()) + Result += Layout.getVBaseClassOffset(RD).getQuantity(); + else + Result += Layout.getBaseClassOffset(RD).getQuantity(); + + if (P.isOnePastEnd()) + Result += getRecordSize(RD); + + P = P.getBase(); + continue; + } + + if (P.isArrayElement()) { + P = P.expand(); + assert(P.getFieldDesc()->isArray()); + if (std::optional<size_t> ElemSize = + getTypeSize(P.getFieldDesc()->getElemQualType())) + Result += *ElemSize * P.getIndex(); + else + return std::nullopt; + + P = P.getArray(); + continue; + } + + if (P.isRoot()) { + if (P.isPastEnd() || P.isOnePastEnd()) { + if (std::optional<size_t> Size = + getTypeSize(P.getDeclDesc()->getType())) + Result += *Size * P.getIndex(); + else + return std::nullopt; + } + break; + } + + assert(P.getField()); + const FieldDecl *F = P.getField(); + const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(F->getParent()); + Result += + ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(F->getFieldIndex())) + .getQuantity(); + + if (P.isPastEnd() || P.isOnePastEnd()) { + if (std::optional<size_t> Size = getTypeSize(F->getType())) + Result += *Size * P.getIndex(); + else + return std::nullopt; + } + + P = P.getBase(); + if (P.isRoot()) + break; + } + return Result; +} + std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const { if (isZero()) return "nullptr"; diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index d4d92d62c0146..8a413d01d2dd5 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -1035,6 +1035,9 @@ class Pointer { /// regarding the AST record layout. std::optional<size_t> computeOffsetForComparison(const ASTContext &ASTCtx) const; + /// Compute the pointer offset as given by the ASTRecordLayout. + /// Returns the result in bytes. + std::optional<size_t> computeLayoutOffset(const ASTContext &ASTCtx) const; private: friend class Block; diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp index 7ff70076ee6e4..b7ca154a0a987 100644 --- a/clang/test/AST/ByteCode/cxx20.cpp +++ b/clang/test/AST/ByteCode/cxx20.cpp @@ -1462,3 +1462,73 @@ namespace ConstWrites { constexpr B bad(true); // both-error {{must be initialized by a constant expression}} \ // both-note {{in call to 'B(true)'}} } + +namespace SubPtr { +#define fold(x) (__builtin_constant_p(x) ? (x) : (x)) + struct A { int a; }; + + struct B : A { + char Padding[12] = {}; + constexpr B() : A{127} {} + }; + + struct C : B { + int c; + constexpr C(int) : B(), c(1000) {} + }; + + constexpr C c(123); + + static_assert(fold((&c.c - &c.a)) == 4, ""); + static_assert(fold(((char*)&c.c - (char*)&c.a)) == 16, ""); + + constexpr const B* b1 = (B*)&c; + constexpr const B* b2 = (B*)&c + 1; + static_assert(fold((char*)b2) - fold((char*)b1) == sizeof(B), ""); + + + constexpr int arr[] = {1,2,3}; + static_assert(&arr[1] - &arr[0] == 1, ""); + static_assert(&arr[3] - &arr[0] == 3, ""); + + constexpr double arr2[] = {1,2,3}; + static_assert(&arr2[1] - &arr2[0] == 1, ""); + static_assert(&arr2[3] - &arr2[0] == 3, ""); + + struct S { + char c[3]; + int a; + }; + constexpr S s[] = {{}, {}}; + static_assert(&s[1] - &s[0] == 1, ""); + static_assert(&s[2] - &s[0] == 2, ""); + static_assert(fold((char*)&s[1]) - fold((char*)&s[0]) == sizeof(S), ""); + static_assert(fold((char*)&s[2]) - fold((char*)&s[0]) == 2 * sizeof(S), ""); + + + constexpr int m = 10; + constexpr const int *p = &m; + constexpr const int *p2 = &m + 1; + static_assert( p2 - p == 1, ""); + + + struct Ints { + int a; + int b; + }; + constexpr Ints I = {}; + static_assert(&I.a + 1 - &I.a == 1, ""); + + + constexpr int **intptr[] = {nullptr}; + static_assert(&intptr[1] - &intptr[0] == 1, ""); + + constexpr int dynAlloc() { + int **p = new int*[1]; + int r = &p[1] - &p[0]; + delete[] p; + + return r; + } + static_assert(dynAlloc() == 1); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
