fa75e88591
Implement type checks for leaf IL instructions using the instruction tag instead of per-type virtual functions, which scale quadratically with the number of IL instructions. out/ReleaseX64/exe.stripped/gen_snapshot 7601248 -> 6890640 (-9.3%) TEST=ci Change-Id: I3d9c84c67e6ab0f3020ebb02068451e6f1c150f7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388043 Reviewed-by: Alexander Aprelev <aam@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
211 lines
7.2 KiB
C++
211 lines
7.2 KiB
C++
// 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_CONCRETE_INSTRUCTION(DO)
|
|
|
|
#undef DO
|
|
|
|
#define DO(type, attrs) \
|
|
entries_[i].name = "SlowPath:" #type; \
|
|
entries_[i].bytes = 0; \
|
|
entries_[i++].count = 0;
|
|
|
|
FOR_EACH_CONCRETE_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, "<checked-entry-prologue>");
|
|
INIT_SPECIAL_ENTRY(kTagIntrinsics, "<intrinsics>");
|
|
#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<float>(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<float>(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<const Entry* const*>(a))->bytes;
|
|
const intptr_t b_size = (*static_cast<const Entry* const*>(b))->bytes;
|
|
if (a_size < b_size) {
|
|
return -1;
|
|
} else if (a_size > b_size) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
CodeStatistics::CodeStatistics(compiler::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<intptr_t>(instruction->statistics_tag()));
|
|
}
|
|
|
|
void CodeStatistics::End(Instruction* instruction) {
|
|
SpecialEnd(static_cast<intptr_t>(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);
|
|
|
|
const intptr_t unaligned_bytes = Instructions::HeaderSize() + function_size;
|
|
alignment_bytes_ =
|
|
Utils::RoundUp(unaligned_bytes, kObjectAlignment) - unaligned_bytes;
|
|
assembler_ = nullptr;
|
|
}
|
|
|
|
void CodeStatistics::AppendTo(CombinedCodeStatistics* stat) {
|
|
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) {
|
|
stat->entries_[i].bytes += bytes;
|
|
if (i != CombinedCodeStatistics::kTagParallelMove &&
|
|
i != CombinedCodeStatistics::kTagDartReturn &&
|
|
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
|