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