[clang] 8a58f0d - [clang][Interp] Handle global composite temporaries
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 20 02:18:36 PDT 2023
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;
More information about the cfe-commits
mailing list