Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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