@@ -2170,29 +2170,32 @@ static bool interp__builtin_memchr(InterpState &S, CodePtr OpPC,
21702170 return true ;
21712171}
21722172
2173- static unsigned computeFullDescSize (const ASTContext &ASTCtx,
2174- const Descriptor *Desc) {
2175-
2173+ static std::optional<unsigned > computeFullDescSize (const ASTContext &ASTCtx,
2174+ const Descriptor *Desc) {
21762175 if (Desc->isPrimitive ())
21772176 return ASTCtx.getTypeSizeInChars (Desc->getType ()).getQuantity ();
2178-
21792177 if (Desc->isArray ())
21802178 return ASTCtx.getTypeSizeInChars (Desc->getElemQualType ()).getQuantity () *
21812179 Desc->getNumElems ();
2180+ if (Desc->isRecord ()) {
2181+ // Can't use Descriptor::getType() as that may return a pointer type. Look
2182+ // at the decl directly.
2183+ return ASTCtx
2184+ .getTypeSizeInChars (
2185+ ASTCtx.getCanonicalTagType (Desc->ElemRecord ->getDecl ()))
2186+ .getQuantity ();
2187+ }
21822188
2183- if (Desc->isRecord ())
2184- return ASTCtx.getTypeSizeInChars (Desc->getType ()).getQuantity ();
2185-
2186- llvm_unreachable (" Unhandled descriptor type" );
2187- return 0 ;
2189+ return std::nullopt ;
21882190}
21892191
2192+ // / Compute the byte offset of \p Ptr in the full declaration.
21902193static unsigned computePointerOffset (const ASTContext &ASTCtx,
21912194 const Pointer &Ptr) {
21922195 unsigned Result = 0 ;
21932196
21942197 Pointer P = Ptr;
2195- while (P.isArrayElement () || P.isField ()) {
2198+ while (P.isField () || P.isArrayElement ()) {
21962199 P = P.expand ();
21972200 const Descriptor *D = P.getFieldDesc ();
21982201
@@ -2205,7 +2208,6 @@ static unsigned computePointerOffset(const ASTContext &ASTCtx,
22052208 Result += ElemSize * P.getIndex ();
22062209 P = P.expand ().getArray ();
22072210 } else if (P.isBaseClass ()) {
2208-
22092211 const auto *RD = cast<CXXRecordDecl>(D->asDecl ());
22102212 bool IsVirtual = Ptr.isVirtualBaseClass ();
22112213 P = P.getBase ();
@@ -2234,30 +2236,136 @@ static unsigned computePointerOffset(const ASTContext &ASTCtx,
22342236 return Result;
22352237}
22362238
2239+ // / Does Ptr point to the last subobject?
2240+ static bool pointsToLastObject (const Pointer &Ptr) {
2241+ Pointer P = Ptr;
2242+ while (!P.isRoot ()) {
2243+
2244+ if (P.isArrayElement ()) {
2245+ P = P.expand ().getArray ();
2246+ continue ;
2247+ }
2248+ if (P.isBaseClass ()) {
2249+ if (P.getRecord ()->getNumFields () > 0 )
2250+ return false ;
2251+ P = P.getBase ();
2252+ continue ;
2253+ }
2254+
2255+ Pointer Base = P.getBase ();
2256+ if (const Record *R = Base.getRecord ()) {
2257+ assert (P.getField ());
2258+ if (P.getField ()->getFieldIndex () != R->getNumFields () - 1 )
2259+ return false ;
2260+ }
2261+ P = Base;
2262+ }
2263+
2264+ return true ;
2265+ }
2266+
2267+ // / Does Ptr point to the last object AND to a flexible array member?
2268+ static bool isUserWritingOffTheEnd (const ASTContext &Ctx, const Pointer &Ptr) {
2269+ auto isFlexibleArrayMember = [&](const Descriptor *FieldDesc) {
2270+ using FAMKind = LangOptions::StrictFlexArraysLevelKind;
2271+ FAMKind StrictFlexArraysLevel =
2272+ Ctx.getLangOpts ().getStrictFlexArraysLevel ();
2273+
2274+ if (StrictFlexArraysLevel == FAMKind::Default)
2275+ return true ;
2276+
2277+ unsigned NumElems = FieldDesc->getNumElems ();
2278+ if (NumElems == 0 && StrictFlexArraysLevel != FAMKind::IncompleteOnly)
2279+ return true ;
2280+
2281+ if (NumElems == 1 && StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete)
2282+ return true ;
2283+ return false ;
2284+ };
2285+
2286+ const Descriptor *FieldDesc = Ptr.getFieldDesc ();
2287+ if (!FieldDesc->isArray ())
2288+ return false ;
2289+
2290+ return Ptr.isDummy () && pointsToLastObject (Ptr) &&
2291+ isFlexibleArrayMember (FieldDesc);
2292+ }
2293+
22372294static bool interp__builtin_object_size (InterpState &S, CodePtr OpPC,
22382295 const InterpFrame *Frame,
22392296 const CallExpr *Call) {
2297+ const ASTContext &ASTCtx = S.getASTContext ();
22402298 PrimType KindT = *S.getContext ().classify (Call->getArg (1 ));
2241- [[maybe_unused]] unsigned Kind = popToAPSInt (S.Stk , KindT).getZExtValue ();
2242-
2299+ // From the GCC docs:
2300+ // Kind is an integer constant from 0 to 3. If the least significant bit is
2301+ // clear, objects are whole variables. If it is set, a closest surrounding
2302+ // subobject is considered the object a pointer points to. The second bit
2303+ // determines if maximum or minimum of remaining bytes is computed.
2304+ unsigned Kind = popToAPSInt (S.Stk , KindT).getZExtValue ();
22432305 assert (Kind <= 3 && " unexpected kind" );
2244-
2306+ bool UseFieldDesc = (Kind & 1u );
2307+ bool ReportMinimum = (Kind & 2u );
22452308 const Pointer &Ptr = S.Stk .pop <Pointer>();
22462309
2247- if (Ptr.isZero ())
2310+ if (Call->getArg (0 )->HasSideEffects (ASTCtx)) {
2311+ // "If there are any side effects in them, it returns (size_t) -1
2312+ // for type 0 or 1 and (size_t) 0 for type 2 or 3."
2313+ pushInteger (S, Kind <= 1 ? -1 : 0 , Call->getType ());
2314+ return true ;
2315+ }
2316+
2317+ if (Ptr.isZero () || !Ptr.isBlockPointer ())
22482318 return false ;
22492319
2250- const Descriptor *DeclDesc = Ptr. getDeclDesc ();
2251- if (!DeclDesc )
2320+ // We can't load through pointers.
2321+ if (Ptr. isDummy () && Ptr. getType ()-> isPointerType () )
22522322 return false ;
22532323
2254- const ASTContext &ASTCtx = S.getASTContext ();
2324+ bool DetermineForCompleteObject = Ptr.getFieldDesc () == Ptr.getDeclDesc ();
2325+ const Descriptor *DeclDesc = Ptr.getDeclDesc ();
2326+ assert (DeclDesc);
22552327
2256- unsigned ByteOffset = computePointerOffset (ASTCtx, Ptr);
2257- unsigned FullSize = computeFullDescSize (ASTCtx, DeclDesc);
2328+ if (!UseFieldDesc || DetermineForCompleteObject) {
2329+ // Lower bound, so we can't fall back to this.
2330+ if (ReportMinimum && !DetermineForCompleteObject)
2331+ return false ;
22582332
2259- pushInteger (S, FullSize - ByteOffset, Call->getType ());
2333+ // Can't read beyond the pointer decl desc.
2334+ if (!UseFieldDesc && !ReportMinimum && DeclDesc->getType ()->isPointerType ())
2335+ return false ;
2336+ } else {
2337+ if (isUserWritingOffTheEnd (ASTCtx, Ptr.expand ())) {
2338+ // If we cannot determine the size of the initial allocation, then we
2339+ // can't given an accurate upper-bound. However, we are still able to give
2340+ // conservative lower-bounds for Type=3.
2341+ if (Kind == 1 )
2342+ return false ;
2343+ }
2344+ }
22602345
2346+ const Descriptor *Desc = UseFieldDesc ? Ptr.getFieldDesc () : DeclDesc;
2347+ assert (Desc);
2348+
2349+ std::optional<unsigned > FullSize = computeFullDescSize (ASTCtx, Desc);
2350+ if (!FullSize)
2351+ return false ;
2352+
2353+ unsigned ByteOffset;
2354+ if (UseFieldDesc) {
2355+ if (Ptr.isBaseClass ())
2356+ ByteOffset = computePointerOffset (ASTCtx, Ptr.getBase ()) -
2357+ computePointerOffset (ASTCtx, Ptr);
2358+ else
2359+ ByteOffset =
2360+ computePointerOffset (ASTCtx, Ptr) -
2361+ computePointerOffset (ASTCtx, Ptr.expand ().atIndex (0 ).narrow ());
2362+ } else
2363+ ByteOffset = computePointerOffset (ASTCtx, Ptr);
2364+
2365+ assert (ByteOffset <= *FullSize);
2366+ unsigned Result = *FullSize - ByteOffset;
2367+
2368+ pushInteger (S, Result, Call->getType ());
22612369 return true ;
22622370}
22632371
0 commit comments