Disassembler-shared.h (6166B)
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 #ifndef jit_shared_Disassembler_shared_h 8 #define jit_shared_Disassembler_shared_h 9 10 #include "mozilla/Atomics.h" 11 #include "mozilla/Attributes.h" 12 13 #include <stdarg.h> 14 #include <stddef.h> 15 #include <stdint.h> 16 17 #include "jstypes.h" // JS_PUBLIC_API 18 19 #if defined(JS_DISASM_ARM) || defined(JS_DISASM_ARM64) 20 # define JS_DISASM_SUPPORTED 21 #endif 22 23 namespace js { 24 25 class JS_PUBLIC_API Sprinter; 26 27 namespace jit { 28 29 class Label; 30 31 // A wrapper around spew/disassembly functionality. The disassembler is built 32 // on a per-instruction disassembler (as in our ARM, ARM64 back-ends) and 33 // formats labels with meaningful names and literals with meaningful values, if 34 // the assembler creates documentation (with provided helpers) at appropriate 35 // points. 36 37 class DisassemblerSpew { 38 #ifdef JS_DISASM_SUPPORTED 39 struct Node { 40 const Label* key; // Never dereferenced, only used for its value 41 uint32_t value; // The printable label value 42 bool bound; // If the label has been seen by spewBind() 43 Node* next; 44 }; 45 46 Node* lookup(const Label* key); 47 Node* add(const Label* key, uint32_t value); 48 bool remove(const Label* key); 49 50 uint32_t probe(const Label* l); 51 uint32_t define(const Label* l); 52 uint32_t internalResolve(const Label* l); 53 #endif 54 55 void spewVA(const char* fmt, va_list args) MOZ_FORMAT_PRINTF(2, 0); 56 57 public: 58 DisassemblerSpew(); 59 ~DisassemblerSpew(); 60 61 #ifdef JS_DISASM_SUPPORTED 62 // Set indentation strings. The spewer retains a reference to s. 63 void setLabelIndent(const char* s); 64 void setTargetIndent(const char* s); 65 #endif 66 67 // Set the spew printer, which will always be used if it is set, regardless 68 // of whether the system spew channel is enabled or not. The spewer retains 69 // a reference to sp. 70 void setPrinter(Sprinter* sp); 71 72 // Return true if disassembly spew is disabled and no additional printer is 73 // set. 74 bool isDisabled(); 75 76 // Format and print text on the spew channel; output is suppressed if spew 77 // is disabled. The output is not indented, and is terminated by a newline. 78 void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3); 79 80 // Documentation for a label reference. 81 struct LabelDoc { 82 #ifdef JS_DISASM_SUPPORTED 83 LabelDoc() : doc(0), bound(false), valid(false) {} 84 LabelDoc(uint32_t doc, bool bound) : doc(doc), bound(bound), valid(true) {} 85 const uint32_t doc; 86 const bool bound; 87 const bool valid; 88 #else 89 LabelDoc() = default; 90 LabelDoc(uint32_t, bool) {} 91 #endif 92 }; 93 94 // Documentation for a literal load. 95 struct LiteralDoc { 96 #ifdef JS_DISASM_SUPPORTED 97 enum class Type { Patchable, I32, U32, I64, U64, F32, F64 }; 98 const Type type; 99 union { 100 int32_t i32; 101 uint32_t u32; 102 int64_t i64; 103 uint64_t u64; 104 float f32; 105 double f64; 106 } value; 107 LiteralDoc() : type(Type::Patchable) {} 108 explicit LiteralDoc(int32_t v) : type(Type::I32) { value.i32 = v; } 109 explicit LiteralDoc(uint32_t v) : type(Type::U32) { value.u32 = v; } 110 explicit LiteralDoc(int64_t v) : type(Type::I64) { value.i64 = v; } 111 explicit LiteralDoc(uint64_t v) : type(Type::U64) { value.u64 = v; } 112 explicit LiteralDoc(float v) : type(Type::F32) { value.f32 = v; } 113 explicit LiteralDoc(double v) : type(Type::F64) { value.f64 = v; } 114 #else 115 LiteralDoc() = default; 116 explicit LiteralDoc(int32_t) {} 117 explicit LiteralDoc(uint32_t) {} 118 explicit LiteralDoc(int64_t) {} 119 explicit LiteralDoc(uint64_t) {} 120 explicit LiteralDoc(float) {} 121 explicit LiteralDoc(double) {} 122 #endif 123 }; 124 125 // Reference a label, resolving it to a printable representation. 126 // 127 // NOTE: The printable representation depends on the state of the label, so 128 // if we call resolve() when emitting & disassembling a branch instruction 129 // then it should be called before the label becomes Used, if emitting the 130 // branch can change the label's state. 131 // 132 // If the disassembler is not defined this returns a structure that is 133 // marked not valid. 134 LabelDoc refLabel(const Label* l); 135 136 #ifdef JS_DISASM_SUPPORTED 137 // Spew the label information previously gathered by refLabel(), at a point 138 // where the label is referenced. The output is indented by targetIndent_ 139 // and terminated by a newline. 140 void spewRef(const LabelDoc& target); 141 142 // Spew the label at the point where the label is bound. The output is 143 // indented by labelIndent_ and terminated by a newline. 144 void spewBind(const Label* label); 145 146 // Spew a retarget directive at the point where the retarget is recorded. 147 // The output is indented by labelIndent_ and terminated by a newline. 148 void spewRetarget(const Label* label, const Label* target); 149 150 // Format a literal value into the buffer. The buffer is always 151 // NUL-terminated even if this chops the formatted value. 152 void formatLiteral(const LiteralDoc& doc, char* buffer, size_t bufsize); 153 154 // Print any unbound labels, one per line, with normal label indent and with 155 // a comment indicating the label is not defined. Labels can be referenced 156 // but unbound in some legitimate cases, normally for traps. Printing them 157 // reduces confusion. 158 void spewOrphans(); 159 #endif 160 161 private: 162 Sprinter* printer_; 163 #ifdef JS_DISASM_SUPPORTED 164 const char* labelIndent_; 165 const char* targetIndent_; 166 uint32_t spewNext_; 167 Node* nodes_; 168 uint32_t tag_; 169 170 // This global is used to disambiguate concurrently live assemblers, see 171 // comments in Disassembler-shared.cpp for why this is desirable. 172 // 173 // The variable is atomic to avoid any kind of complaint from thread 174 // sanitizers etc. However, trying to look at disassembly without using 175 // --no-threads is basically insane, so you can ignore the multi-threading 176 // implications here. 177 static mozilla::Atomic<uint32_t> counter_; 178 #endif 179 }; 180 181 } // namespace jit 182 } // namespace js 183 184 #endif // jit_shared_Disassembler_shared_h