/src/mozilla-central/js/src/jit/shared/Disassembler-shared.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | | * vim: set ts=8 sts=4 et sw=4 tw=99: |
3 | | * This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "jit/shared/Disassembler-shared.h" |
8 | | |
9 | | #include "jit/JitSpewer.h" |
10 | | |
11 | | using namespace js::jit; |
12 | | |
13 | | #ifdef JS_DISASM_SUPPORTED |
14 | | // Concurrent assemblers are disambiguated by prefixing every disassembly with a |
15 | | // tag that is quasi-unique, and certainly unique enough in realistic cases |
16 | | // where we are debugging and looking at disassembler output. The tag is a |
17 | | // letter or digit between brackets prefixing the disassembly, eg, [X]. This |
18 | | // wraps around every 62 assemblers. |
19 | | // |
20 | | // When running with --no-threads we can still have concurrent assemblers in the |
21 | | // form of nested assemblers, as when an IC stub is created by one assembler |
22 | | // while a JS compilation is going on and producing output in another assembler. |
23 | | // |
24 | | // We generate the tag for an assembler by incrementing a global mod-2^32 |
25 | | // counter every time a new disassembler is created. |
26 | | |
27 | | mozilla::Atomic<uint32_t> DisassemblerSpew::counter_(0); |
28 | | #endif |
29 | | |
30 | | DisassemblerSpew::DisassemblerSpew() |
31 | | : printer_(nullptr) |
32 | | #ifdef JS_DISASM_SUPPORTED |
33 | | , |
34 | | labelIndent_(""), |
35 | | targetIndent_(""), |
36 | | spewNext_(1000), |
37 | | nodes_(nullptr), |
38 | | tag_(0) |
39 | | #endif |
40 | 0 | { |
41 | | #ifdef JS_DISASM_SUPPORTED |
42 | | tag_ = counter_++; |
43 | | #endif |
44 | | } |
45 | | |
46 | | DisassemblerSpew::~DisassemblerSpew() |
47 | 0 | { |
48 | | #ifdef JS_DISASM_SUPPORTED |
49 | | Node* p = nodes_; |
50 | | while (p) { |
51 | | Node* victim = p; |
52 | | p = p->next; |
53 | | js_free(victim); |
54 | | } |
55 | | #endif |
56 | | } |
57 | | |
58 | | void |
59 | | DisassemblerSpew::setPrinter(Sprinter* printer) |
60 | 0 | { |
61 | 0 | printer_ = printer; |
62 | 0 | } |
63 | | |
64 | | bool |
65 | | DisassemblerSpew::isDisabled() |
66 | 0 | { |
67 | 0 | return !(JitSpewEnabled(JitSpew_Codegen) || printer_); |
68 | 0 | } |
69 | | |
70 | | void |
71 | | DisassemblerSpew::spew(const char* fmt, ...) |
72 | 0 | { |
73 | | #ifdef JS_DISASM_SUPPORTED |
74 | | static const char prefix_chars[] = "0123456789" |
75 | | "abcdefghijklmnopqrstuvwxyz" |
76 | | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
77 | | static const char prefix_fmt[] = "[%c] "; |
78 | | |
79 | | char fmt2[1024]; |
80 | | if (sizeof(fmt2) >= strlen(fmt) + sizeof(prefix_fmt)) { |
81 | | snprintf(fmt2, sizeof(prefix_fmt), prefix_fmt, |
82 | | prefix_chars[tag_ % (sizeof(prefix_chars) - 1)]); |
83 | | strcat(fmt2, fmt); |
84 | | fmt = fmt2; |
85 | | } |
86 | | #endif |
87 | |
|
88 | 0 | va_list args; |
89 | 0 | va_start(args, fmt); |
90 | 0 | spewVA(fmt, args); |
91 | 0 | va_end(args); |
92 | 0 | } |
93 | | |
94 | | void |
95 | | DisassemblerSpew::spewVA(const char* fmt, va_list va) |
96 | 0 | { |
97 | 0 | if (printer_) { |
98 | 0 | printer_->vprintf(fmt, va); |
99 | 0 | printer_->put("\n"); |
100 | 0 | } |
101 | 0 | js::jit::JitSpewVA(js::jit::JitSpew_Codegen, fmt, va); |
102 | 0 | } |
103 | | |
104 | | #ifdef JS_DISASM_SUPPORTED |
105 | | |
106 | | void |
107 | | DisassemblerSpew::setLabelIndent(const char* s) |
108 | | { |
109 | | labelIndent_ = s; |
110 | | } |
111 | | |
112 | | void |
113 | | DisassemblerSpew::setTargetIndent(const char* s) |
114 | | { |
115 | | targetIndent_ = s; |
116 | | } |
117 | | |
118 | | DisassemblerSpew::LabelDoc |
119 | | DisassemblerSpew::refLabel(const Label* l) |
120 | | { |
121 | | return l ? LabelDoc(internalResolve(l), l->bound()) : LabelDoc(); |
122 | | } |
123 | | |
124 | | void |
125 | | DisassemblerSpew::spewRef(const LabelDoc& target) |
126 | | { |
127 | | if (isDisabled()) { |
128 | | return; |
129 | | } |
130 | | if (!target.valid) { |
131 | | return; |
132 | | } |
133 | | spew("%s-> %d%s", targetIndent_, target.doc, !target.bound ? "f" : ""); |
134 | | } |
135 | | |
136 | | void |
137 | | DisassemblerSpew::spewBind(const Label* label) |
138 | | { |
139 | | if (isDisabled()) { |
140 | | return; |
141 | | } |
142 | | uint32_t v = internalResolve(label); |
143 | | Node* probe = lookup(label); |
144 | | if (probe) { |
145 | | probe->bound = true; |
146 | | } |
147 | | spew("%s%d:", labelIndent_, v); |
148 | | } |
149 | | |
150 | | void |
151 | | DisassemblerSpew::spewRetarget(const Label* label, const Label* target) |
152 | | { |
153 | | if (isDisabled()) { |
154 | | return; |
155 | | } |
156 | | LabelDoc labelDoc = LabelDoc(internalResolve(label), label->bound()); |
157 | | LabelDoc targetDoc = LabelDoc(internalResolve(target), target->bound()); |
158 | | Node* probe = lookup(label); |
159 | | if (probe) { |
160 | | probe->bound = true; |
161 | | } |
162 | | spew("%s%d: .retarget -> %d%s", |
163 | | labelIndent_, labelDoc.doc, targetDoc.doc, !targetDoc.bound ? "f" : ""); |
164 | | } |
165 | | |
166 | | void |
167 | | DisassemblerSpew::formatLiteral(const LiteralDoc& doc, char* buffer, size_t bufsize) |
168 | | { |
169 | | switch (doc.type) { |
170 | | case LiteralDoc::Type::Patchable: |
171 | | snprintf(buffer, bufsize, "patchable"); |
172 | | break; |
173 | | case LiteralDoc::Type::I32: |
174 | | snprintf(buffer, bufsize, "%d", doc.value.i32); |
175 | | break; |
176 | | case LiteralDoc::Type::U32: |
177 | | snprintf(buffer, bufsize, "%u", doc.value.u32); |
178 | | break; |
179 | | case LiteralDoc::Type::I64: |
180 | | snprintf(buffer, bufsize, "%" PRIi64, doc.value.i64); |
181 | | break; |
182 | | case LiteralDoc::Type::U64: |
183 | | snprintf(buffer, bufsize, "%" PRIu64, doc.value.u64); |
184 | | break; |
185 | | case LiteralDoc::Type::F32: |
186 | | snprintf(buffer, bufsize, "%g", doc.value.f32); |
187 | | break; |
188 | | case LiteralDoc::Type::F64: |
189 | | snprintf(buffer, bufsize, "%g", doc.value.f64); |
190 | | break; |
191 | | default: |
192 | | MOZ_CRASH(); |
193 | | } |
194 | | } |
195 | | |
196 | | void |
197 | | DisassemblerSpew::spewOrphans() |
198 | | { |
199 | | for (Node* p = nodes_; p; p = p->next) { |
200 | | if (!p->bound) { |
201 | | spew("%s%d: ; .orphan", labelIndent_, p->value); |
202 | | } |
203 | | } |
204 | | } |
205 | | |
206 | | uint32_t |
207 | | DisassemblerSpew::internalResolve(const Label* l) |
208 | | { |
209 | | // Note, internalResolve will sometimes return 0 when it is triggered by the |
210 | | // profiler and not by a full disassembly, since in that case a label can be |
211 | | // used or bound but not previously have been defined. In that case, |
212 | | // internalResolve(l) will not necessarily create a binding for l! |
213 | | // Consequently a subsequent lookup(l) may still return null. |
214 | | return l->used() || l->bound() ? probe(l) : define(l); |
215 | | } |
216 | | |
217 | | uint32_t |
218 | | DisassemblerSpew::probe(const Label* l) |
219 | | { |
220 | | Node* n = lookup(l); |
221 | | return n ? n->value : 0; |
222 | | } |
223 | | |
224 | | uint32_t |
225 | | DisassemblerSpew::define(const Label* l) |
226 | | { |
227 | | remove(l); |
228 | | uint32_t value = spewNext_++; |
229 | | if (!add(l, value)) { |
230 | | return 0; |
231 | | } |
232 | | return value; |
233 | | } |
234 | | |
235 | | DisassemblerSpew::Node* |
236 | | DisassemblerSpew::lookup(const Label* key) |
237 | | { |
238 | | Node* p; |
239 | | for (p = nodes_; p && p->key != key; p = p->next) { |
240 | | ; |
241 | | } |
242 | | return p; |
243 | | } |
244 | | |
245 | | DisassemblerSpew::Node* |
246 | | DisassemblerSpew::add(const Label* key, uint32_t value) |
247 | | { |
248 | | MOZ_ASSERT(!lookup(key)); |
249 | | Node* node = js_new<Node>(); |
250 | | if (node) { |
251 | | node->key = key; |
252 | | node->value = value; |
253 | | node->bound = false; |
254 | | node->next = nodes_; |
255 | | nodes_ = node; |
256 | | } |
257 | | return node; |
258 | | } |
259 | | |
260 | | bool |
261 | | DisassemblerSpew::remove(const Label* key) |
262 | | { |
263 | | // We do not require that there is a node matching the key. |
264 | | for (Node* p = nodes_, *pp = nullptr; p; pp = p, p = p->next) { |
265 | | if (p->key == key) { |
266 | | if (pp) { |
267 | | pp->next = p->next; |
268 | | } else { |
269 | | nodes_ = p->next; |
270 | | } |
271 | | js_free(p); |
272 | | return true; |
273 | | } |
274 | | } |
275 | | return false; |
276 | | } |
277 | | |
278 | | #else |
279 | | |
280 | | DisassemblerSpew::LabelDoc |
281 | | DisassemblerSpew::refLabel(const Label* l) |
282 | 0 | { |
283 | 0 | return LabelDoc(); |
284 | 0 | } |
285 | | |
286 | | #endif |