summaryrefslogtreecommitdiffstats
path: root/js/src/jit/shared/Disassembler-shared.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /js/src/jit/shared/Disassembler-shared.cpp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--js/src/jit/shared/Disassembler-shared.cpp248
1 files changed, 248 insertions, 0 deletions
diff --git a/js/src/jit/shared/Disassembler-shared.cpp b/js/src/jit/shared/Disassembler-shared.cpp
new file mode 100644
index 0000000000..11d062dd53
--- /dev/null
+++ b/js/src/jit/shared/Disassembler-shared.cpp
@@ -0,0 +1,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