// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/compiler/backend/code_statistics.h" namespace dart { CombinedCodeStatistics::CombinedCodeStatistics() { unaccounted_bytes_ = 0; alignment_bytes_ = 0; object_header_bytes_ = 0; return_const_count_ = 0; return_const_with_load_field_count_ = 0; intptr_t i = 0; #define DO(type, attrs) \ entries_[i].name = #type; \ entries_[i].bytes = 0; \ entries_[i++].count = 0; FOR_EACH_INSTRUCTION(DO) #undef DO #define DO(type, attrs) \ entries_[i].name = "SlowPath:" #type; \ entries_[i].bytes = 0; \ entries_[i++].count = 0; FOR_EACH_INSTRUCTION(DO) #undef DO #define INIT_SPECIAL_ENTRY(tag, str) \ entries_[tag].name = str; \ entries_[tag].bytes = 0; \ entries_[tag].count = 0; INIT_SPECIAL_ENTRY(kTagAssertAssignableParameterCheck, "AssertAssignable:ParameterCheck"); INIT_SPECIAL_ENTRY(kTagAssertAssignableInsertedByFrontend, "AssertAssignable:InsertedByFrontend"); INIT_SPECIAL_ENTRY(kTagAssertAssignableFromSource, "AssertAssignable:FromSource"); INIT_SPECIAL_ENTRY(kTagCheckedEntry, ""); INIT_SPECIAL_ENTRY(kTagIntrinsics, ""); #undef INIT_SPECIAL_ENTRY } void CombinedCodeStatistics::DumpStatistics() { ASSERT(unaccounted_bytes_ >= 0); Entry* sorted[kNumEntries]; for (intptr_t i = 0; i < kNumEntries; i++) { sorted[i] = &entries_[i]; } qsort(sorted, kNumEntries, sizeof(Entry*), &CompareEntries); intptr_t instruction_bytes = 0; for (intptr_t i = 0; i < kNumEntries; i++) { instruction_bytes += entries_[i].bytes; } intptr_t total = object_header_bytes_ + instruction_bytes + unaccounted_bytes_ + alignment_bytes_; float ftotal = static_cast(total) / 100.0; OS::PrintErr("--------------------\n"); for (intptr_t i = 0; i < kNumEntries; i++) { Entry* entry = sorted[i]; const char* name = entry->name; intptr_t bytes = entry->bytes; intptr_t count = entry->count; float percent = bytes / ftotal; float avg = static_cast(bytes) / count; if (bytes > 0) { OS::PrintErr( "%5.2f %% " "% 8" Pd " bytes " "% 8" Pd " count " "%8.2f avg bytes/entry " "- %s\n", percent, bytes, count, avg, name); } } OS::PrintErr("--------------------\n"); OS::PrintErr("%5.2f %% % 8" Pd " bytes unaccounted\n", unaccounted_bytes_ / ftotal, unaccounted_bytes_); OS::PrintErr("%5.2f %% % 8" Pd " bytes alignment\n", alignment_bytes_ / ftotal, alignment_bytes_); OS::PrintErr("%5.2f %% % 8" Pd " bytes instruction object header\n", object_header_bytes_ / ftotal, object_header_bytes_); OS::PrintErr("%5.2f %% % 8" Pd " bytes instructions\n", instruction_bytes / ftotal, instruction_bytes); OS::PrintErr("--------------------\n"); OS::PrintErr("%5.2f %% % 8" Pd " bytes in total\n", total / ftotal, total); OS::PrintErr("--------------------\n"); OS::PrintErr("% 8" Pd " return-constant functions\n", return_const_count_); OS::PrintErr("% 8" Pd " return-constant-with-load-field functions\n", return_const_with_load_field_count_); OS::PrintErr("--------------------\n"); } int CombinedCodeStatistics::CompareEntries(const void* a, const void* b) { const intptr_t a_size = (*static_cast(a))->bytes; const intptr_t b_size = (*static_cast(b))->bytes; if (a_size < b_size) { return -1; } else if (a_size > b_size) { return 1; } else { return 0; } } CodeStatistics::CodeStatistics(Assembler* assembler) : assembler_(assembler) { memset(entries_, 0, CombinedCodeStatistics::kNumEntries * sizeof(Entry)); instruction_bytes_ = 0; unaccounted_bytes_ = 0; alignment_bytes_ = 0; stack_index_ = -1; for (intptr_t i = 0; i < kStackSize; i++) stack_[i] = -1; } void CodeStatistics::Begin(Instruction* instruction) { SpecialBegin(static_cast(instruction->statistics_tag())); } void CodeStatistics::End(Instruction* instruction) { SpecialEnd(static_cast(instruction->statistics_tag())); } void CodeStatistics::SpecialBegin(intptr_t tag) { stack_index_++; RELEASE_ASSERT(stack_index_ < kStackSize); RELEASE_ASSERT(stack_[stack_index_] == -1); RELEASE_ASSERT(tag < CombinedCodeStatistics::kNumEntries); stack_[stack_index_] = assembler_->CodeSize(); RELEASE_ASSERT(stack_[stack_index_] >= 0); } void CodeStatistics::SpecialEnd(intptr_t tag) { RELEASE_ASSERT(stack_index_ > 0 || stack_[stack_index_] >= 0); RELEASE_ASSERT(tag < CombinedCodeStatistics::kNumEntries); intptr_t diff = assembler_->CodeSize() - stack_[stack_index_]; RELEASE_ASSERT(diff >= 0); RELEASE_ASSERT(entries_[tag].bytes >= 0); RELEASE_ASSERT(entries_[tag].count >= 0); entries_[tag].bytes += diff; entries_[tag].count++; instruction_bytes_ += diff; stack_[stack_index_] = -1; stack_index_--; } void CodeStatistics::Finalize() { intptr_t function_size = assembler_->CodeSize(); unaccounted_bytes_ = function_size - instruction_bytes_; ASSERT(unaccounted_bytes_ >= 0); alignment_bytes_ = Utils::RoundUp(function_size, OS::PreferredCodeAlignment()) - function_size; assembler_ = NULL; } void CodeStatistics::AppendTo(CombinedCodeStatistics* stat) { intptr_t sum = 0; bool returns_constant = true; bool returns_const_with_load_field_ = true; for (intptr_t i = 0; i < CombinedCodeStatistics::kNumEntries; i++) { intptr_t bytes = entries_[i].bytes; stat->entries_[i].count += entries_[i].count; if (bytes > 0) { sum += bytes; stat->entries_[i].bytes += bytes; if (i != CombinedCodeStatistics::kTagParallelMove && i != CombinedCodeStatistics::kTagReturn && i != CombinedCodeStatistics::kTagCheckStackOverflow && i != CombinedCodeStatistics::kTagCheckStackOverflowSlowPath) { returns_constant = false; if (i != CombinedCodeStatistics::kTagLoadField && i != CombinedCodeStatistics::kTagTargetEntry && i != CombinedCodeStatistics::kTagJoinEntry) { returns_const_with_load_field_ = false; } } } } stat->unaccounted_bytes_ += unaccounted_bytes_; ASSERT(stat->unaccounted_bytes_ >= 0); stat->alignment_bytes_ += alignment_bytes_; stat->object_header_bytes_ += Instructions::HeaderSize(); if (returns_constant) stat->return_const_count_++; if (returns_const_with_load_field_) { stat->return_const_with_load_field_count_++; } } } // namespace dart