Skip to content

Commit aa5a99e

Browse files
authored
Merge pull request #8306 from Michael137/bugfix/lldb-origin-layout-tracking-to-20230725
[cherry-pick][stable/20230725] [lldb][ClangASTImporter] Import record layouts from origin if available Layout information for a record gets stored in the ClangASTImporter associated with the DWARFASTParserClang that originally parsed the record. LLDB sometimes moves clang types from one AST to another (in the reproducer the origin AST was a precompiled-header and the destination was the AST backing the executable). When clang then asks LLDB to layoutRecordType, it will do so with the help of the ClangASTImporter the type is associated with. If the type's origin is actually in a different LLDB module (and thus a different DWARFASTParserClang was used to set its layout info), we won't find the layout info in our local ClangASTImporter. In the reproducer this meant we would drop the alignment info of the origin type and misread a variable's contents with frame var and expr. There is logic in ClangASTSource::layoutRecordType to import an origin's layout info. This patch re-uses that infrastructure to import an origin's layout from one ClangASTImporter instance to another. rdar://123274144 (cherry picked from commit 07ffb7e)
2 parents 718808e + a444c28 commit aa5a99e

File tree

8 files changed

+441
-267
lines changed

8 files changed

+441
-267
lines changed

lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp

Lines changed: 248 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
#include "lldb/Utility/LLDBAssert.h"
1212
#include "lldb/Utility/LLDBLog.h"
1313
#include "lldb/Utility/Log.h"
14+
#include "clang/AST/ASTContext.h"
1415
#include "clang/AST/Decl.h"
1516
#include "clang/AST/DeclCXX.h"
1617
#include "clang/AST/DeclObjC.h"
18+
#include "clang/AST/RecordLayout.h"
1719
#include "clang/Sema/Lookup.h"
1820
#include "clang/Sema/Sema.h"
1921
#include "llvm/Support/Casting.h"
@@ -29,6 +31,7 @@
2931

3032
#include <memory>
3133
#include <optional>
34+
#include <type_traits>
3235

3336
using namespace lldb_private;
3437
using namespace clang;
@@ -531,6 +534,236 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
531534
return false;
532535
}
533536

537+
/// Copy layout information from \ref source_map to the \ref destination_map.
538+
///
539+
/// In the process of copying over layout info, we may need to import
540+
/// decls from the \ref source_map. This function will use the supplied
541+
/// \ref importer to import the necessary decls into \ref dest_ctx.
542+
///
543+
/// \param[in,out] dest_ctx Destination ASTContext into which we import
544+
/// decls from the \ref source_map.
545+
/// \param[out] destination_map A map from decls in \ref dest_ctx to an
546+
/// integral offest, which will be copies
547+
/// of the decl/offest pairs in \ref source_map
548+
/// if successful.
549+
/// \param[in] source_map A map from decls to integral offests. These will
550+
/// be copied into \ref destination_map.
551+
/// \param[in,out] importer Used to import decls into \ref dest_ctx.
552+
///
553+
/// \returns On success, will return 'true' and the offsets in \ref
554+
/// destination_map
555+
/// are usable copies of \ref source_map.
556+
template <class D, class O>
557+
static bool ImportOffsetMap(clang::ASTContext *dest_ctx,
558+
llvm::DenseMap<const D *, O> &destination_map,
559+
llvm::DenseMap<const D *, O> &source_map,
560+
ClangASTImporter &importer) {
561+
// When importing fields into a new record, clang has a hard requirement that
562+
// fields be imported in field offset order. Since they are stored in a
563+
// DenseMap with a pointer as the key type, this means we cannot simply
564+
// iterate over the map, as the order will be non-deterministic. Instead we
565+
// have to sort by the offset and then insert in sorted order.
566+
typedef llvm::DenseMap<const D *, O> MapType;
567+
typedef typename MapType::value_type PairType;
568+
std::vector<PairType> sorted_items;
569+
sorted_items.reserve(source_map.size());
570+
sorted_items.assign(source_map.begin(), source_map.end());
571+
llvm::sort(sorted_items, llvm::less_second());
572+
573+
for (const auto &item : sorted_items) {
574+
DeclFromUser<D> user_decl(const_cast<D *>(item.first));
575+
DeclFromParser<D> parser_decl(user_decl.Import(dest_ctx, importer));
576+
if (parser_decl.IsInvalid())
577+
return false;
578+
destination_map.insert(
579+
std::pair<const D *, O>(parser_decl.decl, item.second));
580+
}
581+
582+
return true;
583+
}
584+
585+
/// Given a CXXRecordDecl, will calculate and populate \ref base_offsets
586+
/// with the integral offsets of any of its (possibly virtual) base classes.
587+
///
588+
/// \param[in] record_layout ASTRecordLayout of \ref record.
589+
/// \param[in] record The record that we're calculating the base layouts of.
590+
/// \param[out] base_offsets Map of base-class decl to integral offset which
591+
/// this function will fill in.
592+
///
593+
/// \returns On success, will return 'true' and the offsets in \ref base_offsets
594+
/// are usable.
595+
template <bool IsVirtual>
596+
bool ExtractBaseOffsets(const ASTRecordLayout &record_layout,
597+
DeclFromUser<const CXXRecordDecl> &record,
598+
llvm::DenseMap<const clang::CXXRecordDecl *,
599+
clang::CharUnits> &base_offsets) {
600+
for (CXXRecordDecl::base_class_const_iterator
601+
bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()),
602+
be = (IsVirtual ? record->vbases_end() : record->bases_end());
603+
bi != be; ++bi) {
604+
if (!IsVirtual && bi->isVirtual())
605+
continue;
606+
607+
const clang::Type *origin_base_type = bi->getType().getTypePtr();
608+
const clang::RecordType *origin_base_record_type =
609+
origin_base_type->getAs<RecordType>();
610+
611+
if (!origin_base_record_type)
612+
return false;
613+
614+
DeclFromUser<RecordDecl> origin_base_record(
615+
origin_base_record_type->getDecl());
616+
617+
if (origin_base_record.IsInvalid())
618+
return false;
619+
620+
DeclFromUser<CXXRecordDecl> origin_base_cxx_record(
621+
DynCast<CXXRecordDecl>(origin_base_record));
622+
623+
if (origin_base_cxx_record.IsInvalid())
624+
return false;
625+
626+
CharUnits base_offset;
627+
628+
if (IsVirtual)
629+
base_offset =
630+
record_layout.getVBaseClassOffset(origin_base_cxx_record.decl);
631+
else
632+
base_offset =
633+
record_layout.getBaseClassOffset(origin_base_cxx_record.decl);
634+
635+
base_offsets.insert(std::pair<const CXXRecordDecl *, CharUnits>(
636+
origin_base_cxx_record.decl, base_offset));
637+
}
638+
639+
return true;
640+
}
641+
642+
bool ClangASTImporter::importRecordLayoutFromOrigin(
643+
const RecordDecl *record, uint64_t &size, uint64_t &alignment,
644+
llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
645+
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
646+
&base_offsets,
647+
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
648+
&vbase_offsets) {
649+
650+
Log *log = GetLog(LLDBLog::Expressions);
651+
652+
clang::ASTContext &dest_ctx = record->getASTContext();
653+
LLDB_LOG(log,
654+
"LayoutRecordType on (ASTContext*){0} '{1}' for (RecordDecl*)"
655+
"{2} [name = '{3}']",
656+
&dest_ctx,
657+
TypeSystemClang::GetASTContext(&dest_ctx)->getDisplayName(), record,
658+
record->getName());
659+
660+
DeclFromParser<const RecordDecl> parser_record(record);
661+
DeclFromUser<const RecordDecl> origin_record(parser_record.GetOrigin(*this));
662+
663+
if (origin_record.IsInvalid())
664+
return false;
665+
666+
std::remove_reference_t<decltype(field_offsets)> origin_field_offsets;
667+
std::remove_reference_t<decltype(base_offsets)> origin_base_offsets;
668+
std::remove_reference_t<decltype(vbase_offsets)> origin_virtual_base_offsets;
669+
670+
TypeSystemClang::GetCompleteDecl(
671+
&origin_record->getASTContext(),
672+
const_cast<RecordDecl *>(origin_record.decl));
673+
674+
clang::RecordDecl *definition = origin_record.decl->getDefinition();
675+
if (!definition || !definition->isCompleteDefinition())
676+
return false;
677+
678+
const ASTRecordLayout &record_layout(
679+
origin_record->getASTContext().getASTRecordLayout(origin_record.decl));
680+
681+
int field_idx = 0, field_count = record_layout.getFieldCount();
682+
683+
for (RecordDecl::field_iterator fi = definition->field_begin(),
684+
fe = definition->field_end();
685+
fi != fe; ++fi) {
686+
if (field_idx >= field_count)
687+
return false; // Layout didn't go well. Bail out.
688+
689+
uint64_t field_offset = record_layout.getFieldOffset(field_idx);
690+
691+
origin_field_offsets.insert(
692+
std::pair<const FieldDecl *, uint64_t>(*fi, field_offset));
693+
694+
field_idx++;
695+
}
696+
697+
DeclFromUser<const CXXRecordDecl> origin_cxx_record(
698+
DynCast<const CXXRecordDecl>(origin_record));
699+
700+
if (origin_cxx_record.IsValid()) {
701+
if (!ExtractBaseOffsets<false>(record_layout, origin_cxx_record,
702+
origin_base_offsets) ||
703+
!ExtractBaseOffsets<true>(record_layout, origin_cxx_record,
704+
origin_virtual_base_offsets))
705+
return false;
706+
}
707+
708+
if (!ImportOffsetMap(&dest_ctx, field_offsets, origin_field_offsets, *this) ||
709+
!ImportOffsetMap(&dest_ctx, base_offsets, origin_base_offsets, *this) ||
710+
!ImportOffsetMap(&dest_ctx, vbase_offsets, origin_virtual_base_offsets,
711+
*this))
712+
return false;
713+
714+
size = record_layout.getSize().getQuantity() * dest_ctx.getCharWidth();
715+
alignment =
716+
record_layout.getAlignment().getQuantity() * dest_ctx.getCharWidth();
717+
718+
if (log) {
719+
LLDB_LOG(log, "LRT returned:");
720+
LLDB_LOG(log, "LRT Original = (RecordDecl*){0}",
721+
static_cast<const void *>(origin_record.decl));
722+
LLDB_LOG(log, "LRT Size = {0}", size);
723+
LLDB_LOG(log, "LRT Alignment = {0}", alignment);
724+
LLDB_LOG(log, "LRT Fields:");
725+
for (RecordDecl::field_iterator fi = record->field_begin(),
726+
fe = record->field_end();
727+
fi != fe; ++fi) {
728+
LLDB_LOG(log,
729+
"LRT (FieldDecl*){0}, Name = '{1}', Type = '{2}', Offset = "
730+
"{3} bits",
731+
*fi, fi->getName(), fi->getType().getAsString(),
732+
field_offsets[*fi]);
733+
}
734+
DeclFromParser<const CXXRecordDecl> parser_cxx_record =
735+
DynCast<const CXXRecordDecl>(parser_record);
736+
if (parser_cxx_record.IsValid()) {
737+
LLDB_LOG(log, "LRT Bases:");
738+
for (CXXRecordDecl::base_class_const_iterator
739+
bi = parser_cxx_record->bases_begin(),
740+
be = parser_cxx_record->bases_end();
741+
bi != be; ++bi) {
742+
bool is_virtual = bi->isVirtual();
743+
744+
QualType base_type = bi->getType();
745+
const RecordType *base_record_type = base_type->getAs<RecordType>();
746+
DeclFromParser<RecordDecl> base_record(base_record_type->getDecl());
747+
DeclFromParser<CXXRecordDecl> base_cxx_record =
748+
DynCast<CXXRecordDecl>(base_record);
749+
750+
LLDB_LOG(log,
751+
"LRT {0}(CXXRecordDecl*){1}, Name = '{2}', Offset = "
752+
"{3} chars",
753+
(is_virtual ? "Virtual " : ""), base_cxx_record.decl,
754+
base_cxx_record.decl->getName(),
755+
(is_virtual
756+
? vbase_offsets[base_cxx_record.decl].getQuantity()
757+
: base_offsets[base_cxx_record.decl].getQuantity()));
758+
}
759+
} else {
760+
LLDB_LOG(log, "LRD Not a CXXRecord, so no bases");
761+
}
762+
}
763+
764+
return true;
765+
}
766+
534767
bool ClangASTImporter::LayoutRecordType(
535768
const clang::RecordDecl *record_decl, uint64_t &bit_size,
536769
uint64_t &alignment,
@@ -544,7 +777,6 @@ bool ClangASTImporter::LayoutRecordType(
544777

545778
RecordDeclToLayoutMap::iterator pos =
546779
m_record_decl_to_layout_map.find(record_decl);
547-
bool success = false;
548780
base_offsets.clear();
549781
vbase_offsets.clear();
550782
if (pos != m_record_decl_to_layout_map.end()) {
@@ -553,13 +785,22 @@ bool ClangASTImporter::LayoutRecordType(
553785
field_offsets.swap(pos->second.field_offsets);
554786
base_offsets.swap(pos->second.base_offsets);
555787
vbase_offsets.swap(pos->second.vbase_offsets);
556-
success = true;
557-
} else {
558-
bit_size = 0;
559-
alignment = 0;
560-
field_offsets.clear();
561788
}
562-
return success;
789+
790+
// It's possible that we calculated the layout in a different
791+
// ClangASTImporter instance. Try to import such layout if
792+
// our decl has an origin.
793+
if (auto origin = GetDeclOrigin(record_decl); origin.Valid())
794+
if (importRecordLayoutFromOrigin(record_decl, bit_size, alignment,
795+
field_offsets, base_offsets,
796+
vbase_offsets))
797+
return true;
798+
799+
bit_size = 0;
800+
alignment = 0;
801+
field_offsets.clear();
802+
803+
return false;
563804
}
564805

565806
void ClangASTImporter::SetRecordLayout(clang::RecordDecl *decl,

lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <set>
1515
#include <vector>
1616

17+
#include "clang/AST/ASTContext.h"
1718
#include "clang/AST/ASTImporter.h"
1819
#include "clang/AST/CharUnits.h"
1920
#include "clang/AST/Decl.h"
@@ -129,6 +130,27 @@ class ClangASTImporter {
129130
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
130131
&vbase_offsets);
131132

133+
/// If \ref record has a valid origin, this function copies that
134+
/// origin's layout into this ClangASTImporter instance.
135+
///
136+
/// \param[in] record The decl whose layout we're calculating.
137+
/// \param[out] size Size of \ref record in bytes.
138+
/// \param[out] alignment Alignment of \ref record in bytes.
139+
/// \param[out] field_offsets Offsets of fields of \ref record.
140+
/// \param[out] base_offsets Offsets of base classes of \ref record.
141+
/// \param[out] vbase_offsets Offsets of virtual base classes of \ref record.
142+
///
143+
/// \returns Returns 'false' if no valid origin was found for \ref record or
144+
/// this function failed to import the layout from the origin. Otherwise,
145+
/// returns 'true' and the offsets/size/alignment are valid for use.
146+
bool importRecordLayoutFromOrigin(
147+
const clang::RecordDecl *record, uint64_t &size, uint64_t &alignment,
148+
llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
149+
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
150+
&base_offsets,
151+
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
152+
&vbase_offsets);
153+
132154
/// Returns true iff the given type was copied from another TypeSystemClang
133155
/// and the original type in this other TypeSystemClang might contain
134156
/// additional information (e.g., the definition of a 'class' type) that could
@@ -449,6 +471,58 @@ class ClangASTImporter {
449471
RecordDeclToLayoutMap m_record_decl_to_layout_map;
450472
};
451473

474+
template <class D> class TaggedASTDecl {
475+
public:
476+
TaggedASTDecl() : decl(nullptr) {}
477+
TaggedASTDecl(D *_decl) : decl(_decl) {}
478+
bool IsValid() const { return (decl != nullptr); }
479+
bool IsInvalid() const { return !IsValid(); }
480+
D *operator->() const { return decl; }
481+
D *decl;
482+
};
483+
484+
template <class D2, template <class D> class TD, class D1>
485+
TD<D2> DynCast(TD<D1> source) {
486+
return TD<D2>(llvm::dyn_cast<D2>(source.decl));
487+
}
488+
489+
template <class D = clang::Decl> class DeclFromParser;
490+
template <class D = clang::Decl> class DeclFromUser;
491+
492+
template <class D> class DeclFromParser : public TaggedASTDecl<D> {
493+
public:
494+
DeclFromParser() : TaggedASTDecl<D>() {}
495+
DeclFromParser(D *_decl) : TaggedASTDecl<D>(_decl) {}
496+
497+
DeclFromUser<D> GetOrigin(ClangASTImporter &importer);
498+
};
499+
500+
template <class D> class DeclFromUser : public TaggedASTDecl<D> {
501+
public:
502+
DeclFromUser() : TaggedASTDecl<D>() {}
503+
DeclFromUser(D *_decl) : TaggedASTDecl<D>(_decl) {}
504+
505+
DeclFromParser<D> Import(clang::ASTContext *dest_ctx,
506+
ClangASTImporter &importer);
507+
};
508+
509+
template <class D>
510+
DeclFromUser<D> DeclFromParser<D>::GetOrigin(ClangASTImporter &importer) {
511+
ClangASTImporter::DeclOrigin origin = importer.GetDeclOrigin(this->decl);
512+
if (!origin.Valid())
513+
return DeclFromUser<D>();
514+
return DeclFromUser<D>(llvm::dyn_cast<D>(origin.decl));
515+
}
516+
517+
template <class D>
518+
DeclFromParser<D> DeclFromUser<D>::Import(clang::ASTContext *dest_ctx,
519+
ClangASTImporter &importer) {
520+
DeclFromParser<> parser_generic_decl(importer.CopyDecl(dest_ctx, this->decl));
521+
if (parser_generic_decl.IsInvalid())
522+
return DeclFromParser<D>();
523+
return DeclFromParser<D>(llvm::dyn_cast<D>(parser_generic_decl.decl));
524+
}
525+
452526
} // namespace lldb_private
453527

454528
#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTIMPORTER_H

0 commit comments

Comments
 (0)