Author: Timm Bäder Date: 2023-08-20T11:15:17+02:00 New Revision: 8a58f0d370b004ec0c8f4af003da6b370f17ff44
URL: https://github.com/llvm/llvm-project/commit/8a58f0d370b004ec0c8f4af003da6b370f17ff44 DIFF: https://github.com/llvm/llvm-project/commit/8a58f0d370b004ec0c8f4af003da6b370f17ff44.diff LOG: [clang][Interp] Handle global composite temporaries We only did this for primitive temporaries. Unfortunately, the existing Pointer::toAPValue() won't do here, since we're expected to set an rvalue on the LifetimeExtendedTemporaryDecl. Differential Revision: https://reviews.llvm.org/D144457 Added: Modified: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Descriptor.h clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/lib/AST/Interp/Pointer.cpp clang/lib/AST/Interp/Pointer.h clang/lib/AST/Interp/Record.h clang/test/AST/Interp/records.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index f9a7cf7b743ef0..94eb1998839f4a 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -891,19 +891,28 @@ bool ByteCodeExprGen<Emitter>::VisitMaterializeTemporaryExpr( return this->discard(SubExpr); if (E->getStorageDuration() == SD_Static) { - if (std::optional<unsigned> GlobalIndex = P.createGlobal(E)) { - const LifetimeExtendedTemporaryDecl *TempDecl = - E->getLifetimeExtendedTemporaryDecl(); + std::optional<unsigned> GlobalIndex = P.createGlobal(E); + if (!GlobalIndex) + return false; + + const LifetimeExtendedTemporaryDecl *TempDecl = + E->getLifetimeExtendedTemporaryDecl(); + assert(TempDecl); + if (SubExprT) { if (!this->visitInitializer(SubExpr)) return false; - if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E)) return false; return this->emitGetPtrGlobal(*GlobalIndex, E); } - return false; + // Non-primitive values. + if (!this->emitGetPtrGlobal(*GlobalIndex, E)) + return false; + if (!this->visitInitializer(SubExpr)) + return false; + return this->emitInitGlobalTempComp(TempDecl, E); } // For everyhing else, use local variables. diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h index 4d81d757b3976c..c5b73eca4c2e9f 100644 --- a/clang/lib/AST/Interp/Descriptor.h +++ b/clang/lib/AST/Interp/Descriptor.h @@ -187,6 +187,8 @@ struct Descriptor final { /// Checks if the descriptor is of an array. bool isArray() const { return IsArray; } + /// Checks if the descriptor is of a record. + bool isRecord() const { return !IsArray && ElemRecord; } }; /// Bitfield tracking the initialisation status of elements of primitive arrays. diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 79995b5a74897c..cfef45d2e8d689 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -996,6 +996,19 @@ bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I, return true; } +/// 1) Converts the value on top of the stack to an APValue +/// 2) Sets that APValue on \Temp +/// 3) Initialized global with index \I with that +inline bool InitGlobalTempComp(InterpState &S, CodePtr OpPC, + const LifetimeExtendedTemporaryDecl *Temp) { + assert(Temp); + const Pointer &P = S.Stk.peek<Pointer>(); + APValue *Cached = Temp->getOrCreateValue(true); + + *Cached = P.toRValue(S.getCtx()); + return true; +} + template <PrimType Name, class T = typename PrimConv<Name>::T> bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) { if (S.checkingPotentialConstantExpression()) diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index a990d1ed976600..f66c9492ecd1d4 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -353,6 +353,12 @@ def InitGlobal : AccessOpcode; def InitGlobalTemp : AccessOpcode { let Args = [ArgUint32, ArgLETD]; } +// [Pointer] -> [Pointer] +def InitGlobalTempComp : Opcode { + let Args = [ArgLETD]; + let Types = []; + let HasGroup = 0; +} // [Value] -> [] def SetGlobal : AccessOpcode; diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp index 613521facd63c2..e04d29b8bd81be 100644 --- a/clang/lib/AST/Interp/Pointer.cpp +++ b/clang/lib/AST/Interp/Pointer.cpp @@ -7,9 +7,14 @@ //===----------------------------------------------------------------------===// #include "Pointer.h" +#include "Boolean.h" +#include "Context.h" +#include "Floating.h" #include "Function.h" +#include "Integral.h" #include "InterpBlock.h" #include "PrimType.h" +#include "Record.h" using namespace clang; using namespace clang::interp; @@ -217,3 +222,34 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) { return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray; } + +APValue Pointer::toRValue(const Context &Ctx) const { + // Primitives. + if (getFieldDesc()->isPrimitive()) { + PrimType PT = *Ctx.classify(getType()); + TYPE_SWITCH(PT, return deref<T>().toAPValue()); + llvm_unreachable("Unhandled PrimType?"); + } + + APValue Result; + // Records. + if (getFieldDesc()->isRecord()) { + const Record *R = getRecord(); + Result = + APValue(APValue::UninitStruct(), R->getNumBases(), R->getNumFields()); + + for (unsigned I = 0; I != R->getNumFields(); ++I) { + const Pointer &FieldPtr = this->atField(R->getField(I)->Offset); + Result.getStructField(I) = FieldPtr.toRValue(Ctx); + } + + for (unsigned I = 0; I != R->getNumBases(); ++I) { + const Pointer &BasePtr = this->atField(R->getBase(I)->Offset); + Result.getStructBase(I) = BasePtr.toRValue(Ctx); + } + } + + // TODO: Arrays + + return Result; +} diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h index 8385b032c207b0..7012009c675af2 100644 --- a/clang/lib/AST/Interp/Pointer.h +++ b/clang/lib/AST/Interp/Pointer.h @@ -27,6 +27,7 @@ namespace interp { class Block; class DeadBlock; class Pointer; +class Context; enum PrimType : unsigned; class Pointer; @@ -87,6 +88,9 @@ class Pointer { return reinterpret_cast<uintptr_t>(Pointee) + Offset; } + /// Converts the pointer to an APValue that is an rvalue. + APValue toRValue(const Context &Ctx) const; + /// Offsets a pointer inside an array. Pointer atIndex(unsigned Idx) const { if (Base == RootPtrMark) diff --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h index 940b4c9ebf592a..b81070aa77e841 100644 --- a/clang/lib/AST/Interp/Record.h +++ b/clang/lib/AST/Interp/Record.h @@ -87,7 +87,10 @@ class Record final { } unsigned getNumBases() const { return Bases.size(); } - const Base *getBase(unsigned I) const { return &Bases[I]; } + const Base *getBase(unsigned I) const { + assert(I < getNumBases()); + return &Bases[I]; + } using const_virtual_iter = VirtualBaseList::const_iterator; llvm::iterator_range<const_virtual_iter> virtual_bases() const { diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp index 46939fb6d072fa..c4191e3417bf17 100644 --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -114,6 +114,21 @@ constexpr C c2 = C().get(); static_assert(c2.a == 100, ""); static_assert(c2.b == 200, ""); + +/// A global, composite temporary variable. +constexpr const C &c3 = C().get(); + +/// Same, but with a bitfield. +class D { +public: + unsigned a : 4; + constexpr D() : a(15) {} + constexpr D get() const { + return *this; + } +}; +constexpr const D &d4 = D().get(); + constexpr int getB() { C c; int &j = c.b; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits