summaryrefslogtreecommitdiffstats
path: root/js/src/jit/shared/Disassembler-shared.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/jit/shared/Disassembler-shared.h184
1 files changed, 184 insertions, 0 deletions
diff --git a/js/src/jit/shared/Disassembler-shared.h b/js/src/jit/shared/Disassembler-shared.h
new file mode 100644
index 0000000000..882de421a8
--- /dev/null
+++ b/js/src/jit/shared/Disassembler-shared.h
@@ -0,0 +1,184 @@
+/* -*- 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/. */
+
+#ifndef jit_shared_Disassembler_shared_h
+#define jit_shared_Disassembler_shared_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#if defined(JS_DISASM_ARM) || defined(JS_DISASM_ARM64)
+# define JS_DISASM_SUPPORTED
+#endif
+
+namespace js {
+
+class JS_PUBLIC_API Sprinter;
+
+namespace jit {
+
+class Label;
+
+// A wrapper around spew/disassembly functionality. The disassembler is built
+// on a per-instruction disassembler (as in our ARM, ARM64 back-ends) and
+// formats labels with meaningful names and literals with meaningful values, if
+// the assembler creates documentation (with provided helpers) at appropriate
+// points.
+
+class DisassemblerSpew {
+#ifdef JS_DISASM_SUPPORTED
+ struct Node {
+ const Label* key; // Never dereferenced, only used for its value
+ uint32_t value; // The printable label value
+ bool bound; // If the label has been seen by spewBind()
+ Node* next;
+ };
+
+ Node* lookup(const Label* key);
+ Node* add(const Label* key, uint32_t value);
+ bool remove(const Label* key);
+
+ uint32_t probe(const Label* l);
+ uint32_t define(const Label* l);
+ uint32_t internalResolve(const Label* l);
+#endif
+
+ void spewVA(const char* fmt, va_list args) MOZ_FORMAT_PRINTF(2, 0);
+
+ public:
+ DisassemblerSpew();
+ ~DisassemblerSpew();
+
+#ifdef JS_DISASM_SUPPORTED
+ // Set indentation strings. The spewer retains a reference to s.
+ void setLabelIndent(const char* s);
+ void setTargetIndent(const char* s);
+#endif
+
+ // Set the spew printer, which will always be used if it is set, regardless
+ // of whether the system spew channel is enabled or not. The spewer retains
+ // a reference to sp.
+ void setPrinter(Sprinter* sp);
+
+ // Return true if disassembly spew is disabled and no additional printer is
+ // set.
+ bool isDisabled();
+
+ // Format and print text on the spew channel; output is suppressed if spew
+ // is disabled. The output is not indented, and is terminated by a newline.
+ void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
+
+ // Documentation for a label reference.
+ struct LabelDoc {
+#ifdef JS_DISASM_SUPPORTED
+ LabelDoc() : doc(0), bound(false), valid(false) {}
+ LabelDoc(uint32_t doc, bool bound) : doc(doc), bound(bound), valid(true) {}
+ const uint32_t doc;
+ const bool bound;
+ const bool valid;
+#else
+ LabelDoc() = default;
+ LabelDoc(uint32_t, bool) {}
+#endif
+ };
+
+ // Documentation for a literal load.
+ struct LiteralDoc {
+#ifdef JS_DISASM_SUPPORTED
+ enum class Type { Patchable, I32, U32, I64, U64, F32, F64 };
+ const Type type;
+ union {
+ int32_t i32;
+ uint32_t u32;
+ int64_t i64;
+ uint64_t u64;
+ float f32;
+ double f64;
+ } value;
+ LiteralDoc() : type(Type::Patchable) {}
+ explicit LiteralDoc(int32_t v) : type(Type::I32) { value.i32 = v; }
+ explicit LiteralDoc(uint32_t v) : type(Type::U32) { value.u32 = v; }
+ explicit LiteralDoc(int64_t v) : type(Type::I64) { value.i64 = v; }
+ explicit LiteralDoc(uint64_t v) : type(Type::U64) { value.u64 = v; }
+ explicit LiteralDoc(float v) : type(Type::F32) { value.f32 = v; }
+ explicit LiteralDoc(double v) : type(Type::F64) { value.f64 = v; }
+#else
+ LiteralDoc() = default;
+ explicit LiteralDoc(int32_t) {}
+ explicit LiteralDoc(uint32_t) {}
+ explicit LiteralDoc(int64_t) {}
+ explicit LiteralDoc(uint64_t) {}
+ explicit LiteralDoc(float) {}
+ explicit LiteralDoc(double) {}
+#endif
+ };
+
+ // Reference a label, resolving it to a printable representation.
+ //
+ // NOTE: The printable representation depends on the state of the label, so
+ // if we call resolve() when emitting & disassembling a branch instruction
+ // then it should be called before the label becomes Used, if emitting the
+ // branch can change the label's state.
+ //
+ // If the disassembler is not defined this returns a structure that is
+ // marked not valid.
+ LabelDoc refLabel(const Label* l);
+
+#ifdef JS_DISASM_SUPPORTED
+ // Spew the label information previously gathered by refLabel(), at a point
+ // where the label is referenced. The output is indented by targetIndent_
+ // and terminated by a newline.
+ void spewRef(const LabelDoc& target);
+
+ // Spew the label at the point where the label is bound. The output is
+ // indented by labelIndent_ and terminated by a newline.
+ void spewBind(const Label* label);
+
+ // Spew a retarget directive at the point where the retarget is recorded.
+ // The output is indented by labelIndent_ and terminated by a newline.
+ void spewRetarget(const Label* label, const Label* target);
+
+ // Format a literal value into the buffer. The buffer is always
+ // NUL-terminated even if this chops the formatted value.
+ void formatLiteral(const LiteralDoc& doc, char* buffer, size_t bufsize);
+
+ // Print any unbound labels, one per line, with normal label indent and with
+ // a comment indicating the label is not defined. Labels can be referenced
+ // but unbound in some legitimate cases, normally for traps. Printing them
+ // reduces confusion.
+ void spewOrphans();
+#endif
+
+ private:
+ Sprinter* printer_;
+#ifdef JS_DISASM_SUPPORTED
+ const char* labelIndent_;
+ const char* targetIndent_;
+ uint32_t spewNext_;
+ Node* nodes_;
+ uint32_t tag_;
+
+ // This global is used to disambiguate concurrently live assemblers, see
+ // comments in Disassembler-shared.cpp for why this is desirable.
+ //
+ // The variable is atomic to avoid any kind of complaint from thread
+ // sanitizers etc. However, trying to look at disassembly without using
+ // --no-threads is basically insane, so you can ignore the multi-threading
+ // implications here.
+ static mozilla::Atomic<uint32_t> counter_;
+#endif
+};
+
+} // namespace jit
+} // namespace js
+
+#endif // jit_shared_Disassembler_shared_h