Skip to content

Commit

Permalink
[clang][Interp] Fix non-primitive ltor casts
Browse files Browse the repository at this point in the history
This doesn't happen in C++ since it will instead call the
struct's copy constructor. However, in C, this needs to work.
  • Loading branch information
tbaederr committed Mar 14, 2024
1 parent 516ccce commit b97c129
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 41 deletions.
53 changes: 16 additions & 37 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);

Expand Down
9 changes: 5 additions & 4 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand All @@ -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)) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
45 changes: 45 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -720,3 +720,5 @@ def CheckNonNullArg : Opcode {
let Types = [PtrTypeClass];
let HasGroup = 1;
}

def Memcpy : Opcode;
8 changes: 8 additions & 0 deletions clang/test/AST/Interp/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}}

0 comments on commit b97c129

Please sign in to comment.