11
11
#include " lldb/Utility/LLDBAssert.h"
12
12
#include " lldb/Utility/LLDBLog.h"
13
13
#include " lldb/Utility/Log.h"
14
+ #include " clang/AST/ASTContext.h"
14
15
#include " clang/AST/Decl.h"
15
16
#include " clang/AST/DeclCXX.h"
16
17
#include " clang/AST/DeclObjC.h"
18
+ #include " clang/AST/RecordLayout.h"
17
19
#include " clang/Sema/Lookup.h"
18
20
#include " clang/Sema/Sema.h"
19
21
#include " llvm/Support/Casting.h"
29
31
30
32
#include < memory>
31
33
#include < optional>
34
+ #include < type_traits>
32
35
33
36
using namespace lldb_private ;
34
37
using namespace clang ;
@@ -531,6 +534,236 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
531
534
return false ;
532
535
}
533
536
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
+
534
767
bool ClangASTImporter::LayoutRecordType (
535
768
const clang::RecordDecl *record_decl, uint64_t &bit_size,
536
769
uint64_t &alignment,
@@ -544,7 +777,6 @@ bool ClangASTImporter::LayoutRecordType(
544
777
545
778
RecordDeclToLayoutMap::iterator pos =
546
779
m_record_decl_to_layout_map.find (record_decl);
547
- bool success = false ;
548
780
base_offsets.clear ();
549
781
vbase_offsets.clear ();
550
782
if (pos != m_record_decl_to_layout_map.end ()) {
@@ -553,13 +785,22 @@ bool ClangASTImporter::LayoutRecordType(
553
785
field_offsets.swap (pos->second .field_offsets );
554
786
base_offsets.swap (pos->second .base_offsets );
555
787
vbase_offsets.swap (pos->second .vbase_offsets );
556
- success = true ;
557
- } else {
558
- bit_size = 0 ;
559
- alignment = 0 ;
560
- field_offsets.clear ();
561
788
}
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 ;
563
804
}
564
805
565
806
void ClangASTImporter::SetRecordLayout (clang::RecordDecl *decl,
0 commit comments