1 | // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/compiler/backend/code_statistics.h" |
6 | |
7 | namespace dart { |
8 | |
9 | CombinedCodeStatistics::CombinedCodeStatistics() { |
10 | unaccounted_bytes_ = 0; |
11 | alignment_bytes_ = 0; |
12 | object_header_bytes_ = 0; |
13 | return_const_count_ = 0; |
14 | return_const_with_load_field_count_ = 0; |
15 | intptr_t i = 0; |
16 | |
17 | #define DO(type, attrs) \ |
18 | entries_[i].name = #type; \ |
19 | entries_[i].bytes = 0; \ |
20 | entries_[i++].count = 0; |
21 | |
22 | FOR_EACH_INSTRUCTION(DO) |
23 | |
24 | #undef DO |
25 | |
26 | #define DO(type, attrs) \ |
27 | entries_[i].name = "SlowPath:" #type; \ |
28 | entries_[i].bytes = 0; \ |
29 | entries_[i++].count = 0; |
30 | |
31 | FOR_EACH_INSTRUCTION(DO) |
32 | |
33 | #undef DO |
34 | |
35 | #define INIT_SPECIAL_ENTRY(tag, str) \ |
36 | entries_[tag].name = str; \ |
37 | entries_[tag].bytes = 0; \ |
38 | entries_[tag].count = 0; |
39 | |
40 | INIT_SPECIAL_ENTRY(kTagAssertAssignableParameterCheck, |
41 | "AssertAssignable:ParameterCheck" ); |
42 | INIT_SPECIAL_ENTRY(kTagAssertAssignableInsertedByFrontend, |
43 | "AssertAssignable:InsertedByFrontend" ); |
44 | INIT_SPECIAL_ENTRY(kTagAssertAssignableFromSource, |
45 | "AssertAssignable:FromSource" ); |
46 | |
47 | INIT_SPECIAL_ENTRY(kTagCheckedEntry, "<checked-entry-prologue>" ); |
48 | INIT_SPECIAL_ENTRY(kTagIntrinsics, "<intrinsics>" ); |
49 | #undef INIT_SPECIAL_ENTRY |
50 | } |
51 | |
52 | void CombinedCodeStatistics::DumpStatistics() { |
53 | ASSERT(unaccounted_bytes_ >= 0); |
54 | |
55 | Entry* sorted[kNumEntries]; |
56 | for (intptr_t i = 0; i < kNumEntries; i++) { |
57 | sorted[i] = &entries_[i]; |
58 | } |
59 | qsort(sorted, kNumEntries, sizeof(Entry*), &CompareEntries); |
60 | |
61 | intptr_t instruction_bytes = 0; |
62 | for (intptr_t i = 0; i < kNumEntries; i++) { |
63 | instruction_bytes += entries_[i].bytes; |
64 | } |
65 | intptr_t total = object_header_bytes_ + instruction_bytes + |
66 | unaccounted_bytes_ + alignment_bytes_; |
67 | float ftotal = static_cast<float>(total) / 100.0; |
68 | |
69 | OS::PrintErr("--------------------\n" ); |
70 | |
71 | for (intptr_t i = 0; i < kNumEntries; i++) { |
72 | Entry* entry = sorted[i]; |
73 | const char* name = entry->name; |
74 | intptr_t bytes = entry->bytes; |
75 | intptr_t count = entry->count; |
76 | float percent = bytes / ftotal; |
77 | float avg = static_cast<float>(bytes) / count; |
78 | if (bytes > 0) { |
79 | OS::PrintErr( |
80 | "%5.2f %% " |
81 | "% 8" Pd |
82 | " bytes " |
83 | "% 8" Pd |
84 | " count " |
85 | "%8.2f avg bytes/entry " |
86 | "- %s\n" , |
87 | percent, bytes, count, avg, name); |
88 | } |
89 | } |
90 | |
91 | OS::PrintErr("--------------------\n" ); |
92 | |
93 | OS::PrintErr("%5.2f %% % 8" Pd " bytes unaccounted\n" , |
94 | unaccounted_bytes_ / ftotal, unaccounted_bytes_); |
95 | OS::PrintErr("%5.2f %% % 8" Pd " bytes alignment\n" , |
96 | alignment_bytes_ / ftotal, alignment_bytes_); |
97 | OS::PrintErr("%5.2f %% % 8" Pd " bytes instruction object header\n" , |
98 | object_header_bytes_ / ftotal, object_header_bytes_); |
99 | OS::PrintErr("%5.2f %% % 8" Pd " bytes instructions\n" , |
100 | instruction_bytes / ftotal, instruction_bytes); |
101 | OS::PrintErr("--------------------\n" ); |
102 | OS::PrintErr("%5.2f %% % 8" Pd " bytes in total\n" , total / ftotal, total); |
103 | OS::PrintErr("--------------------\n" ); |
104 | OS::PrintErr("% 8" Pd " return-constant functions\n" , return_const_count_); |
105 | OS::PrintErr("% 8" Pd " return-constant-with-load-field functions\n" , |
106 | return_const_with_load_field_count_); |
107 | OS::PrintErr("--------------------\n" ); |
108 | } |
109 | |
110 | int CombinedCodeStatistics::CompareEntries(const void* a, const void* b) { |
111 | const intptr_t a_size = (*static_cast<const Entry* const*>(a))->bytes; |
112 | const intptr_t b_size = (*static_cast<const Entry* const*>(b))->bytes; |
113 | if (a_size < b_size) { |
114 | return -1; |
115 | } else if (a_size > b_size) { |
116 | return 1; |
117 | } else { |
118 | return 0; |
119 | } |
120 | } |
121 | |
122 | CodeStatistics::CodeStatistics(compiler::Assembler* assembler) |
123 | : assembler_(assembler) { |
124 | memset(entries_, 0, CombinedCodeStatistics::kNumEntries * sizeof(Entry)); |
125 | instruction_bytes_ = 0; |
126 | unaccounted_bytes_ = 0; |
127 | alignment_bytes_ = 0; |
128 | |
129 | stack_index_ = -1; |
130 | for (intptr_t i = 0; i < kStackSize; i++) |
131 | stack_[i] = -1; |
132 | } |
133 | |
134 | void CodeStatistics::Begin(Instruction* instruction) { |
135 | SpecialBegin(static_cast<intptr_t>(instruction->statistics_tag())); |
136 | } |
137 | |
138 | void CodeStatistics::End(Instruction* instruction) { |
139 | SpecialEnd(static_cast<intptr_t>(instruction->statistics_tag())); |
140 | } |
141 | |
142 | void CodeStatistics::SpecialBegin(intptr_t tag) { |
143 | stack_index_++; |
144 | RELEASE_ASSERT(stack_index_ < kStackSize); |
145 | RELEASE_ASSERT(stack_[stack_index_] == -1); |
146 | RELEASE_ASSERT(tag < CombinedCodeStatistics::kNumEntries); |
147 | stack_[stack_index_] = assembler_->CodeSize(); |
148 | RELEASE_ASSERT(stack_[stack_index_] >= 0); |
149 | } |
150 | |
151 | void CodeStatistics::SpecialEnd(intptr_t tag) { |
152 | RELEASE_ASSERT(stack_index_ > 0 || stack_[stack_index_] >= 0); |
153 | RELEASE_ASSERT(tag < CombinedCodeStatistics::kNumEntries); |
154 | |
155 | intptr_t diff = assembler_->CodeSize() - stack_[stack_index_]; |
156 | RELEASE_ASSERT(diff >= 0); |
157 | RELEASE_ASSERT(entries_[tag].bytes >= 0); |
158 | RELEASE_ASSERT(entries_[tag].count >= 0); |
159 | entries_[tag].bytes += diff; |
160 | entries_[tag].count++; |
161 | instruction_bytes_ += diff; |
162 | stack_[stack_index_] = -1; |
163 | stack_index_--; |
164 | } |
165 | |
166 | void CodeStatistics::Finalize() { |
167 | intptr_t function_size = assembler_->CodeSize(); |
168 | unaccounted_bytes_ = function_size - instruction_bytes_; |
169 | ASSERT(unaccounted_bytes_ >= 0); |
170 | |
171 | const intptr_t unaligned_bytes = Instructions::HeaderSize() + function_size; |
172 | alignment_bytes_ = |
173 | Utils::RoundUp(unaligned_bytes, kObjectAlignment) - unaligned_bytes; |
174 | assembler_ = NULL; |
175 | } |
176 | |
177 | void CodeStatistics::AppendTo(CombinedCodeStatistics* stat) { |
178 | intptr_t sum = 0; |
179 | bool returns_constant = true; |
180 | bool returns_const_with_load_field_ = true; |
181 | |
182 | for (intptr_t i = 0; i < CombinedCodeStatistics::kNumEntries; i++) { |
183 | intptr_t bytes = entries_[i].bytes; |
184 | stat->entries_[i].count += entries_[i].count; |
185 | if (bytes > 0) { |
186 | sum += bytes; |
187 | stat->entries_[i].bytes += bytes; |
188 | if (i != CombinedCodeStatistics::kTagParallelMove && |
189 | i != CombinedCodeStatistics::kTagReturn && |
190 | i != CombinedCodeStatistics::kTagCheckStackOverflow && |
191 | i != CombinedCodeStatistics::kTagCheckStackOverflowSlowPath) { |
192 | returns_constant = false; |
193 | if (i != CombinedCodeStatistics::kTagLoadField && |
194 | i != CombinedCodeStatistics::kTagTargetEntry && |
195 | i != CombinedCodeStatistics::kTagJoinEntry) { |
196 | returns_const_with_load_field_ = false; |
197 | } |
198 | } |
199 | } |
200 | } |
201 | stat->unaccounted_bytes_ += unaccounted_bytes_; |
202 | ASSERT(stat->unaccounted_bytes_ >= 0); |
203 | stat->alignment_bytes_ += alignment_bytes_; |
204 | stat->object_header_bytes_ += Instructions::HeaderSize(); |
205 | |
206 | if (returns_constant) stat->return_const_count_++; |
207 | if (returns_const_with_load_field_) { |
208 | stat->return_const_with_load_field_count_++; |
209 | } |
210 | } |
211 | |
212 | } // namespace dart |
213 | |