Skip to content

Commit 55bdd99

Browse files
committed
[lldb][NFC] Move helpers to import record layout into ClangASTImporter (llvm#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. (cherry picked from commit 8c10032)
1 parent 5083d68 commit 55bdd99

File tree

4 files changed

+331
-260
lines changed

4 files changed

+331
-260
lines changed

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

Lines changed: 233 additions & 0 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,

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)