1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "file_item_container.h"
17 #include <cstdint>
18 #include <type_traits>
19 #include "macros.h"
20 #include "file_format_version.h"
21 #include "pgo.h"
22
23 namespace ark::panda_file {
24
25 class ItemDeduper {
26 public:
27 template <class T>
Deduplicate(T * item)28 T *Deduplicate(T *item)
29 {
30 static_assert(std::is_base_of_v<BaseItem, T>);
31
32 if (auto iter = alreadyDedupedItems_.find(item); iter != alreadyDedupedItems_.end()) {
33 ASSERT(item->GetItemType() == iter->second->GetItemType());
34 return static_cast<T *>(iter->second);
35 }
36
37 ItemData itemData(item);
38 auto it = items_.find(itemData);
39 if (it == items_.cend()) {
40 items_.insert(itemData);
41 return item;
42 }
43
44 auto resultItem = it->GetItem();
45 ASSERT(item->GetItemType() == resultItem->GetItemType());
46 auto result = static_cast<T *>(resultItem);
47 if (item != result) {
48 alreadyDedupedItems_.emplace(item, result);
49 if constexpr (!std::is_same_v<T, LineNumberProgramItem>) {
50 item->SetNeedsEmit(false);
51 }
52 }
53
54 return result;
55 }
56
GetUniqueCount() const57 size_t GetUniqueCount() const
58 {
59 return items_.size();
60 }
61
62 private:
63 class ItemWriter : public Writer {
64 public:
ItemWriter(std::vector<uint8_t> * buf,size_t offset)65 ItemWriter(std::vector<uint8_t> *buf, size_t offset) : buf_(buf), offset_(offset) {}
66 ~ItemWriter() override = default;
67
68 NO_COPY_SEMANTIC(ItemWriter);
69 NO_MOVE_SEMANTIC(ItemWriter);
70
WriteByte(uint8_t byte)71 bool WriteByte(uint8_t byte) override
72 {
73 buf_->push_back(byte);
74 ++offset_;
75 return true;
76 }
77
WriteBytes(const std::vector<uint8_t> & bytes)78 bool WriteBytes(const std::vector<uint8_t> &bytes) override
79 {
80 buf_->insert(buf_->end(), bytes.cbegin(), bytes.cend());
81 offset_ += bytes.size();
82 return true;
83 }
84
GetOffset() const85 size_t GetOffset() const override
86 {
87 return offset_;
88 }
89
90 private:
91 std::vector<uint8_t> *buf_;
92 size_t offset_;
93 };
94
95 class ItemData {
96 public:
ItemData(BaseItem * item)97 explicit ItemData(BaseItem *item) : item_(item)
98 {
99 Initialize();
100 }
101
102 ~ItemData() = default;
103
104 DEFAULT_COPY_SEMANTIC(ItemData);
105 NO_MOVE_SEMANTIC(ItemData);
106
GetItem() const107 BaseItem *GetItem() const
108 {
109 return item_;
110 }
111
GetHash() const112 uint32_t GetHash() const
113 {
114 ASSERT(IsInitialized());
115 return hash_;
116 }
117
operator ==(const ItemData & itemData) const118 bool operator==(const ItemData &itemData) const noexcept
119 {
120 ASSERT(IsInitialized());
121 return data_ == itemData.data_;
122 }
123
124 private:
IsInitialized() const125 bool IsInitialized() const
126 {
127 return !data_.empty();
128 }
129
Initialize()130 void Initialize()
131 {
132 ASSERT(item_->NeedsEmit());
133
134 ItemWriter writer(&data_, item_->GetOffset());
135 [[maybe_unused]] auto res = item_->Write(&writer);
136 ASSERT(res);
137 ASSERT(data_.size() == item_->GetSize());
138
139 hash_ = GetHash32(data_.data(), data_.size());
140 }
141
142 BaseItem *item_;
143 uint32_t hash_ {0};
144 std::vector<uint8_t> data_;
145 };
146
147 struct ItemHash {
operator ()ark::panda_file::ItemDeduper::ItemHash148 size_t operator()(const ItemData &itemData) const noexcept
149 {
150 return itemData.GetHash();
151 }
152 };
153
154 std::unordered_set<ItemData, ItemHash> items_;
155 std::unordered_map<BaseItem *, BaseItem *> alreadyDedupedItems_;
156 };
157
158 // NOTE(nsizov): make method for items deletion
159 template <class T, class C, class I, class P, class E, class... Args>
160 // CC-OFFNXT(G.FUN.01-CPP) solid logic
GetOrInsert(C & map,I & items,const P & pos,const E & key,bool isForeign,Args &&...args)161 static T *GetOrInsert(C &map, I &items, const P &pos, const E &key, bool isForeign, Args &&...args)
162 {
163 auto it = map.find(key);
164 if (it != map.cend()) {
165 auto *item = it->second;
166 if (item->IsForeign() == isForeign) {
167 return static_cast<T *>(item);
168 }
169
170 UNREACHABLE();
171 return nullptr;
172 }
173
174 auto ii = items.insert(pos, std::make_unique<T>(std::forward<Args>(args)...));
175 auto *item = static_cast<T *>(ii->get());
176
177 [[maybe_unused]] auto res = map.insert({key, item});
178 ASSERT(res.second);
179 return item;
180 }
181
ItemContainer()182 ItemContainer::ItemContainer()
183 {
184 itemsEnd_ = items_.insert(items_.end(), std::make_unique<EndItem>());
185 annotationItemsEnd_ = items_.insert(items_.end(), std::make_unique<EndItem>());
186 codeItemsEnd_ = items_.insert(items_.end(), std::make_unique<EndItem>());
187 debugItemsEnd_ = items_.insert(items_.end(), std::make_unique<EndItem>());
188 end_ = debugItemsEnd_->get();
189 }
190
GetOrCreateClassItem(const std::string & str)191 ClassItem *ItemContainer::GetOrCreateClassItem(const std::string &str)
192 {
193 return GetOrInsert<ClassItem>(classMap_, items_, itemsEnd_, str, false, str);
194 }
195
GetOrCreateForeignClassItem(const std::string & str)196 ForeignClassItem *ItemContainer::GetOrCreateForeignClassItem(const std::string &str)
197 {
198 return GetOrInsert<ForeignClassItem>(classMap_, foreignItems_, foreignItems_.end(), str, true, str);
199 }
200
GetOrCreateStringItem(const std::string & str)201 StringItem *ItemContainer::GetOrCreateStringItem(const std::string &str)
202 {
203 auto it = classMap_.find(str);
204 if (it != classMap_.cend()) {
205 return it->second->GetNameItem();
206 }
207
208 return GetOrInsert<StringItem>(stringMap_, items_, itemsEnd_, str, false, str);
209 }
210
GetOrCreateLiteralArrayItem(const std::string & id)211 LiteralArrayItem *ItemContainer::GetOrCreateLiteralArrayItem(const std::string &id)
212 {
213 return GetOrInsert<LiteralArrayItem>(literalarrayMap_, items_, itemsEnd_, id, false);
214 }
215
GetOrCreateIntegerValueItem(uint32_t v)216 ScalarValueItem *ItemContainer::GetOrCreateIntegerValueItem(uint32_t v)
217 {
218 return GetOrInsert<ScalarValueItem>(intValueMap_, items_, itemsEnd_, v, false, v);
219 }
220
GetOrCreateLongValueItem(uint64_t v)221 ScalarValueItem *ItemContainer::GetOrCreateLongValueItem(uint64_t v)
222 {
223 return GetOrInsert<ScalarValueItem>(longValueMap_, items_, itemsEnd_, v, false, v);
224 }
225
GetOrCreateFloatValueItem(float v)226 ScalarValueItem *ItemContainer::GetOrCreateFloatValueItem(float v)
227 {
228 return GetOrInsert<ScalarValueItem>(floatValueMap_, items_, itemsEnd_, bit_cast<uint32_t>(v), false, v);
229 }
230
GetOrCreateDoubleValueItem(double v)231 ScalarValueItem *ItemContainer::GetOrCreateDoubleValueItem(double v)
232 {
233 return GetOrInsert<ScalarValueItem>(doubleValueMap_, items_, itemsEnd_, bit_cast<uint64_t>(v), false, v);
234 }
235
GetOrCreateIdValueItem(BaseItem * v)236 ScalarValueItem *ItemContainer::GetOrCreateIdValueItem(BaseItem *v)
237 {
238 return GetOrInsert<ScalarValueItem>(idValueMap_, items_, itemsEnd_, v, false, v);
239 }
240
GetOrCreateProtoItem(TypeItem * retType,const std::vector<MethodParamItem> & params)241 ProtoItem *ItemContainer::GetOrCreateProtoItem(TypeItem *retType, const std::vector<MethodParamItem> ¶ms)
242 {
243 ProtoKey key(retType, params);
244 return GetOrInsert<ProtoItem>(protoMap_, items_, itemsEnd_, key, false, retType, params);
245 }
246
GetOrCreatePrimitiveTypeItem(Type type)247 PrimitiveTypeItem *ItemContainer::GetOrCreatePrimitiveTypeItem(Type type)
248 {
249 return GetOrCreatePrimitiveTypeItem(type.GetId());
250 }
251
GetOrCreatePrimitiveTypeItem(Type::TypeId type)252 PrimitiveTypeItem *ItemContainer::GetOrCreatePrimitiveTypeItem(Type::TypeId type)
253 {
254 return GetOrInsert<PrimitiveTypeItem>(primitiveTypeMap_, items_, itemsEnd_, type, false, type);
255 }
256
CreateLineNumberProgramItem()257 LineNumberProgramItem *ItemContainer::CreateLineNumberProgramItem()
258 {
259 auto it = items_.insert(debugItemsEnd_, std::make_unique<LineNumberProgramItem>());
260 auto *item = static_cast<LineNumberProgramItem *>(it->get());
261 [[maybe_unused]] auto res = lineNumberProgramIndexItem_.Add(item);
262 ASSERT(res);
263 return item;
264 }
265
IncRefLineNumberProgramItem(LineNumberProgramItem * it)266 void ItemContainer::IncRefLineNumberProgramItem(LineNumberProgramItem *it)
267 {
268 lineNumberProgramIndexItem_.IncRefCount(it);
269 }
270
DeduplicateLineNumberProgram(DebugInfoItem * item,ItemDeduper * deduper)271 void ItemContainer::DeduplicateLineNumberProgram(DebugInfoItem *item, ItemDeduper *deduper)
272 {
273 auto *lineNumberProgram = item->GetLineNumberProgram();
274 auto *deduplicated = deduper->Deduplicate(lineNumberProgram);
275 if (deduplicated != lineNumberProgram) {
276 item->SetLineNumberProgram(deduplicated);
277 lineNumberProgramIndexItem_.IncRefCount(deduplicated);
278 lineNumberProgramIndexItem_.DecRefCount(lineNumberProgram);
279 }
280 }
281
DeduplicateDebugInfo(MethodItem * method,ItemDeduper * debugInfoDeduper,ItemDeduper * lineNumberProgramDeduper)282 void ItemContainer::DeduplicateDebugInfo(MethodItem *method, ItemDeduper *debugInfoDeduper,
283 ItemDeduper *lineNumberProgramDeduper)
284 {
285 auto *debugItem = method->GetDebugInfo();
286 if (debugItem == nullptr) {
287 return;
288 }
289
290 DeduplicateLineNumberProgram(debugItem, lineNumberProgramDeduper);
291
292 auto *deduplicated = debugInfoDeduper->Deduplicate(debugItem);
293 if (deduplicated != debugItem) {
294 method->SetDebugInfo(deduplicated);
295 lineNumberProgramIndexItem_.DecRefCount(debugItem->GetLineNumberProgram());
296 }
297 }
298
DeduplicateCode(MethodItem * method,ItemDeduper * codeDeduper)299 static void DeduplicateCode(MethodItem *method, ItemDeduper *codeDeduper)
300 {
301 auto *codeItem = method->GetCode();
302 if (codeItem == nullptr) {
303 return;
304 }
305
306 auto *deduplicated = codeDeduper->Deduplicate(codeItem);
307 if (deduplicated != codeItem) {
308 method->SetCode(deduplicated);
309 deduplicated->AddMethod(method); // we need it for Profile-Guided optimization
310 }
311 }
312
DeduplicateCodeAndDebugInfo()313 void ItemContainer::DeduplicateCodeAndDebugInfo()
314 {
315 ItemDeduper lineNumberProgramDeduper;
316 ItemDeduper debugDeduper;
317 ItemDeduper codeDeduper;
318
319 for (auto &p : classMap_) {
320 auto *item = p.second;
321 if (item->IsForeign()) {
322 continue;
323 }
324
325 auto *classItem = static_cast<ClassItem *>(item);
326
327 classItem->VisitMethods([this, &debugDeduper, &lineNumberProgramDeduper, &codeDeduper](BaseItem *paramItem) {
328 auto *methodItem = static_cast<MethodItem *>(paramItem);
329 DeduplicateDebugInfo(methodItem, &debugDeduper, &lineNumberProgramDeduper);
330 DeduplicateCode(methodItem, &codeDeduper);
331 return true;
332 });
333 }
334 }
335
DeduplicateAnnotationValue(AnnotationItem * annotationItem,ItemDeduper * deduper)336 static void DeduplicateAnnotationValue(AnnotationItem *annotationItem, ItemDeduper *deduper)
337 {
338 auto *elems = annotationItem->GetElements();
339 const auto &tags = annotationItem->GetTags();
340
341 for (size_t i = 0; i < elems->size(); i++) {
342 auto tag = tags[i];
343
344 // try to dedupe only ArrayValueItems
345 switch (tag.GetItem()) {
346 case 'K':
347 case 'L':
348 case 'M':
349 case 'N':
350 case 'O':
351 case 'P':
352 case 'Q':
353 case 'R':
354 case 'S':
355 case 'T':
356 case 'U':
357 case 'V':
358 case 'W':
359 case 'X':
360 case 'Y':
361 case 'Z':
362 case '@':
363 break;
364 default:
365 continue;
366 }
367
368 auto &elem = (*elems)[i];
369 auto *value = elem.GetValue();
370 auto *deduplicated = deduper->Deduplicate(value);
371 if (deduplicated != value) {
372 elem.SetValue(deduplicated);
373 }
374 }
375 }
376
DeduplicateAnnotations(std::vector<AnnotationItem * > * items,ItemDeduper * annotationDeduper,ItemDeduper * valueDeduper)377 static void DeduplicateAnnotations(std::vector<AnnotationItem *> *items, ItemDeduper *annotationDeduper,
378 ItemDeduper *valueDeduper)
379 {
380 for (auto &item : *items) {
381 DeduplicateAnnotationValue(item, valueDeduper);
382 auto *deduplicated = annotationDeduper->Deduplicate(item);
383 if (deduplicated != item) {
384 item = deduplicated;
385 }
386 }
387 }
388
DeduplicateAnnotations()389 void ItemContainer::DeduplicateAnnotations()
390 {
391 ItemDeduper valueDeduper;
392 ItemDeduper annotationDeduper;
393
394 for (auto &p : classMap_) {
395 auto *item = p.second;
396 if (item->IsForeign()) {
397 continue;
398 }
399
400 auto *classItem = static_cast<ClassItem *>(item);
401
402 panda_file::DeduplicateAnnotations(classItem->GetRuntimeAnnotations(), &annotationDeduper, &valueDeduper);
403 panda_file::DeduplicateAnnotations(classItem->GetAnnotations(), &annotationDeduper, &valueDeduper);
404 panda_file::DeduplicateAnnotations(classItem->GetRuntimeTypeAnnotations(), &annotationDeduper, &valueDeduper);
405 panda_file::DeduplicateAnnotations(classItem->GetTypeAnnotations(), &annotationDeduper, &valueDeduper);
406
407 classItem->VisitMethods([&annotationDeduper, &valueDeduper](BaseItem *paramItem) {
408 auto *methodItem = static_cast<MethodItem *>(paramItem);
409 panda_file::DeduplicateAnnotations(methodItem->GetRuntimeAnnotations(), &annotationDeduper, &valueDeduper);
410 panda_file::DeduplicateAnnotations(methodItem->GetAnnotations(), &annotationDeduper, &valueDeduper);
411 panda_file::DeduplicateAnnotations(methodItem->GetRuntimeTypeAnnotations(), &annotationDeduper,
412 &valueDeduper);
413 panda_file::DeduplicateAnnotations(methodItem->GetTypeAnnotations(), &annotationDeduper, &valueDeduper);
414 return true;
415 });
416
417 classItem->VisitFields([&annotationDeduper, &valueDeduper](BaseItem *paramItem) {
418 auto *fieldItem = static_cast<FieldItem *>(paramItem);
419 panda_file::DeduplicateAnnotations(fieldItem->GetRuntimeAnnotations(), &annotationDeduper, &valueDeduper);
420 panda_file::DeduplicateAnnotations(fieldItem->GetAnnotations(), &annotationDeduper, &valueDeduper);
421 panda_file::DeduplicateAnnotations(fieldItem->GetRuntimeTypeAnnotations(), &annotationDeduper,
422 &valueDeduper);
423 panda_file::DeduplicateAnnotations(fieldItem->GetTypeAnnotations(), &annotationDeduper, &valueDeduper);
424 return true;
425 });
426 }
427 }
428
DeduplicateItems(bool computeLayout)429 void ItemContainer::DeduplicateItems(bool computeLayout)
430 {
431 if (computeLayout) {
432 ComputeLayout();
433 }
434 DeduplicateCodeAndDebugInfo();
435 DeduplicateAnnotations();
436 }
437
MarkLiteralarrayMap()438 void ItemContainer::MarkLiteralarrayMap()
439 {
440 for (auto &entry : literalarrayMap_) {
441 entry.second->SetDependencyMark();
442 }
443 }
444
CleanupArrayValueItems(ValueItem * value)445 void ItemContainer::CleanupArrayValueItems(ValueItem *value)
446 {
447 auto arrayValue = value->GetAsArray();
448 auto *vecItems = arrayValue->GetMutableItems();
449 auto newEnd = std::partition(vecItems->begin(), vecItems->end(), [](const ScalarValueItem &valueItem) {
450 if (valueItem.GetType() == ValueItem::Type::ID) {
451 auto realitem = valueItem.GetIdItem();
452 return realitem->GetDependencyMark();
453 }
454 return true;
455 });
456 vecItems->erase(newEnd, vecItems->end());
457 }
458
DeleteReferenceFromAnno(AnnotationItem * annoItem)459 void ItemContainer::DeleteReferenceFromAnno(AnnotationItem *annoItem)
460 {
461 auto &elements = *annoItem->GetElements();
462 for (auto element = elements.begin(); element != elements.end();) {
463 bool nodelete = true;
464 auto *value = element->GetValue();
465 if (value->GetType() == ValueItem::Type::ARRAY) {
466 CleanupArrayValueItems(value);
467 } else if (value->GetType() == ValueItem::Type::ID) {
468 auto scalarValue = value->GetAsScalar();
469 nodelete = scalarValue->GetIdItem()->GetDependencyMark();
470 }
471 if (nodelete) {
472 ++element;
473 } else {
474 element = elements.erase(element);
475 }
476 }
477 }
478
DeleteItems()479 uint32_t ItemContainer::DeleteItems()
480 {
481 // ANNOTATION_ITEM reference other item by
482 // AnnotationItem->std::vector<Elem>
483 // Elem->ValueItem
484 // ValueItem->BaseItem if ValueItem::Type == Type::ID
485 // BaseItem should be remove from AnnotationItem before delete
486 auto shouldRemoveAnnotationItem = [this](const std::unique_ptr<BaseItem> &item) -> bool {
487 if (item->GetItemType() != ItemTypes::ANNOTATION_ITEM) {
488 return false;
489 }
490 auto annoItem = static_cast<AnnotationItem *>(item.get());
491 DeleteReferenceFromAnno(annoItem);
492 return !item->GetDependencyMark() && item->NeedsEmit();
493 };
494 items_.remove_if(shouldRemoveAnnotationItem);
495 items_.remove_if([this](const std::unique_ptr<BaseItem> &item) {
496 if (!item->GetDependencyMark()) {
497 // lineNumberProgramIndexItem_ reference LineNumberProgramItem.
498 // remove from lnp before item deleted if item not necessary.
499 if (item->GetItemType() == ItemTypes::LINE_NUMBER_PROGRAM_ITEM) {
500 auto lnpitem = static_cast<LineNumberProgramItem *>(item.get());
501 lineNumberProgramIndexItem_.Remove(lnpitem);
502 }
503 }
504 return !item->GetDependencyMark() && item->NeedsEmit();
505 });
506 return 0;
507 }
508
DeleteForeignItems()509 uint32_t ItemContainer::DeleteForeignItems()
510 {
511 auto newEnd = std::partition(foreignItems_.begin(), foreignItems_.end(), [](const std::unique_ptr<BaseItem> &item) {
512 return item->GetDependencyMark() || !item->NeedsEmit();
513 });
514 foreignItems_.erase(newEnd, foreignItems_.end());
515 return 0;
516 }
517
ComputeLayout()518 uint32_t ItemContainer::ComputeLayout()
519 {
520 uint32_t numClasses = classMap_.size();
521 uint32_t numLiteralarrays = literalarrayMap_.size();
522 uint32_t classIdxOffset = sizeof(File::Header);
523 uint32_t curOffset = classIdxOffset + (numClasses + numLiteralarrays) * ID_SIZE;
524
525 UpdateOrderIndexes();
526 UpdateLiteralIndexes();
527
528 RebuildRegionSection();
529 RebuildLineNumberProgramIndex();
530
531 regionSectionItem_.SetOffset(curOffset);
532 regionSectionItem_.ComputeLayout();
533 curOffset += regionSectionItem_.GetSize();
534
535 for (auto &item : foreignItems_) {
536 curOffset = RoundUp(curOffset, item->Alignment());
537 item->SetOffset(curOffset);
538 item->ComputeLayout();
539 curOffset += item->GetSize();
540 }
541
542 for (auto &item : items_) {
543 if (!item->NeedsEmit()) {
544 continue;
545 }
546
547 curOffset = RoundUp(curOffset, item->Alignment());
548 item->SetOffset(curOffset);
549 item->ComputeLayout();
550 curOffset += item->GetSize();
551 }
552
553 // Line number program should be last because it's size is known only after deduplication
554 curOffset = RoundUp(curOffset, lineNumberProgramIndexItem_.Alignment());
555 lineNumberProgramIndexItem_.SetOffset(curOffset);
556 lineNumberProgramIndexItem_.ComputeLayout();
557 curOffset += lineNumberProgramIndexItem_.GetSize();
558
559 end_->SetOffset(curOffset);
560
561 return curOffset;
562 }
563
RebuildLineNumberProgramIndex()564 void ItemContainer::RebuildLineNumberProgramIndex()
565 {
566 lineNumberProgramIndexItem_.Reset();
567 lineNumberProgramIndexItem_.UpdateItems(nullptr, nullptr);
568 }
569
RebuildRegionSection()570 void ItemContainer::RebuildRegionSection()
571 {
572 regionSectionItem_.Reset();
573
574 for (auto &item : foreignItems_) {
575 ProcessIndexDependecies(item.get());
576 }
577
578 for (auto &item : items_) {
579 if (!item->NeedsEmit()) {
580 continue;
581 }
582
583 ProcessIndexDependecies(item.get());
584 }
585
586 if (!regionSectionItem_.IsEmpty()) {
587 regionSectionItem_.GetCurrentHeader()->SetEnd(end_);
588 }
589
590 regionSectionItem_.UpdateItems();
591 }
592
UpdateOrderIndexes()593 void ItemContainer::UpdateOrderIndexes()
594 {
595 size_t idx = 0;
596
597 for (auto &item : foreignItems_) {
598 item->SetOrderIndex(idx++);
599 item->Visit([&idx](BaseItem *paramItem) {
600 paramItem->SetOrderIndex(idx++);
601 return true;
602 });
603 }
604
605 for (auto &item : items_) {
606 if (!item->NeedsEmit()) {
607 continue;
608 }
609
610 item->SetOrderIndex(idx++);
611 item->Visit([&idx](BaseItem *paramItem) {
612 paramItem->SetOrderIndex(idx++);
613 return true;
614 });
615 }
616
617 end_->SetOrderIndex(idx++);
618 }
619
UpdateLiteralIndexes()620 void ItemContainer::UpdateLiteralIndexes()
621 {
622 size_t idx = 0;
623
624 for (auto &it : literalarrayMap_) {
625 it.second->SetIndex(idx++);
626 }
627 }
628
ReorderItems(ark::panda_file::pgo::ProfileOptimizer * profileOpt)629 void ItemContainer::ReorderItems(ark::panda_file::pgo::ProfileOptimizer *profileOpt)
630 {
631 profileOpt->ProfileGuidedRelayout(items_);
632 }
633
ProcessIndexDependecies(BaseItem * item)634 void ItemContainer::ProcessIndexDependecies(BaseItem *item)
635 {
636 auto deps = item->GetIndexDependencies();
637
638 item->Visit([&deps](BaseItem *paramItem) {
639 const auto &itemDeps = paramItem->GetIndexDependencies();
640 deps.insert(deps.end(), itemDeps.cbegin(), itemDeps.cend());
641 return true;
642 });
643
644 if (regionSectionItem_.IsEmpty()) {
645 regionSectionItem_.AddHeader();
646 regionSectionItem_.GetCurrentHeader()->SetStart(item);
647 }
648
649 if (regionSectionItem_.GetCurrentHeader()->Add(deps)) {
650 return;
651 }
652
653 regionSectionItem_.GetCurrentHeader()->SetEnd(item);
654 regionSectionItem_.AddHeader();
655 regionSectionItem_.GetCurrentHeader()->SetStart(item);
656
657 if (!regionSectionItem_.GetCurrentHeader()->Add(deps)) {
658 LOG(FATAL, PANDAFILE) << "Cannot add " << deps.size() << " items to index";
659 }
660 }
661
WriteHeaderIndexInfo(Writer * writer)662 bool ItemContainer::WriteHeaderIndexInfo(Writer *writer)
663 {
664 if (!writer->Write<uint32_t>(classMap_.size())) {
665 return false;
666 }
667
668 if (!writer->Write<uint32_t>(sizeof(File::Header))) {
669 return false;
670 }
671
672 if (!writer->Write<uint32_t>(lineNumberProgramIndexItem_.GetNumItems())) {
673 return false;
674 }
675
676 if (!writer->Write<uint32_t>(lineNumberProgramIndexItem_.GetOffset())) {
677 return false;
678 }
679
680 if (!writer->Write<uint32_t>(literalarrayMap_.size())) {
681 return false;
682 }
683
684 uint32_t literalarrayIdxOffset = sizeof(File::Header) + classMap_.size() * ID_SIZE;
685 if (!writer->Write<uint32_t>(literalarrayIdxOffset)) {
686 return false;
687 }
688
689 if (!writer->Write<uint32_t>(regionSectionItem_.GetNumHeaders())) {
690 return false;
691 }
692
693 size_t indexSectionOff = literalarrayIdxOffset + literalarrayMap_.size() * ID_SIZE;
694 return writer->Write<uint32_t>(indexSectionOff);
695 }
696
WriteHeader(Writer * writer,ssize_t * checksumOffset)697 bool ItemContainer::WriteHeader(Writer *writer, ssize_t *checksumOffset)
698 {
699 uint32_t fileSize = ComputeLayout();
700
701 std::vector<uint8_t> magic;
702 magic.assign(File::MAGIC.cbegin(), File::MAGIC.cend());
703 if (!writer->WriteBytes(magic)) {
704 return false;
705 }
706
707 *checksumOffset = static_cast<ssize_t>(writer->GetOffset());
708 uint32_t checksum = 0;
709 if (!writer->Write(checksum)) {
710 return false;
711 }
712 writer->CountChecksum(true);
713
714 std::vector<uint8_t> versionVec(std::begin(VERSION), std::end(VERSION));
715 if (!writer->WriteBytes(versionVec)) {
716 return false;
717 }
718
719 if (!writer->Write(fileSize)) {
720 return false;
721 }
722
723 uint32_t foreignOffset = GetForeignOffset();
724 if (!writer->Write(foreignOffset)) {
725 return false;
726 }
727
728 uint32_t foreignSize = GetForeignSize();
729 if (!writer->Write(foreignSize)) {
730 return false;
731 }
732
733 if (!writer->Write<uint32_t>(static_cast<uint32_t>(isQuickened_))) {
734 return false;
735 }
736
737 return WriteHeaderIndexInfo(writer);
738 }
739
Write(Writer * writer,bool deduplicateItems,bool computeLayout)740 bool ItemContainer::Write(Writer *writer, bool deduplicateItems, bool computeLayout)
741 {
742 if (deduplicateItems) {
743 DeduplicateItems(computeLayout);
744 }
745
746 ssize_t checksumOffset = -1;
747 if (!WriteHeader(writer, &checksumOffset)) {
748 return false;
749 }
750 ASSERT(checksumOffset != -1);
751
752 // Write class idx
753
754 for (auto &entry : classMap_) {
755 if (!writer->Write(entry.second->GetOffset())) {
756 return false;
757 }
758 }
759
760 // Write literalArray idx
761
762 for (auto &entry : literalarrayMap_) {
763 if (!writer->Write(entry.second->GetOffset())) {
764 return false;
765 }
766 }
767
768 // Write index section
769
770 if (!regionSectionItem_.Write(writer)) {
771 return false;
772 }
773
774 for (auto &item : foreignItems_) {
775 if (!writer->Align(item->Alignment()) || !item->Write(writer)) {
776 return false;
777 }
778 }
779
780 for (auto &item : items_) {
781 if (!item->NeedsEmit()) {
782 continue;
783 }
784
785 if (!writer->Align(item->Alignment()) || !item->Write(writer)) {
786 return false;
787 }
788 }
789
790 if (!writer->Align(lineNumberProgramIndexItem_.Alignment())) {
791 return false;
792 }
793
794 // Write line number program idx
795
796 if (!lineNumberProgramIndexItem_.Write(writer)) {
797 return false;
798 }
799
800 writer->CountChecksum(false);
801 writer->WriteChecksum(checksumOffset);
802
803 return true;
804 }
805
GetStat()806 std::map<std::string, size_t> ItemContainer::GetStat()
807 {
808 std::map<std::string, size_t> stat;
809
810 DeduplicateItems();
811 ComputeLayout();
812
813 stat["header_item"] = sizeof(File::Header);
814 stat["class_idx_item"] = classMap_.size() * ID_SIZE;
815 stat["line_number_program_idx_item"] = lineNumberProgramIndexItem_.GetNumItems() * ID_SIZE;
816 stat["literalarray_idx"] = literalarrayMap_.size() * ID_SIZE;
817
818 stat["region_section_item"] = regionSectionItem_.GetSize();
819 stat["foreign_item"] = GetForeignSize();
820
821 size_t numIns = 0;
822 size_t codesize = 0;
823 for (auto &item : items_) {
824 if (!item->NeedsEmit()) {
825 continue;
826 }
827
828 const auto &name = item->GetName();
829 size_t size = item->GetSize();
830 auto it = stat.find(name);
831 if (it != stat.cend()) {
832 stat[name] += size;
833 } else if (size != 0) {
834 stat[name] = size;
835 }
836 if (name == "code_item") {
837 numIns += static_cast<CodeItem *>(item.get())->GetNumInstructions();
838 codesize += static_cast<CodeItem *>(item.get())->GetCodeSize();
839 }
840 }
841 stat["instructions_number"] = numIns;
842 stat["codesize"] = codesize;
843
844 return stat;
845 }
846
DumpItemsStat(std::ostream & os) const847 void ItemContainer::DumpItemsStat(std::ostream &os) const
848 {
849 struct Stat {
850 size_t n;
851 size_t totalSize;
852 };
853
854 std::map<std::string, Stat> stat;
855
856 auto collectStat = [&stat](auto &items) {
857 for (auto &item : items) {
858 if (!item->NeedsEmit()) {
859 continue;
860 }
861
862 const auto &name = item->GetName();
863 size_t size = item->GetSize();
864 auto it = stat.find(name);
865 if (it != stat.cend()) {
866 stat[name].n += 1;
867 stat[name].totalSize += size;
868 } else if (size != 0) {
869 stat[name] = {1, size};
870 }
871 }
872 };
873
874 collectStat(foreignItems_);
875 collectStat(items_);
876
877 for (auto &[name, elem] : stat) {
878 os << name << ":" << std::endl;
879 os << " n = " << elem.n << std::endl;
880 os << " total size = " << elem.totalSize << std::endl;
881 }
882 }
883
GetForeignOffset() const884 size_t ItemContainer::GetForeignOffset() const
885 {
886 if (foreignItems_.empty()) {
887 return 0;
888 }
889
890 return foreignItems_.front()->GetOffset();
891 }
892
GetForeignSize() const893 size_t ItemContainer::GetForeignSize() const
894 {
895 if (foreignItems_.empty()) {
896 return 0;
897 }
898
899 size_t begin = foreignItems_.front()->GetOffset();
900 size_t end = foreignItems_.back()->GetOffset() + foreignItems_.back()->GetSize();
901
902 return end - begin;
903 }
904
Write(Writer * writer)905 bool ItemContainer::RegionHeaderItem::Write(Writer *writer)
906 {
907 ASSERT(GetOffset() == writer->GetOffset());
908 ASSERT(start_ != nullptr);
909 ASSERT(start_->GetOffset() != 0);
910 ASSERT(end_ != nullptr);
911 ASSERT(end_->GetOffset() != 0);
912
913 if (!writer->Write<uint32_t>(start_->GetOffset())) {
914 return false;
915 }
916
917 if (!writer->Write<uint32_t>(end_->GetOffset())) {
918 return false;
919 }
920
921 for (auto *indexItem : indexes_) {
922 if (!writer->Write<uint32_t>(indexItem->GetNumItems())) {
923 return false;
924 }
925
926 ASSERT(indexItem->GetOffset() != 0);
927 if (!writer->Write<uint32_t>(indexItem->GetOffset())) {
928 return false;
929 }
930 }
931
932 return true;
933 }
934
Add(const std::list<IndexedItem * > & items)935 bool ItemContainer::RegionHeaderItem::Add(const std::list<IndexedItem *> &items)
936 {
937 std::list<IndexedItem *> addedItems;
938
939 for (auto *item : items) {
940 auto type = item->GetIndexType();
941 ASSERT(type != IndexType::NONE);
942
943 auto *indexItem = GetIndexByType(type);
944
945 if (indexItem->Has(item)) {
946 continue;
947 }
948
949 if (!indexItem->Add(item)) {
950 Remove(addedItems);
951 return false;
952 }
953
954 addedItems.push_back(item);
955 }
956
957 return true;
958 }
959
Remove(const std::list<IndexedItem * > & items)960 void ItemContainer::RegionHeaderItem::Remove(const std::list<IndexedItem *> &items)
961 {
962 for (auto *item : items) {
963 auto type = item->GetIndexType();
964 ASSERT(type != IndexType::NONE);
965
966 auto *indexItem = GetIndexByType(type);
967 indexItem->Remove(item);
968 }
969 }
970
Write(Writer * writer)971 bool ItemContainer::IndexItem::Write(Writer *writer)
972 {
973 ASSERT(GetOffset() == writer->GetOffset());
974
975 for (auto *item : index_) {
976 if (!writer->Write<uint32_t>(item->GetOffset())) {
977 return false;
978 }
979 }
980
981 return true;
982 }
983
GetItemType() const984 ItemTypes ItemContainer::IndexItem::GetItemType() const
985 {
986 switch (type_) {
987 case IndexType::CLASS:
988 return ItemTypes::CLASS_INDEX_ITEM;
989 case IndexType::METHOD:
990 return ItemTypes::METHOD_INDEX_ITEM;
991 case IndexType::FIELD:
992 return ItemTypes::FIELD_INDEX_ITEM;
993 case IndexType::PROTO:
994 return ItemTypes::PROTO_INDEX_ITEM;
995 case IndexType::LINE_NUMBER_PROG:
996 return ItemTypes::LINE_NUMBER_PROGRAM_INDEX_ITEM;
997 default:
998 break;
999 }
1000
1001 UNREACHABLE();
1002 }
1003
Add(IndexedItem * item)1004 bool ItemContainer::IndexItem::Add(IndexedItem *item)
1005 {
1006 auto size = index_.size();
1007 ASSERT(size <= maxIndex_);
1008
1009 if (size == maxIndex_) {
1010 return false;
1011 }
1012
1013 auto res = index_.insert(item);
1014 ASSERT(res.second);
1015
1016 return res.second;
1017 }
1018
AddHeader()1019 void ItemContainer::RegionSectionItem::AddHeader()
1020 {
1021 std::vector<IndexItem *> indexItems;
1022 for (size_t i = 0; i < INDEX_COUNT_16; i++) {
1023 auto type = static_cast<IndexType>(i);
1024 indexes_.emplace_back(type, MAX_INDEX_16);
1025 indexItems.push_back(&indexes_.back());
1026 }
1027 headers_.emplace_back(indexItems);
1028 }
1029
CalculateSize() const1030 size_t ItemContainer::RegionSectionItem::CalculateSize() const
1031 {
1032 size_t size = headers_.size() * sizeof(File::RegionHeader);
1033 for (auto &indexItem : indexes_) {
1034 size += indexItem.GetSize();
1035 }
1036 return size;
1037 }
1038
ComputeLayout()1039 void ItemContainer::RegionSectionItem::ComputeLayout()
1040 {
1041 size_t offset = GetOffset();
1042
1043 for (auto &header : headers_) {
1044 header.SetOffset(offset);
1045 header.ComputeLayout();
1046 offset += header.GetSize();
1047 }
1048
1049 for (auto &index : indexes_) {
1050 index.SetOffset(offset);
1051 index.ComputeLayout();
1052 offset += index.GetSize();
1053 }
1054 }
1055
Write(Writer * writer)1056 bool ItemContainer::RegionSectionItem::Write(Writer *writer)
1057 {
1058 ASSERT(GetOffset() == writer->GetOffset());
1059
1060 for (auto &header : headers_) {
1061 if (!header.Write(writer)) {
1062 return false;
1063 }
1064 }
1065
1066 for (auto &index : indexes_) {
1067 if (!index.Write(writer)) {
1068 return false;
1069 }
1070 }
1071
1072 return true;
1073 }
1074
ProtoKey(TypeItem * retType,const std::vector<MethodParamItem> & params)1075 ItemContainer::ProtoKey::ProtoKey(TypeItem *retType, const std::vector<MethodParamItem> ¶ms)
1076 {
1077 Add(retType);
1078 for (const auto ¶m : params) {
1079 Add(param.GetType());
1080 }
1081 size_t shortyHash = std::hash<std::string>()(shorty_);
1082 size_t retTypeHash = std::hash<TypeItem *>()(retType);
1083 // combine hashes of shorty and ref_types
1084 hash_ = ark::MergeHashes(shortyHash, retTypeHash);
1085 // combine hashes of all param types
1086 for (const auto &item : params) {
1087 size_t paramTypeHash = std::hash<TypeItem *>()(item.GetType());
1088 hash_ = ark::MergeHashes(hash_, paramTypeHash);
1089 }
1090 }
1091
Add(TypeItem * item)1092 void ItemContainer::ProtoKey::Add(TypeItem *item)
1093 {
1094 auto type = item->GetType();
1095 shorty_.append(Type::GetSignatureByTypeId(type));
1096 if (type.IsReference()) {
1097 refTypes_.push_back(item);
1098 }
1099 }
1100
1101 } // namespace ark::panda_file
1102