summaryrefslogtreecommitdiffstats
path: root/js/src/jit/shared/Disassembler-shared.cpp
blob: 11d062dd53e345076c5af92427de82cedf3d0093 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "jit/shared/Disassembler-shared.h"

#include "jit/JitSpewer.h"
#include "jit/Label.h"
#include "vm/Printer.h"

using namespace js::jit;

using js::Sprinter;

#ifdef JS_DISASM_SUPPORTED
// Concurrent assemblers are disambiguated by prefixing every disassembly with a
// tag that is quasi-unique, and certainly unique enough in realistic cases
// where we are debugging and looking at disassembler output.  The tag is a
// letter or digit between brackets prefixing the disassembly, eg, [X]. This
// wraps around every 62 assemblers.
//
// When running with --no-threads we can still have concurrent assemblers in the
// form of nested assemblers, as when an IC stub is created by one assembler
// while a JS compilation is going on and producing output in another assembler.
//
// We generate the tag for an assembler by incrementing a global mod-2^32
// counter every time a new disassembler is created.

mozilla::Atomic<uint32_t> DisassemblerSpew::counter_(0);
#endif

DisassemblerSpew::DisassemblerSpew()
    : printer_(nullptr)
#ifdef JS_DISASM_SUPPORTED
      ,
      labelIndent_(""),
      targetIndent_(""),
      spewNext_(1000),
      nodes_(nullptr),
      tag_(0)
#endif
{
#ifdef JS_DISASM_SUPPORTED
  tag_ = counter_++;
#endif
}

DisassemblerSpew::~DisassemblerSpew() {
#ifdef JS_DISASM_SUPPORTED
  Node* p = nodes_;
  while (p) {
    Node* victim = p;
    p = p->next;
    js_free(victim);
  }
#endif
}

void DisassemblerSpew::setPrinter(Sprinter* printer) { printer_ = printer; }

bool DisassemblerSpew::isDisabled() {
  return !(JitSpewEnabled(JitSpew_Codegen) || printer_);
}

void DisassemblerSpew::spew(const char* fmt, ...) {
#ifdef JS_DISASM_SUPPORTED
  static const char prefix_chars[] =
      "0123456789"
      "abcdefghijklmnopqrstuvwxyz"
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  static const char prefix_fmt[] = "[%c] ";

  char fmt2[1024];
  if (sizeof(fmt2) >= strlen(fmt) + sizeof(prefix_fmt)) {
    snprintf(fmt2, sizeof(prefix_fmt), prefix_fmt,
             prefix_chars[tag_ % (sizeof(prefix_chars) - 1)]);
    strcat(fmt2, fmt);
    fmt = fmt2;
  }
#endif

  va_list args;
  va_start(args, fmt);
  spewVA(fmt, args);
  va_end(args);
}

void DisassemblerSpew::spewVA(const char* fmt, va_list va) {
  if (printer_) {
    printer_->vprintf(fmt, va);
    printer_->put("\n");
  }
  js::jit::JitSpewVA(js::jit::JitSpew_Codegen, fmt, va);
}

#ifdef JS_DISASM_SUPPORTED

void DisassemblerSpew::setLabelIndent(const char* s) { labelIndent_ = s; }

void DisassemblerSpew::setTargetIndent(const char* s) { targetIndent_ = s; }

DisassemblerSpew::LabelDoc DisassemblerSpew::refLabel(const Label* l) {
  return l ? LabelDoc(internalResolve(l), l->bound()) : LabelDoc();
}

void DisassemblerSpew::spewRef(const LabelDoc& target) {
  if (isDisabled()) {
    return;
  }
  if (!target.valid) {
    return;
  }
  spew("%s-> %d%s", targetIndent_, target.doc, !target.bound ? "f" : "");
}

void DisassemblerSpew::spewBind(const Label* label) {
  if (isDisabled()) {
    return;
  }
  uint32_t v = internalResolve(label);
  Node* probe = lookup(label);
  if (probe) {
    probe->bound = true;
  }
  spew("%s%d:", labelIndent_, v);
}

void DisassemblerSpew::spewRetarget(const Label* label, const Label* target) {
  if (isDisabled()) {
    return;
  }
  LabelDoc labelDoc = LabelDoc(internalResolve(label), label->bound());
  LabelDoc targetDoc = LabelDoc(internalResolve(target), target->bound());
  Node* probe = lookup(label);
  if (probe) {
    probe->bound = true;
  }
  spew("%s%d: .retarget -> %d%s", labelIndent_, labelDoc.doc, targetDoc.doc,
       !targetDoc.bound ? "f" : "");
}

void DisassemblerSpew::formatLiteral(const LiteralDoc& doc, char* buffer,
                                     size_t bufsize) {
  switch (doc.type) {
    case LiteralDoc::Type::Patchable:
      snprintf(buffer, bufsize, "patchable");
      break;
    case LiteralDoc::Type::I32:
      snprintf(buffer, bufsize, "%d", doc.value.i32);
      break;
    case LiteralDoc::Type::U32:
      snprintf(buffer, bufsize, "%u", doc.value.u32);
      break;
    case LiteralDoc::Type::I64:
      snprintf(buffer, bufsize, "%" PRIi64, doc.value.i64);
      break;
    case LiteralDoc::Type::U64:
      snprintf(buffer, bufsize, "%" PRIu64, doc.value.u64);
      break;
    case LiteralDoc::Type::F32:
      snprintf(buffer, bufsize, "%g", doc.value.f32);
      break;
    case LiteralDoc::Type::F64:
      snprintf(buffer, bufsize, "%g", doc.value.f64);
      break;
    default:
      MOZ_CRASH();
  }
}

void DisassemblerSpew::spewOrphans() {
  for (Node* p = nodes_; p; p = p->next) {
    if (!p->bound) {
      spew("%s%d:    ; .orphan", labelIndent_, p->value);
    }
  }
}

uint32_t DisassemblerSpew::internalResolve(const Label* l) {
  // Note, internalResolve will sometimes return 0 when it is triggered by the
  // profiler and not by a full disassembly, since in that case a label can be
  // used or bound but not previously have been defined.  In that case,
  // internalResolve(l) will not necessarily create a binding for l!
  // Consequently a subsequent lookup(l) may still return null.
  return l->used() || l->bound() ? probe(l) : define(l);
}

uint32_t DisassemblerSpew::probe(const Label* l) {
  Node* n = lookup(l);
  return n ? n->value : 0;
}

uint32_t DisassemblerSpew::define(const Label* l) {
  remove(l);
  uint32_t value = spewNext_++;
  if (!add(l, value)) {
    return 0;
  }
  return value;
}

DisassemblerSpew::Node* DisassemblerSpew::lookup(const Label* key) {
  Node* p;
  for (p = nodes_; p && p->key != key; p = p->next) {
    ;
  }
  return p;
}

DisassemblerSpew::Node* DisassemblerSpew::add(const Label* key,
                                              uint32_t value) {
  MOZ_ASSERT(!lookup(key));
  Node* node = js_new<Node>();
  if (node) {
    node->key = key;
    node->value = value;
    node->bound = false;
    node->next = nodes_;
    nodes_ = node;
  }
  return node;
}

bool DisassemblerSpew::remove(const Label* key) {
  // We do not require that there is a node matching the key.
  for (Node *p = nodes_, *pp = nullptr; p; pp = p, p = p->next) {
    if (p->key == key) {
      if (pp) {
        pp->next = p->next;
      } else {
        nodes_ = p->next;
      }
      js_free(p);
      return true;
    }
  }
  return false;
}

#else

DisassemblerSpew::LabelDoc DisassemblerSpew::refLabel(const Label* l) {
  return LabelDoc();
}

#endif