mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-25 17:31:02 +00:00
[lldb][PDB] Constexpr static member values as AST literals
Summary: When evaluating an expression referencing a constexpr static member variable, an error is issued because the PDB does not specify a symbol with an address that can be relocated against. Rather than attempt to resolve the variable's value within the IR execution, the values of all constants can be looked up and incorporated into the AST of the record type as a literal, mirroring the original compiler AST. This change applies to DIA and native PDB loaders. Patch By: jackoalan Reviewers: aleksandr.urakov, jasonmolenda, zturner, jdoerfert, teemperor Reviewed By: aleksandr.urakov Subscribers: sstefan1, lldb-commits, llvm-commits, #lldb Tags: #lldb, #llvm Differential Revision: https://reviews.llvm.org/D82160
This commit is contained in:
parent
2c061998b5
commit
895529cfd8
@ -681,7 +681,7 @@ bool PdbAstBuilder::CompleteTagDecl(clang::TagDecl &tag) {
|
||||
// Visit all members of this class, then perform any finalization necessary
|
||||
// to complete the class.
|
||||
CompilerType ct = ToCompilerType(tag_qt);
|
||||
UdtRecordCompleter completer(best_ti, ct, tag, *this, m_index.tpi());
|
||||
UdtRecordCompleter completer(best_ti, ct, tag, *this, m_index);
|
||||
auto error =
|
||||
llvm::codeview::visitMemberRecordStream(field_list_cvt.data(), completer);
|
||||
completer.complete();
|
||||
|
@ -6,14 +6,17 @@
|
||||
#include "PdbUtil.h"
|
||||
|
||||
#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
|
||||
#include "Plugins/ExpressionParser/Clang/ClangUtil.h"
|
||||
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
||||
#include "lldb/Symbol/Type.h"
|
||||
#include "lldb/Utility/LLDBAssert.h"
|
||||
#include "lldb/lldb-enumerations.h"
|
||||
#include "lldb/lldb-forward.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBTypes.h"
|
||||
|
||||
@ -29,10 +32,10 @@ UdtRecordCompleter::UdtRecordCompleter(PdbTypeSymId id,
|
||||
CompilerType &derived_ct,
|
||||
clang::TagDecl &tag_decl,
|
||||
PdbAstBuilder &ast_builder,
|
||||
TpiStream &tpi)
|
||||
PdbIndex &index)
|
||||
: m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl),
|
||||
m_ast_builder(ast_builder), m_tpi(tpi) {
|
||||
CVType cvt = m_tpi.getType(m_id.index);
|
||||
m_ast_builder(ast_builder), m_index(index) {
|
||||
CVType cvt = m_index.tpi().getType(m_id.index);
|
||||
switch (cvt.kind()) {
|
||||
case LF_ENUM:
|
||||
llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, m_cvr.er));
|
||||
@ -55,7 +58,7 @@ clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex(
|
||||
PdbTypeSymId type_id(ti);
|
||||
clang::QualType qt = m_ast_builder.GetOrCreateType(type_id);
|
||||
|
||||
CVType udt_cvt = m_tpi.getType(ti);
|
||||
CVType udt_cvt = m_index.tpi().getType(ti);
|
||||
|
||||
std::unique_ptr<clang::CXXBaseSpecifier> base_spec =
|
||||
m_ast_builder.clang().CreateBaseClassSpecifier(
|
||||
@ -128,9 +131,70 @@ Error UdtRecordCompleter::visitKnownMember(
|
||||
|
||||
lldb::AccessType access =
|
||||
TranslateMemberAccess(static_data_member.getAccess());
|
||||
TypeSystemClang::AddVariableToRecordType(
|
||||
auto decl = TypeSystemClang::AddVariableToRecordType(
|
||||
m_derived_ct, static_data_member.Name, member_ct, access);
|
||||
|
||||
// Static constant members may be a const[expr] declaration.
|
||||
// Query the symbol's value as the variable initializer if valid.
|
||||
if (member_ct.IsConst()) {
|
||||
std::string qual_name = decl->getQualifiedNameAsString();
|
||||
|
||||
auto results =
|
||||
m_index.globals().findRecordsByName(qual_name, m_index.symrecords());
|
||||
|
||||
for (const auto &result : results) {
|
||||
if (result.second.kind() == SymbolKind::S_CONSTANT) {
|
||||
ConstantSym constant(SymbolRecordKind::ConstantSym);
|
||||
cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(result.second,
|
||||
constant));
|
||||
|
||||
clang::QualType qual_type = decl->getType();
|
||||
unsigned type_width = decl->getASTContext().getIntWidth(qual_type);
|
||||
unsigned constant_width = constant.Value.getBitWidth();
|
||||
|
||||
if (qual_type->isIntegralOrEnumerationType()) {
|
||||
if (type_width >= constant_width) {
|
||||
TypeSystemClang::SetIntegerInitializerForVariable(
|
||||
decl, constant.Value.extOrTrunc(type_width));
|
||||
} else {
|
||||
LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST),
|
||||
"Class '{0}' has a member '{1}' of type '{2}' ({3} bits) "
|
||||
"which resolves to a wider constant value ({4} bits). "
|
||||
"Ignoring constant.",
|
||||
m_derived_ct.GetTypeName(), static_data_member.Name,
|
||||
member_ct.GetTypeName(), type_width, constant_width);
|
||||
}
|
||||
} else {
|
||||
lldb::BasicType basic_type_enum = member_ct.GetBasicTypeEnumeration();
|
||||
switch (basic_type_enum) {
|
||||
case lldb::eBasicTypeFloat:
|
||||
case lldb::eBasicTypeDouble:
|
||||
case lldb::eBasicTypeLongDouble:
|
||||
if (type_width == constant_width) {
|
||||
TypeSystemClang::SetFloatingInitializerForVariable(
|
||||
decl, basic_type_enum == lldb::eBasicTypeFloat
|
||||
? llvm::APFloat(constant.Value.bitsToFloat())
|
||||
: llvm::APFloat(constant.Value.bitsToDouble()));
|
||||
decl->setConstexpr(true);
|
||||
} else {
|
||||
LLDB_LOG(
|
||||
GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST),
|
||||
"Class '{0}' has a member '{1}' of type '{2}' ({3} bits) "
|
||||
"which resolves to a constant value of mismatched width "
|
||||
"({4} bits). Ignoring constant.",
|
||||
m_derived_ct.GetTypeName(), static_data_member.Name,
|
||||
member_ct.GetTypeName(), type_width, constant_width);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Add a PdbSymUid namespace for field list members and update
|
||||
// the m_uid_to_decl map with this decl.
|
||||
return Error::success();
|
||||
@ -149,7 +213,7 @@ Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
|
||||
|
||||
TypeIndex ti(data_member.Type);
|
||||
if (!ti.isSimple()) {
|
||||
CVType cvt = m_tpi.getType(ti);
|
||||
CVType cvt = m_index.tpi().getType(ti);
|
||||
if (cvt.kind() == LF_BITFIELD) {
|
||||
BitFieldRecord bfr;
|
||||
llvm::cantFail(TypeDeserializer::deserializeAs<BitFieldRecord>(cvt, bfr));
|
||||
@ -187,7 +251,7 @@ Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
|
||||
OverloadedMethodRecord &overloaded) {
|
||||
TypeIndex method_list_idx = overloaded.MethodList;
|
||||
|
||||
CVType method_list_type = m_tpi.getType(method_list_idx);
|
||||
CVType method_list_type = m_index.tpi().getType(method_list_idx);
|
||||
assert(method_list_type.kind() == LF_METHODLIST);
|
||||
|
||||
MethodOverloadListRecord method_list;
|
||||
|
@ -25,6 +25,7 @@ class TagDecl;
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
class TpiStream;
|
||||
class GlobalsStream;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
@ -33,6 +34,7 @@ class Type;
|
||||
class CompilerType;
|
||||
namespace npdb {
|
||||
class PdbAstBuilder;
|
||||
class PdbIndex;
|
||||
|
||||
class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks {
|
||||
using IndexedBase =
|
||||
@ -49,14 +51,14 @@ class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks {
|
||||
CompilerType &m_derived_ct;
|
||||
clang::TagDecl &m_tag_decl;
|
||||
PdbAstBuilder &m_ast_builder;
|
||||
llvm::pdb::TpiStream &m_tpi;
|
||||
PdbIndex &m_index;
|
||||
std::vector<IndexedBase> m_bases;
|
||||
ClangASTImporter::LayoutInfo m_layout;
|
||||
|
||||
public:
|
||||
UdtRecordCompleter(PdbTypeSymId id, CompilerType &derived_ct,
|
||||
clang::TagDecl &tag_decl, PdbAstBuilder &ast_builder,
|
||||
llvm::pdb::TpiStream &tpi);
|
||||
PdbIndex &index);
|
||||
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
llvm::Error visitKnownMember(llvm::codeview::CVMemberRecord &CVR, \
|
||||
|
@ -1265,6 +1265,52 @@ void PDBASTParser::AddRecordMembers(
|
||||
if (!decl)
|
||||
continue;
|
||||
|
||||
// Static constant members may be a const[expr] declaration.
|
||||
// Query the symbol's value as the variable initializer if valid.
|
||||
if (member_comp_type.IsConst()) {
|
||||
auto value = member->getValue();
|
||||
clang::QualType qual_type = decl->getType();
|
||||
unsigned type_width = m_ast.getASTContext().getIntWidth(qual_type);
|
||||
unsigned constant_width = value.getBitWidth();
|
||||
|
||||
if (qual_type->isIntegralOrEnumerationType()) {
|
||||
if (type_width >= constant_width) {
|
||||
TypeSystemClang::SetIntegerInitializerForVariable(
|
||||
decl, value.toAPSInt().extOrTrunc(type_width));
|
||||
} else {
|
||||
LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST),
|
||||
"Class '{0}' has a member '{1}' of type '{2}' ({3} bits) "
|
||||
"which resolves to a wider constant value ({4} bits). "
|
||||
"Ignoring constant.",
|
||||
record_type.GetTypeName(), member_name,
|
||||
member_comp_type.GetTypeName(), type_width,
|
||||
constant_width);
|
||||
}
|
||||
} else {
|
||||
switch (member_comp_type.GetBasicTypeEnumeration()) {
|
||||
case lldb::eBasicTypeFloat:
|
||||
case lldb::eBasicTypeDouble:
|
||||
case lldb::eBasicTypeLongDouble:
|
||||
if (type_width == constant_width) {
|
||||
TypeSystemClang::SetFloatingInitializerForVariable(
|
||||
decl, value.toAPFloat());
|
||||
decl->setConstexpr(true);
|
||||
} else {
|
||||
LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST),
|
||||
"Class '{0}' has a member '{1}' of type '{2}' ({3} "
|
||||
"bits) which resolves to a constant value of mismatched "
|
||||
"width ({4} bits). Ignoring constant.",
|
||||
record_type.GetTypeName(), member_name,
|
||||
member_comp_type.GetTypeName(), type_width,
|
||||
constant_width);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_uid_to_decl[member->getSymIndexId()] = decl;
|
||||
|
||||
break;
|
||||
|
@ -7316,6 +7316,35 @@ clang::VarDecl *TypeSystemClang::AddVariableToRecordType(
|
||||
return var_decl;
|
||||
}
|
||||
|
||||
void TypeSystemClang::SetIntegerInitializerForVariable(
|
||||
VarDecl *var, const llvm::APInt &init_value) {
|
||||
assert(!var->hasInit() && "variable already initialized");
|
||||
|
||||
clang::ASTContext &ast = var->getASTContext();
|
||||
QualType qt = var->getType();
|
||||
assert(qt->isIntegralOrEnumerationType() &&
|
||||
"only integer or enum types supported");
|
||||
// If the variable is an enum type, take the underlying integer type as
|
||||
// the type of the integer literal.
|
||||
if (const EnumType *enum_type = llvm::dyn_cast<EnumType>(qt.getTypePtr())) {
|
||||
const EnumDecl *enum_decl = enum_type->getDecl();
|
||||
qt = enum_decl->getIntegerType();
|
||||
}
|
||||
var->setInit(IntegerLiteral::Create(ast, init_value, qt.getUnqualifiedType(),
|
||||
SourceLocation()));
|
||||
}
|
||||
|
||||
void TypeSystemClang::SetFloatingInitializerForVariable(
|
||||
clang::VarDecl *var, const llvm::APFloat &init_value) {
|
||||
assert(!var->hasInit() && "variable already initialized");
|
||||
|
||||
clang::ASTContext &ast = var->getASTContext();
|
||||
QualType qt = var->getType();
|
||||
assert(qt->isFloatingType() && "only floating point types supported");
|
||||
var->setInit(FloatingLiteral::Create(
|
||||
ast, init_value, true, qt.getUnqualifiedType(), SourceLocation()));
|
||||
}
|
||||
|
||||
clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType(
|
||||
lldb::opaque_compiler_type_t type, llvm::StringRef name,
|
||||
const char *mangled_name, const CompilerType &method_clang_type,
|
||||
|
@ -873,6 +873,24 @@ public:
|
||||
const CompilerType &var_type,
|
||||
lldb::AccessType access);
|
||||
|
||||
/// Initializes a variable with an integer value.
|
||||
/// \param var The variable to initialize. Must not already have an
|
||||
/// initializer and must have an integer or enum type.
|
||||
/// \param init_value The integer value that the variable should be
|
||||
/// initialized to. Has to match the bit width of the
|
||||
/// variable type.
|
||||
static void SetIntegerInitializerForVariable(clang::VarDecl *var,
|
||||
const llvm::APInt &init_value);
|
||||
|
||||
/// Initializes a variable with a floating point value.
|
||||
/// \param var The variable to initialize. Must not already have an
|
||||
/// initializer and must have a floating point type.
|
||||
/// \param init_value The float value that the variable should be
|
||||
/// initialized to.
|
||||
static void
|
||||
SetFloatingInitializerForVariable(clang::VarDecl *var,
|
||||
const llvm::APFloat &init_value);
|
||||
|
||||
clang::CXXMethodDecl *AddMethodToCXXRecordType(
|
||||
lldb::opaque_compiler_type_t type, llvm::StringRef name,
|
||||
const char *mangled_name, const CompilerType &method_type,
|
||||
|
@ -3,6 +3,7 @@ namespace N1 {
|
||||
|
||||
namespace {
|
||||
enum Enum { Enum_0 = 1, Enum_1 = 2, Enum_2 = 4, Enum_3 = 8 };
|
||||
enum class ScopedEnum { Enum_0 = 1, Enum_1 = 2, Enum_2 = 4, Enum_3 = 8 };
|
||||
}
|
||||
|
||||
Enum Global = Enum_3;
|
||||
@ -22,6 +23,13 @@ public:
|
||||
const Enum m_ce;
|
||||
|
||||
static int ClassStatic;
|
||||
static const int ClassStaticConst = 8;
|
||||
static constexpr int ClassStaticConstexpr = 9;
|
||||
static constexpr float ClassStaticConstexprFloat = 10.f;
|
||||
static constexpr double ClassStaticConstexprDouble = 11.0;
|
||||
static constexpr long double ClassStaticConstexprLongDouble = 12.0;
|
||||
static const Enum ClassStaticConstEnum = Enum_3;
|
||||
static const ScopedEnum ClassStaticConstScopedEnum = ScopedEnum::Enum_2;
|
||||
|
||||
private:
|
||||
struct Inner {
|
||||
|
@ -4,7 +4,8 @@ RUN: env LLDB_USE_NATIVE_PDB_READER=0 lldb-test symbols -dump-ast %t.exe | FileC
|
||||
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=ENUM %s
|
||||
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=GLOBAL %s
|
||||
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=BASE %s
|
||||
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s
|
||||
RUN: env LLDB_USE_NATIVE_PDB_READER=0 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s
|
||||
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s
|
||||
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=INNER %s
|
||||
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=TEMPLATE %s
|
||||
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=FOO %s
|
||||
@ -46,6 +47,13 @@ CLASS: namespace N1 {
|
||||
CLASS: class Class : public N0::N1::Base {
|
||||
CLASS-DAG: const N0::N1::(anonymous namespace)::Enum m_ce;
|
||||
CLASS-DAG: static int ClassStatic;
|
||||
CLASS-DAG: static const int ClassStaticConst = 8;
|
||||
CLASS-DAG: static const int ClassStaticConstexpr = 9;
|
||||
CLASS-DAG: static constexpr float ClassStaticConstexprFloat = 10.F;
|
||||
CLASS-DAG: static constexpr double ClassStaticConstexprDouble = 11.;
|
||||
CLASS-DAG: static constexpr double ClassStaticConstexprLongDouble = 12.;
|
||||
CLASS-DAG: static const N0::N1::(anonymous namespace)::Enum ClassStaticConstEnum = 8;
|
||||
CLASS-DAG: static const N0::N1::(anonymous namespace)::ScopedEnum ClassStaticConstScopedEnum = 4;
|
||||
CLASS-DAG: N0::N1::Class::Inner m_inner;
|
||||
CLASS-DAG: {{(inline )?}}Class(N0::N1::(anonymous namespace)::Enum);
|
||||
CLASS-DAG: static {{(inline )?}}int StaticFunc(const N0::N1::Class &);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef LLVM_DEBUGINFO_PDB_PDBTYPES_H
|
||||
#define LLVM_DEBUGINFO_PDB_PDBTYPES_H
|
||||
|
||||
#include "llvm/ADT/APFloat.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
|
||||
#include "llvm/DebugInfo/PDB/IPDBFrameData.h"
|
||||
@ -464,6 +465,88 @@ struct Variant {
|
||||
char *String;
|
||||
} Value;
|
||||
|
||||
bool isIntegralType() const {
|
||||
switch (Type) {
|
||||
case Bool:
|
||||
case Int8:
|
||||
case Int16:
|
||||
case Int32:
|
||||
case Int64:
|
||||
case UInt8:
|
||||
case UInt16:
|
||||
case UInt32:
|
||||
case UInt64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#define VARIANT_WIDTH(Enum, NumBits) \
|
||||
case PDB_VariantType::Enum: \
|
||||
return NumBits;
|
||||
|
||||
unsigned getBitWidth() const {
|
||||
switch (Type) {
|
||||
VARIANT_WIDTH(Bool, 1u)
|
||||
VARIANT_WIDTH(Int8, 8u)
|
||||
VARIANT_WIDTH(Int16, 16u)
|
||||
VARIANT_WIDTH(Int32, 32u)
|
||||
VARIANT_WIDTH(Int64, 64u)
|
||||
VARIANT_WIDTH(Single, 32u)
|
||||
VARIANT_WIDTH(Double, 64u)
|
||||
VARIANT_WIDTH(UInt8, 8u)
|
||||
VARIANT_WIDTH(UInt16, 16u)
|
||||
VARIANT_WIDTH(UInt32, 32u)
|
||||
VARIANT_WIDTH(UInt64, 64u)
|
||||
default:
|
||||
assert(false && "Variant::toAPSInt called on non-numeric type");
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
#undef VARIANT_WIDTH
|
||||
|
||||
#define VARIANT_APSINT(Enum, NumBits, IsUnsigned) \
|
||||
case PDB_VariantType::Enum: \
|
||||
return APSInt(APInt(NumBits, Value.Enum), IsUnsigned);
|
||||
|
||||
APSInt toAPSInt() const {
|
||||
switch (Type) {
|
||||
VARIANT_APSINT(Bool, 1u, true)
|
||||
VARIANT_APSINT(Int8, 8u, false)
|
||||
VARIANT_APSINT(Int16, 16u, false)
|
||||
VARIANT_APSINT(Int32, 32u, false)
|
||||
VARIANT_APSINT(Int64, 64u, false)
|
||||
VARIANT_APSINT(UInt8, 8u, true)
|
||||
VARIANT_APSINT(UInt16, 16u, true)
|
||||
VARIANT_APSINT(UInt32, 32u, true)
|
||||
VARIANT_APSINT(UInt64, 64u, true)
|
||||
default:
|
||||
assert(false && "Variant::toAPSInt called on non-integral type");
|
||||
return APSInt();
|
||||
}
|
||||
}
|
||||
|
||||
#undef VARIANT_APSINT
|
||||
|
||||
APFloat toAPFloat() const {
|
||||
// Float constants may be tagged as integers.
|
||||
switch (Type) {
|
||||
case PDB_VariantType::Single:
|
||||
case PDB_VariantType::UInt32:
|
||||
case PDB_VariantType::Int32:
|
||||
return APFloat(Value.Single);
|
||||
case PDB_VariantType::Double:
|
||||
case PDB_VariantType::UInt64:
|
||||
case PDB_VariantType::Int64:
|
||||
return APFloat(Value.Double);
|
||||
default:
|
||||
assert(false && "Variant::toAPFloat called on non-floating-point type");
|
||||
return APFloat::getZero(APFloat::IEEEsingle());
|
||||
}
|
||||
}
|
||||
|
||||
#define VARIANT_EQUAL_CASE(Enum) \
|
||||
case PDB_VariantType::Enum: \
|
||||
return Value.Enum == Other.Value.Enum;
|
||||
|
Loading…
Reference in New Issue
Block a user