[clang] b97c129 - [clang][Interp] Fix non-primitive ltor casts
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 14 05:56:12 PDT 2024
Author: Timm Bäder
Date: 2024-03-14T13:55:55+01:00
New Revision: b97c12936dd8d520a5565ace3d51a460939a5c61
URL: https://github.com/llvm/llvm-project/commit/b97c12936dd8d520a5565ace3d51a460939a5c61
DIFF: https://github.com/llvm/llvm-project/commit/b97c12936dd8d520a5565ace3d51a460939a5c61.diff
LOG: [clang][Interp] Fix non-primitive ltor casts
This doesn't happen in C++ since it will instead call the
struct's copy constructor. However, in C, this needs to work.
Added:
Modified:
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/Descriptor.cpp
clang/lib/AST/Interp/Descriptor.h
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/InterpBuiltin.cpp
clang/lib/AST/Interp/Opcodes.td
clang/test/AST/Interp/c.c
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 86304a54473cea..ae5e2dadac951b 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -82,15 +82,27 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
if (DiscardResult)
return this->discard(SubExpr);
- if (SubExpr->getType()->isAnyComplexType())
- return this->delegate(SubExpr);
+ std::optional<PrimType> SubExprT = classify(SubExpr->getType());
+ // Prepare storage for the result.
+ if (!Initializing && !SubExprT) {
+ std::optional<unsigned> LocalIndex =
+ allocateLocal(SubExpr, /*IsExtended=*/false);
+ if (!LocalIndex)
+ return false;
+ if (!this->emitGetPtrLocal(*LocalIndex, CE))
+ return false;
+ }
if (!this->visit(SubExpr))
return false;
- if (std::optional<PrimType> SubExprT = classify(SubExpr->getType()))
+ if (SubExprT)
return this->emitLoadPop(*SubExprT, CE);
- return false;
+
+ // If the subexpr type is not primitive, we need to perform a copy here.
+ // This happens for example in C when dereferencing a pointer of struct
+ // type.
+ return this->emitMemcpy(CE);
}
case CK_UncheckedDerivedToBase:
@@ -3247,53 +3259,20 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
// pointer to the actual value) instead of a pointer to the pointer to the
// value.
bool IsReference = D->getType()->isReferenceType();
- // Complex values are copied in the AST via a simply assignment or
- // ltor cast. But we represent them as two-element arrays, which means
- // we pass them around as pointers. So, to assignm from them, we will
- // have to copy both (primitive) elements instead.
- bool IsComplex = D->getType()->isAnyComplexType();
// Check for local/global variables and parameters.
if (auto It = Locals.find(D); It != Locals.end()) {
const unsigned Offset = It->second.Offset;
- // FIXME: Fix the code duplication here with the code in the global case.
- if (Initializing && IsComplex) {
- PrimType ElemT = classifyComplexElementType(D->getType());
- for (unsigned I = 0; I != 2; ++I) {
- if (!this->emitGetPtrLocal(Offset, E))
- return false;
- if (!this->emitArrayElemPop(ElemT, I, E))
- return false;
- if (!this->emitInitElem(ElemT, I, E))
- return false;
- }
- return true;
- }
-
if (IsReference)
return this->emitGetLocal(PT_Ptr, Offset, E);
return this->emitGetPtrLocal(Offset, E);
} else if (auto GlobalIndex = P.getGlobal(D)) {
- if (Initializing && IsComplex) {
- PrimType ElemT = classifyComplexElementType(D->getType());
- for (unsigned I = 0; I != 2; ++I) {
- if (!this->emitGetPtrGlobal(*GlobalIndex, E))
- return false;
- if (!this->emitArrayElemPop(ElemT, I, E))
- return false;
- if (!this->emitInitElem(ElemT, I, E))
- return false;
- }
- return true;
- }
-
if (IsReference)
return this->emitGetGlobalPtr(*GlobalIndex, E);
return this->emitGetPtrGlobal(*GlobalIndex, E);
} else if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
- // FIXME: _Complex initializing case?
if (IsReference || !It->second.IsPtr)
return this->emitGetParamPtr(It->second.Offset, E);
diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp
index ce7ed9cec3db3f..dff8eed12428ec 100644
--- a/clang/lib/AST/Interp/Descriptor.cpp
+++ b/clang/lib/AST/Interp/Descriptor.cpp
@@ -233,9 +233,10 @@ static BlockMoveFn getMoveArrayPrim(PrimType Type) {
Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
bool IsConst, bool IsTemporary, bool IsMutable)
: Source(D), ElemSize(primSize(Type)), Size(ElemSize),
- MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst),
- IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)),
- DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) {
+ MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), PrimT(Type),
+ IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
+ CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)),
+ MoveFn(getMovePrim(Type)) {
assert(AllocSize >= Size);
assert(Source && "Missing source");
}
@@ -246,7 +247,7 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
bool IsMutable)
: Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
MDSize(MD.value_or(0)),
- AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)),
+ AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type),
IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
IsArray(true), CtorFn(getCtorArrayPrim(Type)),
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h
index 0f64d678f3ef6b..5160f07466fba7 100644
--- a/clang/lib/AST/Interp/Descriptor.h
+++ b/clang/lib/AST/Interp/Descriptor.h
@@ -112,6 +112,10 @@ struct Descriptor final {
const Record *const ElemRecord = nullptr;
/// Descriptor of the array element.
const Descriptor *const ElemDesc = nullptr;
+ /// The primitive type this descriptor was created for,
+ /// or the primitive element type in case this is
+ /// a primitive array.
+ const std::optional<PrimType> PrimT = std::nullopt;
/// Flag indicating if the block is mutable.
const bool IsConst = false;
/// Flag indicating if a field is mutable.
@@ -183,6 +187,11 @@ struct Descriptor final {
return Size;
}
+ PrimType getPrimType() const {
+ assert(isPrimitiveArray() || isPrimitive());
+ return *PrimT;
+ }
+
/// Returns the allocated size, including metadata.
unsigned getAllocSize() const { return AllocSize; }
/// returns the size of an element when the structure is viewed as an array.
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index db80e2d59753f8..507d91129d6886 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -122,6 +122,9 @@ bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
const Pointer &Ptr, const APSInt &IntValue);
+/// Copy the contents of Src into Dest.
+bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest);
+
/// Checks if the shift operation is legal.
template <typename LT, typename RT>
bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
@@ -1487,6 +1490,16 @@ bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
return true;
}
+inline bool Memcpy(InterpState &S, CodePtr OpPC) {
+ const Pointer &Src = S.Stk.pop<Pointer>();
+ Pointer &Dest = S.Stk.peek<Pointer>();
+
+ if (!CheckLoad(S, OpPC, Src))
+ return false;
+
+ return DoMemcpy(S, OpPC, Src, Dest);
+}
+
//===----------------------------------------------------------------------===//
// AddOffset, SubOffset
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index c500b9d502d707..8113254f8fa8e2 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -1326,5 +1326,50 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
return true;
}
+bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
+ assert(Src.isLive() && Dest.isLive());
+
+ const Descriptor *SrcDesc = Src.getFieldDesc();
+ const Descriptor *DestDesc = Dest.getFieldDesc();
+
+ assert(!DestDesc->isPrimitive() && !SrcDesc->isPrimitive());
+
+ if (DestDesc->isPrimitiveArray()) {
+ assert(SrcDesc->isPrimitiveArray());
+ assert(SrcDesc->getNumElems() == DestDesc->getNumElems());
+ PrimType ET = DestDesc->getPrimType();
+ for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) {
+ Pointer DestElem = Dest.atIndex(I);
+ TYPE_SWITCH(ET, {
+ DestElem.deref<T>() = Src.atIndex(I).deref<T>();
+ DestElem.initialize();
+ });
+ }
+ return true;
+ }
+
+ if (DestDesc->isRecord()) {
+ assert(SrcDesc->isRecord());
+ assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
+ const Record *R = DestDesc->ElemRecord;
+ for (const Record::Field &F : R->fields()) {
+ Pointer DestField = Dest.atField(F.Offset);
+ if (std::optional<PrimType> FT = S.Ctx.classify(F.Decl->getType())) {
+ TYPE_SWITCH(*FT, {
+ DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
+ DestField.initialize();
+ });
+ } else {
+ return Invalid(S, OpPC);
+ }
+ }
+ return true;
+ }
+
+ // FIXME: Composite types.
+
+ return Invalid(S, OpPC);
+}
+
} // namespace interp
} // namespace clang
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 9b99aa0ccb558a..0ed214af3548aa 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -720,3 +720,5 @@ def CheckNonNullArg : Opcode {
let Types = [PtrTypeClass];
let HasGroup = 1;
}
+
+def Memcpy : Opcode;
diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c
index 8de6139efbea09..5caebd9ce6f14e 100644
--- a/clang/test/AST/Interp/c.c
+++ b/clang/test/AST/Interp/c.c
@@ -201,3 +201,11 @@ void localCompoundLiteral(void) {
// pedantic-ref-warning {{use of an empty initializer}}
};
}
+
+/// struct copy
+struct StrA {int a; };
+const struct StrA sa = { 12 };
+const struct StrA * const sb = &sa;
+const struct StrA sc = *sb;
+_Static_assert(sc.a == 12, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
More information about the cfe-commits
mailing list