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