Skip to content

Commit 8c10032

Browse files
authored
[lldb][NFC] Move helpers to import record layout into ClangASTImporter (#83291)
This patch moves the logic for copying the layout info of a `RecordDecl`s origin into a target AST. A follow-up patch re-uses the logic from within the `ClangASTImporter`, so the natural choice was to move it there.
1 parent ac26708 commit 8c10032

File tree

4 files changed

+331
-259
lines changed

4 files changed

+331
-259
lines changed

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

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
#include "lldb/Utility/LLDBAssert.h"
1111
#include "lldb/Utility/LLDBLog.h"
1212
#include "lldb/Utility/Log.h"
13+
#include "clang/AST/ASTContext.h"
1314
#include "clang/AST/Decl.h"
1415
#include "clang/AST/DeclCXX.h"
1516
#include "clang/AST/DeclObjC.h"
17+
#include "clang/AST/RecordLayout.h"
1618
#include "clang/Sema/Lookup.h"
1719
#include "clang/Sema/Sema.h"
1820
#include "llvm/Support/raw_ostream.h"
@@ -26,6 +28,7 @@
2628

2729
#include <memory>
2830
#include <optional>
31+
#include <type_traits>
2932

3033
using namespace lldb_private;
3134
using namespace clang;
@@ -517,6 +520,236 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
517520
return false;
518521
}
519522

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

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"
@@ -127,6 +128,27 @@ class ClangASTImporter {
127128
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
128129
&vbase_offsets);
129130

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

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

461535
#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTIMPORTER_H

0 commit comments

Comments
 (0)