[clang] Improve Serialization/Imporing/Dumping of APValues

Changes:
 - initializer expressions of constexpr variable are now wraped in a ConstantExpr. this is mainly used for testing purposes. the old caching system has not yet been removed.
 - Add all the missing Serialization and Importing for APValue.
 - Improve dumping of APValue when ASTContext isn't available.
 - Cleanup leftover from last patch.
 - Add Tests for Import and serialization.

Differential Revision: https://reviews.llvm.org/D63640
This commit is contained in:
Tyker 2020-10-01 17:58:07 +02:00
parent 8b7dac81d3
commit cf34dd0c4e
10 changed files with 912 additions and 64 deletions

View File

@ -235,8 +235,10 @@ public:
struct UninitArray {};
struct UninitStruct {};
friend class ASTReader;
friend class ASTRecordReader;
friend class ASTWriter;
friend class ASTImporter;
friend class ASTNodeImporter;
private:
ValueKind Kind;
@ -569,11 +571,9 @@ public:
*(APFixedPoint *)(char *)Data.buffer = std::move(FX);
}
void setVector(const APValue *E, unsigned N) {
assert(isVector() && "Invalid accessor");
((Vec*)(char*)Data.buffer)->Elts = new APValue[N];
((Vec*)(char*)Data.buffer)->NumElts = N;
MutableArrayRef<APValue> InternalElts = setVectorUninit(N);
for (unsigned i = 0; i != N; ++i)
((Vec*)(char*)Data.buffer)->Elts[i] = E[i];
InternalElts[i] = E[i];
}
void setComplexInt(APSInt R, APSInt I) {
assert(R.getBitWidth() == I.getBitWidth() &&
@ -656,6 +656,24 @@ private:
new ((void*)(char*)Data.buffer) AddrLabelDiffData();
Kind = AddrLabelDiff;
}
private:
/// The following functions are used as part of initialization, during
/// deserialization and importing. Reserve the space so that it can be
/// filled in by those steps.
MutableArrayRef<APValue> setVectorUninit(unsigned N) {
assert(isVector() && "Invalid accessor");
Vec *V = ((Vec *)(char *)Data.buffer);
V->Elts = new APValue[N];
V->NumElts = N;
return {V->Elts, V->NumElts};
}
MutableArrayRef<LValuePathEntry>
setLValueUninit(LValueBase B, const CharUnits &O, unsigned Size,
bool OnePastTheEnd, bool IsNullPtr);
MutableArrayRef<const CXXRecordDecl *>
setMemberPointerUninit(const ValueDecl *Member, bool IsDerivedMember,
unsigned Size);
};
} // end namespace clang.

View File

@ -289,9 +289,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// Mapping from GUIDs to the corresponding MSGuidDecl.
mutable llvm::FoldingSet<MSGuidDecl> MSGuidDecls;
/// Used to cleanups APValues stored in the AST.
mutable llvm::SmallVector<APValue *, 0> APValueCleanups;
/// A cache mapping a string value to a StringLiteral object with the same
/// value.
///

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_ASTIMPORTER_H
#define LLVM_CLANG_AST_ASTIMPORTER_H
#include "clang/AST/APValue.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/ExprCXX.h"
@ -503,6 +504,13 @@ class TypeSourceInfo;
/// "to" context, or the import error.
llvm::Expected<CXXBaseSpecifier *> Import(const CXXBaseSpecifier *FromSpec);
/// Import the given APValue from the "from" context into
/// the "to" context.
///
/// \return the equivalent APValue in the "to" context or the import
/// error.
llvm::Expected<APValue> Import(const APValue &FromValue);
/// Import the definition of the given declaration, including all of
/// the declarations it contains.
LLVM_NODISCARD llvm::Error ImportDefinition(Decl *From);

View File

@ -882,17 +882,26 @@ void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath,
LVal.IsNullPtr = IsNullPtr;
}
void APValue::setLValue(LValueBase B, const CharUnits &O,
ArrayRef<LValuePathEntry> Path, bool IsOnePastTheEnd,
bool IsNullPtr) {
MutableArrayRef<APValue::LValuePathEntry>
APValue::setLValueUninit(LValueBase B, const CharUnits &O, unsigned Size,
bool IsOnePastTheEnd, bool IsNullPtr) {
assert(isLValue() && "Invalid accessor");
LV &LVal = *((LV*)(char*)Data.buffer);
LV &LVal = *((LV *)(char *)Data.buffer);
LVal.Base = B;
LVal.IsOnePastTheEnd = IsOnePastTheEnd;
LVal.Offset = O;
LVal.resizePath(Path.size());
memcpy(LVal.getPath(), Path.data(), Path.size() * sizeof(LValuePathEntry));
LVal.IsNullPtr = IsNullPtr;
LVal.resizePath(Size);
return {LVal.getPath(), Size};
}
void APValue::setLValue(LValueBase B, const CharUnits &O,
ArrayRef<LValuePathEntry> Path, bool IsOnePastTheEnd,
bool IsNullPtr) {
MutableArrayRef<APValue::LValuePathEntry> InternalPath =
setLValueUninit(B, O, Path.size(), IsOnePastTheEnd, IsNullPtr);
memcpy(InternalPath.data(), Path.data(),
Path.size() * sizeof(LValuePathEntry));
}
const ValueDecl *APValue::getMemberPointerDecl() const {
@ -929,15 +938,27 @@ void APValue::MakeArray(unsigned InitElts, unsigned Size) {
Kind = Array;
}
void APValue::MakeMemberPointer(const ValueDecl *Member, bool IsDerivedMember,
ArrayRef<const CXXRecordDecl*> Path) {
MutableArrayRef<APValue::LValuePathEntry>
setLValueUninit(APValue::LValueBase B, const CharUnits &O, unsigned Size,
bool OnePastTheEnd, bool IsNullPtr);
MutableArrayRef<const CXXRecordDecl *>
APValue::setMemberPointerUninit(const ValueDecl *Member, bool IsDerivedMember,
unsigned Size) {
assert(isAbsent() && "Bad state change");
MemberPointerData *MPD = new ((void*)(char*)Data.buffer) MemberPointerData;
MemberPointerData *MPD = new ((void *)(char *)Data.buffer) MemberPointerData;
Kind = MemberPointer;
MPD->MemberAndIsDerivedMember.setPointer(
Member ? cast<ValueDecl>(Member->getCanonicalDecl()) : nullptr);
MPD->MemberAndIsDerivedMember.setInt(IsDerivedMember);
MPD->resizePath(Path.size());
for (unsigned I = 0; I != Path.size(); ++I)
MPD->getPath()[I] = Path[I]->getCanonicalDecl();
MPD->resizePath(Size);
return {MPD->getPath(), MPD->PathLength};
}
void APValue::MakeMemberPointer(const ValueDecl *Member, bool IsDerivedMember,
ArrayRef<const CXXRecordDecl *> Path) {
MutableArrayRef<const CXXRecordDecl *> InternalPath =
setMemberPointerUninit(Member, IsDerivedMember, Path.size());
for (unsigned I = 0; I != Path.size(); ++I)
InternalPath[I] = Path[I]->getCanonicalDecl();
}

View File

@ -1005,9 +1005,6 @@ ASTContext::~ASTContext() {
for (const auto &Value : ModuleInitializers)
Value.second->~PerModuleInitializers();
for (APValue *Value : APValueCleanups)
Value->~APValue();
}
void ASTContext::setTraversalScope(const std::vector<Decl *> &TopLevelDecls) {

View File

@ -397,6 +397,7 @@ namespace clang {
Error ImportImplicitMethods(const CXXRecordDecl *From, CXXRecordDecl *To);
Expected<CXXCastPath> ImportCastPath(CastExpr *E);
Expected<APValue> ImportAPValue(const APValue &FromValue);
using Designator = DesignatedInitExpr::Designator;
@ -6692,18 +6693,11 @@ ExpectedStmt ASTNodeImporter::VisitAddrLabelExpr(AddrLabelExpr *E) {
ExpectedStmt ASTNodeImporter::VisitConstantExpr(ConstantExpr *E) {
Error Err = Error::success();
auto ToSubExpr = importChecked(Err, E->getSubExpr());
auto ToResult = importChecked(Err, E->getAPValueResult());
if (Err)
return std::move(Err);
// TODO : Handle APValue::ValueKind that require importing.
APValue::ValueKind Kind = E->getResultAPValueKind();
if (Kind == APValue::Int || Kind == APValue::Float ||
Kind == APValue::FixedPoint || Kind == APValue::ComplexFloat ||
Kind == APValue::ComplexInt)
return ConstantExpr::Create(Importer.getToContext(), ToSubExpr,
E->getAPValueResult());
return ConstantExpr::Create(Importer.getToContext(), ToSubExpr);
return ConstantExpr::Create(Importer.getToContext(), ToSubExpr, ToResult);
}
ExpectedStmt ASTNodeImporter::VisitParenExpr(ParenExpr *E) {
Error Err = Error::success();
@ -8804,6 +8798,11 @@ ASTImporter::Import(const CXXBaseSpecifier *BaseSpec) {
return Imported;
}
llvm::Expected<APValue> ASTImporter::Import(const APValue &FromValue) {
ASTNodeImporter Importer(*this);
return Importer.ImportAPValue(FromValue);
}
Error ASTImporter::ImportDefinition(Decl *From) {
ExpectedDecl ToOrErr = Import(From);
if (!ToOrErr)
@ -8934,6 +8933,172 @@ Expected<Selector> ASTImporter::Import(Selector FromSel) {
return ToContext.Selectors.getSelector(FromSel.getNumArgs(), Idents.data());
}
llvm::Expected<APValue>
ASTNodeImporter::ImportAPValue(const APValue &FromValue) {
APValue Result;
llvm::Error Err = llvm::Error::success();
auto ImportLoop = [&](const APValue *From, APValue *To, unsigned Size) {
for (unsigned Idx = 0; Idx < Size; Idx++) {
APValue Tmp = importChecked(Err, From[Idx]);
To[Idx] = Tmp;
}
};
switch (FromValue.getKind()) {
case APValue::None:
case APValue::Indeterminate:
case APValue::Int:
case APValue::Float:
case APValue::FixedPoint:
case APValue::ComplexInt:
case APValue::ComplexFloat:
Result = FromValue;
break;
case APValue::Vector: {
Result.MakeVector();
MutableArrayRef<APValue> Elts =
Result.setVectorUninit(FromValue.getVectorLength());
ImportLoop(
((const APValue::Vec *)(const char *)FromValue.Data.buffer)->Elts,
Elts.data(), FromValue.getVectorLength());
break;
}
case APValue::Array:
Result.MakeArray(FromValue.getArrayInitializedElts(),
FromValue.getArraySize());
ImportLoop(
((const APValue::Arr *)(const char *)FromValue.Data.buffer)->Elts,
((const APValue::Arr *)(const char *)Result.Data.buffer)->Elts,
FromValue.getArrayInitializedElts());
break;
case APValue::Struct:
Result.MakeStruct(FromValue.getStructNumBases(),
FromValue.getStructNumFields());
ImportLoop(
((const APValue::StructData *)(const char *)FromValue.Data.buffer)
->Elts,
((const APValue::StructData *)(const char *)Result.Data.buffer)->Elts,
FromValue.getStructNumBases() + FromValue.getStructNumFields());
break;
case APValue::Union: {
Result.MakeUnion();
const Decl *ImpFDecl = importChecked(Err, FromValue.getUnionField());
APValue ImpValue = importChecked(Err, FromValue.getUnionValue());
if (Err)
return std::move(Err);
Result.setUnion(cast<FieldDecl>(ImpFDecl), ImpValue);
break;
}
case APValue::AddrLabelDiff: {
Result.MakeAddrLabelDiff();
const Expr *ImpLHS = importChecked(Err, FromValue.getAddrLabelDiffLHS());
const Expr *ImpRHS = importChecked(Err, FromValue.getAddrLabelDiffRHS());
if (Err)
return std::move(Err);
Result.setAddrLabelDiff(cast<AddrLabelExpr>(ImpLHS),
cast<AddrLabelExpr>(ImpRHS));
break;
}
case APValue::MemberPointer: {
const Decl *ImpMemPtrDecl =
importChecked(Err, FromValue.getMemberPointerDecl());
if (Err)
return std::move(Err);
MutableArrayRef<const CXXRecordDecl *> ToPath =
Result.setMemberPointerUninit(
cast<const ValueDecl>(ImpMemPtrDecl),
FromValue.isMemberPointerToDerivedMember(),
FromValue.getMemberPointerPath().size());
llvm::ArrayRef<const CXXRecordDecl *> FromPath =
Result.getMemberPointerPath();
for (unsigned Idx = 0; Idx < FromValue.getMemberPointerPath().size();
Idx++) {
const Decl *ImpDecl = importChecked(Err, FromPath[Idx]);
if (Err)
return std::move(Err);
ToPath[Idx] = cast<const CXXRecordDecl>(ImpDecl->getCanonicalDecl());
}
break;
}
case APValue::LValue:
APValue::LValueBase Base;
QualType FromElemTy;
if (FromValue.getLValueBase()) {
assert(!FromValue.getLValueBase().is<DynamicAllocLValue>() &&
"in C++20 dynamic allocation are transient so they shouldn't "
"appear in the AST");
if (!FromValue.getLValueBase().is<TypeInfoLValue>()) {
if (const auto *E =
FromValue.getLValueBase().dyn_cast<const Expr *>()) {
FromElemTy = E->getType();
const Expr *ImpExpr = importChecked(Err, E);
if (Err)
return std::move(Err);
Base = APValue::LValueBase(ImpExpr,
FromValue.getLValueBase().getCallIndex(),
FromValue.getLValueBase().getVersion());
} else {
FromElemTy =
FromValue.getLValueBase().get<const ValueDecl *>()->getType();
const Decl *ImpDecl = importChecked(
Err, FromValue.getLValueBase().get<const ValueDecl *>());
if (Err)
return std::move(Err);
Base = APValue::LValueBase(cast<ValueDecl>(ImpDecl),
FromValue.getLValueBase().getCallIndex(),
FromValue.getLValueBase().getVersion());
}
} else {
FromElemTy = FromValue.getLValueBase().getTypeInfoType();
QualType ImpTypeInfo = importChecked(
Err,
QualType(FromValue.getLValueBase().get<TypeInfoLValue>().getType(),
0));
QualType ImpType =
importChecked(Err, FromValue.getLValueBase().getTypeInfoType());
if (Err)
return std::move(Err);
Base = APValue::LValueBase::getTypeInfo(
TypeInfoLValue(ImpTypeInfo.getTypePtr()), ImpType);
}
}
CharUnits Offset = FromValue.getLValueOffset();
unsigned PathLength = FromValue.getLValuePath().size();
Result.MakeLValue();
if (FromValue.hasLValuePath()) {
MutableArrayRef<APValue::LValuePathEntry> ToPath = Result.setLValueUninit(
Base, Offset, PathLength, FromValue.isLValueOnePastTheEnd(),
FromValue.isNullPointer());
llvm::ArrayRef<APValue::LValuePathEntry> FromPath =
FromValue.getLValuePath();
for (unsigned LoopIdx = 0; LoopIdx < PathLength; LoopIdx++) {
if (FromElemTy->isRecordType()) {
const Decl *FromDecl =
FromPath[LoopIdx].getAsBaseOrMember().getPointer();
const Decl *ImpDecl = importChecked(Err, FromDecl);
if (Err)
return std::move(Err);
if (auto *RD = dyn_cast<CXXRecordDecl>(FromDecl))
FromElemTy = Importer.FromContext.getRecordType(RD);
else
FromElemTy = cast<ValueDecl>(FromDecl)->getType();
ToPath[LoopIdx] = APValue::LValuePathEntry(APValue::BaseOrMemberType(
ImpDecl, FromPath[LoopIdx].getAsBaseOrMember().getInt()));
} else {
FromElemTy =
Importer.FromContext.getAsArrayType(FromElemTy)->getElementType();
ToPath[LoopIdx] = APValue::LValuePathEntry::ArrayIndex(
FromPath[LoopIdx].getAsArrayIndex());
}
}
} else
Result.setLValue(Base, Offset, APValue::NoLValuePath{},
FromValue.isNullPointer());
}
if (Err)
return std::move(Err);
return Result;
}
Expected<DeclarationName> ASTImporter::HandleNameConflict(DeclarationName Name,
DeclContext *DC,
unsigned IDNS,

View File

@ -360,7 +360,6 @@ llvm::APSInt ConstantExpr::getResultAsAPSInt() const {
}
APValue ConstantExpr::getAPValueResult() const {
assert(hasAPValueResult());
switch (ConstantExprBits.ResultKind) {
case ConstantExpr::RSK_APValue:
@ -370,6 +369,8 @@ APValue ConstantExpr::getAPValueResult() const {
llvm::APSInt(llvm::APInt(ConstantExprBits.BitWidth, Int64Result()),
ConstantExprBits.IsUnsigned));
case ConstantExpr::RSK_None:
if (ConstantExprBits.APValueKind == APValue::Indeterminate)
return APValue::IndeterminateValue();
return APValue();
}
llvm_unreachable("invalid ResultKind");

View File

@ -8963,48 +8963,146 @@ ReadFixedPointSemantics(const SmallVectorImpl<uint64_t> &Record,
HasUnsignedPadding);
}
static const llvm::fltSemantics &
readAPFloatSemantics(ASTRecordReader &reader) {
return llvm::APFloatBase::EnumToSemantics(
static_cast<llvm::APFloatBase::Semantics>(reader.readInt()));
}
APValue ASTRecordReader::readAPValue() {
unsigned Kind = readInt();
switch ((APValue::ValueKind) Kind) {
auto Kind = static_cast<APValue::ValueKind>(asImpl().readUInt32());
switch (Kind) {
case APValue::None:
return APValue();
case APValue::Indeterminate:
return APValue::IndeterminateValue();
case APValue::Int:
return APValue(readAPSInt());
return APValue(asImpl().readAPSInt());
case APValue::Float: {
const llvm::fltSemantics &FloatSema = readAPFloatSemantics(*this);
return APValue(readAPFloat(FloatSema));
const llvm::fltSemantics &FloatSema = llvm::APFloatBase::EnumToSemantics(
static_cast<llvm::APFloatBase::Semantics>(asImpl().readUInt32()));
return APValue(asImpl().readAPFloat(FloatSema));
}
case APValue::FixedPoint: {
llvm::FixedPointSemantics FPSema = ReadFixedPointSemantics(Record, Idx);
return APValue(llvm::APFixedPoint(readAPInt(), FPSema));
}
case APValue::ComplexInt: {
llvm::APSInt First = readAPSInt();
return APValue(std::move(First), readAPSInt());
llvm::APSInt First = asImpl().readAPSInt();
return APValue(std::move(First), asImpl().readAPSInt());
}
case APValue::ComplexFloat: {
const llvm::fltSemantics &FloatSema1 = readAPFloatSemantics(*this);
llvm::APFloat First = readAPFloat(FloatSema1);
const llvm::fltSemantics &FloatSema2 = readAPFloatSemantics(*this);
return APValue(std::move(First), readAPFloat(FloatSema2));
const llvm::fltSemantics &FloatSema = llvm::APFloatBase::EnumToSemantics(
static_cast<llvm::APFloatBase::Semantics>(asImpl().readUInt32()));
llvm::APFloat First = readAPFloat(FloatSema);
return APValue(std::move(First), asImpl().readAPFloat(FloatSema));
}
case APValue::Vector: {
APValue Result;
Result.MakeVector();
unsigned Length = asImpl().readUInt32();
(void)Result.setVectorUninit(Length);
for (unsigned LoopIdx = 0; LoopIdx < Length; LoopIdx++)
Result.getVectorElt(LoopIdx) = asImpl().readAPValue();
return Result;
}
case APValue::Array: {
APValue Result;
unsigned InitLength = asImpl().readUInt32();
unsigned TotalLength = asImpl().readUInt32();
Result.MakeArray(InitLength, TotalLength);
for (unsigned LoopIdx = 0; LoopIdx < InitLength; LoopIdx++)
Result.getArrayInitializedElt(LoopIdx) = asImpl().readAPValue();
return Result;
}
case APValue::Struct: {
APValue Result;
unsigned BasesLength = asImpl().readUInt32();
unsigned FieldsLength = asImpl().readUInt32();
Result.MakeStruct(BasesLength, FieldsLength);
for (unsigned LoopIdx = 0; LoopIdx < BasesLength; LoopIdx++)
Result.getStructBase(LoopIdx) = asImpl().readAPValue();
for (unsigned LoopIdx = 0; LoopIdx < FieldsLength; LoopIdx++)
Result.getStructField(LoopIdx) = asImpl().readAPValue();
return Result;
}
case APValue::Union: {
auto *FDecl = asImpl().readDeclAs<FieldDecl>();
APValue Value = asImpl().readAPValue();
return APValue(FDecl, std::move(Value));
}
case APValue::AddrLabelDiff: {
auto *LHS = cast<AddrLabelExpr>(asImpl().readExpr());
auto *RHS = cast<AddrLabelExpr>(asImpl().readExpr());
return APValue(LHS, RHS);
}
case APValue::MemberPointer: {
APValue Result;
bool IsDerived = asImpl().readUInt32();
auto *Member = asImpl().readDeclAs<ValueDecl>();
unsigned PathSize = asImpl().readUInt32();
const CXXRecordDecl **PathArray =
Result.setMemberPointerUninit(Member, IsDerived, PathSize).data();
for (unsigned LoopIdx = 0; LoopIdx < PathSize; LoopIdx++)
PathArray[LoopIdx] =
asImpl().readDeclAs<const CXXRecordDecl>()->getCanonicalDecl();
return Result;
}
case APValue::LValue: {
uint64_t Bits = asImpl().readUInt32();
bool HasLValuePath = Bits & 0x1;
bool IsLValueOnePastTheEnd = Bits & 0x2;
bool IsExpr = Bits & 0x4;
bool IsTypeInfo = Bits & 0x8;
bool IsNullPtr = Bits & 0x10;
bool HasBase = Bits & 0x20;
APValue::LValueBase Base;
QualType ElemTy;
assert((!IsExpr || !IsTypeInfo) && "LValueBase cannot be both");
if (HasBase) {
if (!IsTypeInfo) {
unsigned CallIndex = asImpl().readUInt32();
unsigned Version = asImpl().readUInt32();
if (IsExpr) {
Base = APValue::LValueBase(asImpl().readExpr(), CallIndex, Version);
ElemTy = Base.get<const Expr *>()->getType();
} else {
Base = APValue::LValueBase(asImpl().readDeclAs<const ValueDecl>(),
CallIndex, Version);
ElemTy = Base.get<const ValueDecl *>()->getType();
}
} else {
QualType TypeInfo = asImpl().readType();
QualType Type = asImpl().readType();
Base = APValue::LValueBase::getTypeInfo(
TypeInfoLValue(TypeInfo.getTypePtr()), Type);
Base.getTypeInfoType();
}
}
CharUnits Offset = CharUnits::fromQuantity(asImpl().readUInt32());
unsigned PathLength = asImpl().readUInt32();
APValue Result;
Result.MakeLValue();
if (HasLValuePath) {
APValue::LValuePathEntry *Path =
Result
.setLValueUninit(Base, Offset, PathLength, IsLValueOnePastTheEnd,
IsNullPtr)
.data();
for (unsigned LoopIdx = 0; LoopIdx < PathLength; LoopIdx++) {
if (ElemTy->getAs<RecordType>()) {
unsigned Int = asImpl().readUInt32();
Decl *D = asImpl().readDeclAs<Decl>();
if (auto *RD = dyn_cast<CXXRecordDecl>(D))
ElemTy = getASTContext().getRecordType(RD);
else
ElemTy = cast<ValueDecl>(D)->getType();
Path[LoopIdx] =
APValue::LValuePathEntry(APValue::BaseOrMemberType(D, Int));
} else {
ElemTy = getASTContext().getAsArrayType(ElemTy)->getElementType();
Path[LoopIdx] =
APValue::LValuePathEntry::ArrayIndex(asImpl().readUInt32());
}
}
} else
Result.setLValue(Base, Offset, APValue::NoLValuePath{}, IsNullPtr);
return Result;
}
case APValue::LValue:
case APValue::Vector:
case APValue::Array:
case APValue::Struct:
case APValue::Union:
case APValue::MemberPointer:
case APValue::AddrLabelDiff:
// TODO : Handle all these APValue::ValueKind.
return APValue();
}
llvm_unreachable("Invalid APValue::ValueKind");
}

View File

@ -5155,22 +5155,103 @@ void ASTRecordWriter::AddAPValue(const APValue &Value) {
return;
}
case APValue::ComplexFloat: {
assert(llvm::APFloatBase::SemanticsToEnum(
Value.getComplexFloatImag().getSemantics()) ==
llvm::APFloatBase::SemanticsToEnum(
Value.getComplexFloatReal().getSemantics()));
push_back(static_cast<uint64_t>(llvm::APFloatBase::SemanticsToEnum(
Value.getComplexFloatReal().getSemantics())));
AddAPFloat(Value.getComplexFloatReal());
push_back(static_cast<uint64_t>(llvm::APFloatBase::SemanticsToEnum(
Value.getComplexFloatImag().getSemantics())));
AddAPFloat(Value.getComplexFloatImag());
return;
}
case APValue::LValue:
case APValue::Vector:
push_back(Value.getVectorLength());
for (unsigned Idx = 0; Idx < Value.getVectorLength(); Idx++)
AddAPValue(Value.getVectorElt(Idx));
return;
case APValue::Array:
push_back(Value.getArrayInitializedElts());
push_back(Value.getArraySize());
for (unsigned Idx = 0; Idx < Value.getArrayInitializedElts(); Idx++)
AddAPValue(Value.getArrayInitializedElt(Idx));
return;
case APValue::Struct:
push_back(Value.getStructNumBases());
push_back(Value.getStructNumFields());
for (unsigned Idx = 0; Idx < Value.getStructNumBases(); Idx++)
AddAPValue(Value.getStructBase(Idx));
for (unsigned Idx = 0; Idx < Value.getStructNumFields(); Idx++)
AddAPValue(Value.getStructField(Idx));
return;
case APValue::Union:
case APValue::MemberPointer:
AddDeclRef(Value.getUnionField());
AddAPValue(Value.getUnionValue());
return;
case APValue::AddrLabelDiff:
// TODO : Handle all these APValue::ValueKind.
AddStmt(const_cast<AddrLabelExpr *>(Value.getAddrLabelDiffLHS()));
AddStmt(const_cast<AddrLabelExpr *>(Value.getAddrLabelDiffRHS()));
return;
case APValue::MemberPointer: {
push_back(Value.isMemberPointerToDerivedMember());
AddDeclRef(Value.getMemberPointerDecl());
ArrayRef<const CXXRecordDecl *> RecordPath = Value.getMemberPointerPath();
push_back(RecordPath.size());
for (auto Elem : RecordPath)
AddDeclRef(Elem);
return;
}
case APValue::LValue: {
push_back(Value.hasLValuePath() | Value.isLValueOnePastTheEnd() << 1 |
Value.getLValueBase().is<const Expr *>() << 2 |
Value.getLValueBase().is<TypeInfoLValue>() << 3 |
Value.isNullPointer() << 4 |
static_cast<bool>(Value.getLValueBase()) << 5);
QualType ElemTy;
if (Value.getLValueBase()) {
assert(!Value.getLValueBase().is<DynamicAllocLValue>() &&
"in C++20 dynamic allocation are transient so they shouldn't "
"appear in the AST");
if (!Value.getLValueBase().is<TypeInfoLValue>()) {
push_back(Value.getLValueBase().getCallIndex());
push_back(Value.getLValueBase().getVersion());
if (const auto *E = Value.getLValueBase().dyn_cast<const Expr *>()) {
AddStmt(const_cast<Expr *>(E));
ElemTy = E->getType();
} else {
AddDeclRef(Value.getLValueBase().get<const ValueDecl *>());
ElemTy = Value.getLValueBase().get<const ValueDecl *>()->getType();
}
} else {
AddTypeRef(
QualType(Value.getLValueBase().get<TypeInfoLValue>().getType(), 0));
AddTypeRef(Value.getLValueBase().getTypeInfoType());
ElemTy = Value.getLValueBase().getTypeInfoType();
}
}
push_back(Value.getLValueOffset().getQuantity());
push_back(Value.getLValuePath().size());
if (Value.hasLValuePath()) {
ArrayRef<APValue::LValuePathEntry> Path = Value.getLValuePath();
for (auto Elem : Path) {
if (ElemTy->getAs<RecordType>()) {
push_back(Elem.getAsBaseOrMember().getInt());
const Decl *BaseOrMember = Elem.getAsBaseOrMember().getPointer();
if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {
AddDeclRef(RD);
ElemTy = Writer->Context->getRecordType(RD);
} else {
const auto *VD = cast<ValueDecl>(BaseOrMember);
AddDeclRef(VD);
ElemTy = VD->getType();
}
} else {
push_back(Elem.getAsArrayIndex());
ElemTy = Writer->Context->getAsArrayType(ElemTy)->getElementType();
}
}
}
}
return;
}
llvm_unreachable("Invalid APValue::ValueKind");

View File

@ -0,0 +1,462 @@
// RUN: %clang_cc1 -std=gnu++2a -emit-pch %s -o %t.pch
// RUN: %clang_cc1 -std=gnu++2a %s -DEMIT -ast-merge %t.pch -ast-dump-all | FileCheck %s
// XFAIL: *
#ifndef EMIT
#define EMIT
namespace Integer {
consteval int fint() {
return 6789;
}
int Unique_Int = fint();
//CHECK: VarDecl {{.*}} Unique_Int
//CHECK-NEXT: ConstantExpr {{.*}} 'int'
//CHECK-NEXT: value: Int 6789
consteval __uint128_t fint128() {
return ((__uint128_t)0x75f17d6b3588f843 << 64) | 0xb13dea7c9c324e51;
}
constexpr __uint128_t Unique_Int128 = fint128();
//CHECK: VarDecl {{.*}} Unique_Int128
//CHECK-NEXT: value: Int 156773562844924187900898496343692168785
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: Int 156773562844924187900898496343692168785
} // namespace Integer
namespace FloatingPoint {
consteval double fdouble() {
return double(567890.67890);
}
double Unique_Double = fdouble();
//CHECK: VarDecl {{.*}} Unique_Double
//CHECK-NEXT: ConstantExpr {{.*}}
//CHECK-NEXT: value: Float 5.678907e+05
} // namespace FloatingPoint
// FIXME: Add test for FixedPoint, ComplexInt, ComplexFloat, AddrLabelDiff.
namespace Struct {
struct B {
int i;
double d;
};
consteval B fB() {
return B{1, 0.7};
}
constexpr B Basic_Struct = fB();
//CHECK: VarDecl {{.*}} Basic_Struct
//CHECK-NEXT: value: Struct
//CHECK-NEXT: fields: Int 1, Float 7.000000e-01
//CHECK-NEXT: ImplicitCastExpr
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: Struct
//CHECK-NEXT: fields: Int 1, Float 7.000000e-01
struct C {
int i = 9;
};
struct A : B {
constexpr A(B b, int I, double D, C _c) : B(b), i(I), d(D), c(_c) {}
int i;
double d;
C c;
};
consteval A fA() {
return A(Basic_Struct, 1, 79.789, {});
}
A Advanced_Struct = fA();
//CHECK: VarDecl {{.*}} Advanced_Struct
//CHECK-NEXT: ConstantExpr {{.*}}
//CHECK-NEXT: value: Struct
//CHECK-NEXT: base: Struct
//CHECK-NEXT: fields: Int 1, Float 7.000000e-01
//CHECK-NEXT: fields: Int 1, Float 7.978900e+01
//CHECK-NEXT: field: Struct
//CHECK-NEXT: field: Int 9
} // namespace Struct
namespace Vector {
using v4si = int __attribute__((__vector_size__(16)));
consteval v4si fv4si() {
return (v4si){8, 2, 3};
}
v4si Vector_Int = fv4si();
//CHECK: VarDecl {{.*}} Vector_Int
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: Vector length=4
//CHECK-NEXT: elements: Int 8, Int 2, Int 3, Int 0
} // namespace Vector
namespace Array {
struct B {
int arr[6];
};
consteval B fint() {
return B{1, 2, 3, 4, 5, 6};
}
B Array_Int = fint();
//CHECK: VarDecl {{.*}} Array_Int
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: Struct
//CHECK-NEXT: field: Array size=6
//CHECK-NEXT: elements: Int 1, Int 2, Int 3, Int 4
//CHECK-NEXT: elements: Int 5, Int 6
struct A {
int i = 789;
double d = 67890.09876;
};
struct C {
A arr[3];
};
consteval C fA() {
return {{A{}, A{-45678, 9.8}, A{9}}};
}
C Array2_Struct = fA();
//CHECK: VarDecl {{.*}} Array2_Struct
//CHECK-NEXT: ConstantExpr {{.*}}
using v4si = int __attribute__((__vector_size__(16)));
struct D {
v4si arr[2];
};
consteval D fv4si() {
return {{{1, 2, 3, 4}, {4, 5, 6, 7}}};
}
D Array_Vector = fv4si();
//CHECK: VarDecl {{.*}} Array_Vector
//CHECK-NEXT: ConstantExpr {{.*}}
//CHECK-NEXT: value: Struct
//CHECK-NEXT: field: Array size=2
//CHECK-NEXT: element: Vector length=4
//CHECK-NEXT: elements: Int 1, Int 2, Int 3, Int 4
//CHECK-NEXT: element: Vector length=4
//CHECK-NEXT: elements: Int 4, Int 5, Int 6, Int 7
} // namespace Array
namespace Union {
struct A {
int i = 6789;
float f = 987.9876;
};
union U {
int i;
A a{567890, 9876.5678f};
};
consteval U fU1() {
return U{0};
}
U Unique_Union1 = fU1();
//CHECK: VarDecl {{.*}} Unique_Union
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: Union .i Int 0
consteval U fU() {
return U{};
}
U Unique_Union2 = fU();
//CHECK: VarDecl {{.*}} Unique_Union
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: Union .a
//CHECK-NEXT: Struct
//CHECK-NEXT: fields: Int 567890, Float 9.876567e+03
} // namespace Union
namespace MemberPointer {
struct A {
struct B {
struct C {
struct D {
struct E {
struct F {
struct G {
int i;
};
};
};
};
};
};
};
consteval auto fmem_ptr() -> decltype(&A::B::C::D::E::F::G::i) {
return &A::B::C::D::E::F::G::i;
}
auto MemberPointer1 = fmem_ptr();
//CHECK: VarDecl {{.*}} MemberPointer1
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: MemberPointer &G::i
struct A1 {
struct B1 {
int f() const {
return 0;
}
};
};
consteval auto fmem_ptr2() {
return &A1::B1::f;
}
auto MemberPointer2 = fmem_ptr2();
//CHECK: VarDecl {{.*}} MemberPointer2
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: MemberPointer &B1::f
} // namespace MemberPointer
namespace std {
struct type_info;
};
namespace LValue {
constexpr int g = 0;
consteval const int &fg_ref() {
return g;
}
const int &g_ref = fg_ref();
//CHECK: VarDecl {{.*}} g_ref
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue &g
consteval const int *fint_ptr() {
return &g;
}
const int *g_ptr = fint_ptr();
//CHECK: VarDecl {{.*}} g_ptr
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue &g
consteval const int *fnull_ptr() {
return nullptr;
}
const int *ptr2 = fnull_ptr();
//CHECK: VarDecl {{.*}} ptr2
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue nullptr
int fconst();
consteval auto ffunc_ptr() {
return &fconst;
}
int (*func_ptr)() = ffunc_ptr();
//CHECK: VarDecl {{.*}} func_ptr
//CHECK-NEXT: ConstantExpr {{.*}}
//CHECK-NEXT: value: LValue &fconst
struct A {
int Arr[6] = {0, 1, 3, 4, 5, 9};
int i = 0;
};
struct D {
A arr[6] = {};
};
consteval D fA() {
return {};
}
constexpr D Arr = fA();
// CHECK: VarDecl {{.*}} Arr
// CHECK-NEXT: value: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: ImplicitCastExpr
// CHECK-NEXT: ConstantExpr
// CHECK-NEXT: value: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
// CHECK-NEXT: element: Struct
// CHECK-NEXT: field: Array size=6
// CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
// CHECK-NEXT: elements: Int 5, Int 9
// CHECK-NEXT: field: Int 0
consteval const int &fconstintref() {
return Arr.arr[0].i;
}
const int &ArrayStructRef1 = fconstintref();
//CHECK: VarDecl {{.*}} ArrayStructRef1
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue &Arr.arr[0].i
consteval const int &fconstintref2() {
return Arr.arr[1].Arr[5];
}
const int &ArrayStructRef2 = fconstintref2();
//CHECK: VarDecl {{.*}} ArrayStructRef2
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue &Arr.arr[1].Arr[5]
consteval const int *fconststar() {
return &ArrayStructRef2;
}
const int *ArrayStructRef3 = fconststar();
//CHECK: VarDecl {{.*}} ArrayStructRef3
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue &Arr.arr[1].Arr[5]
struct B : A {
};
struct C {
B b;
};
consteval C fC() {
return {};
}
C c = fC();
//CHECK: VarDecl {{.*}} c
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: Struct
//CHECK-NEXT: field: Struct
//CHECK-NEXT: base: Struct
//CHECK-NEXT: field: Array size=6
//CHECK-NEXT: elements: Int 0, Int 1, Int 3, Int 4
//CHECK-NEXT: elements: Int 5, Int 9
//CHECK-NEXT: field: Int 0
consteval const int &f2constintref() {
return c.b.i;
}
const int &StructPathRef = f2constintref();
//CHECK: VarDecl {{.*}} StructPathRef
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue &c.b.A::i
consteval const std::type_info *ftype_info() {
return &typeid(c);
}
const std::type_info *T1 = ftype_info();
//CHECK: VarDecl {{.*}} T1
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT:value: LValue &typeid(LValue::C)
consteval const std::type_info *ftype_info2() {
return &typeid(Arr.arr[1].Arr[2]);
}
const std::type_info *T2 = ftype_info2();
//CHECK: VarDecl {{.*}} T2
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue &typeid(int)
consteval const char *fstring() {
return "test";
}
const char *cptr = fstring();
//CHECK: VarDecl {{.*}} cptr
//CHECK-NEXT: ConstantExpr
//CHECK-NEXT: value: LValue &"test"[0]
} // namespace LValue
#endif