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/. */
9 # include "jit/CacheIRSpewer.h"
11 # include "mozilla/Sprintf.h"
19 # include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
20 # include "js/ScalarType.h" // js::Scalar::Type
21 # include "util/GetPidProvider.h"
22 # include "util/Text.h"
23 # include "vm/JSFunction.h"
24 # include "vm/JSObject.h"
25 # include "vm/JSScript.h"
27 # include "vm/JSObject-inl.h"
28 # include "vm/Realm-inl.h"
31 using namespace js::jit
;
33 // Text spewer for CacheIR ops that can be used with JitSpew.
34 // Output looks like this:
36 // GuardToInt32 inputId 0, resultId 2
37 // GuardToInt32 inputId 1, resultId 3
38 // CompareInt32Result op JSOp::Lt, lhsId 2, rhsId 3
40 class MOZ_RAII CacheIROpsJitSpewer
{
43 // String prepended to each line. Can be used for indentation.
46 CACHE_IR_SPEWER_GENERATED
48 void spewOp(CacheOp op
) {
49 const char* opName
= CacheIROpNames
[size_t(op
)];
50 out_
.printf("%s%-30s", prefix_
, opName
);
52 void spewOpEnd() { out_
.printf("\n"); }
54 void spewArgSeparator() { out_
.printf(", "); }
56 void spewOperandId(const char* name
, OperandId id
) {
57 spewRawOperandId(name
, id
.id());
59 void spewRawOperandId(const char* name
, uint32_t id
) {
60 out_
.printf("%s %u", name
, id
);
62 void spewField(const char* name
, uint32_t offset
) {
63 out_
.printf("%s %u", name
, offset
);
65 void spewBoolImm(const char* name
, bool b
) {
66 out_
.printf("%s %s", name
, b
? "true" : "false");
68 void spewByteImm(const char* name
, uint8_t val
) {
69 out_
.printf("%s %u", name
, val
);
71 void spewJSOpImm(const char* name
, JSOp op
) {
72 out_
.printf("%s JSOp::%s", name
, CodeName(op
));
74 void spewStaticStringImm(const char* name
, const char* str
) {
75 out_
.printf("%s \"%s\"", name
, str
);
77 void spewInt32Imm(const char* name
, int32_t val
) {
78 out_
.printf("%s %d", name
, val
);
80 void spewUInt32Imm(const char* name
, uint32_t val
) {
81 out_
.printf("%s %u", name
, val
);
83 void spewCallFlagsImm(const char* name
, CallFlags flags
) {
85 "%s (format %u%s%s%s)", name
, flags
.getArgFormat(),
86 flags
.isConstructing() ? ", isConstructing" : "",
87 flags
.isSameRealm() ? ", isSameRealm" : "",
88 flags
.needsUninitializedThis() ? ", needsUninitializedThis" : "");
90 void spewJSWhyMagicImm(const char* name
, JSWhyMagic magic
) {
91 out_
.printf("%s JSWhyMagic(%u)", name
, unsigned(magic
));
93 void spewScalarTypeImm(const char* name
, Scalar::Type type
) {
94 out_
.printf("%s Scalar::Type(%u)", name
, unsigned(type
));
96 void spewUnaryMathFunctionImm(const char* name
, UnaryMathFunction fun
) {
97 const char* funName
= GetUnaryMathFunctionName(fun
);
98 out_
.printf("%s UnaryMathFunction::%s", name
, funName
);
100 void spewValueTypeImm(const char* name
, ValueType type
) {
101 out_
.printf("%s ValueType(%u)", name
, unsigned(type
));
103 void spewJSNativeImm(const char* name
, JSNative native
) {
104 out_
.printf("%s %p", name
, native
);
106 void spewGuardClassKindImm(const char* name
, GuardClassKind kind
) {
107 out_
.printf("%s GuardClassKind(%u)", name
, unsigned(kind
));
109 void spewArrayBufferViewKindImm(const char* name
, ArrayBufferViewKind kind
) {
110 out_
.printf("%s ArrayBufferViewKind(%u)", name
, unsigned(kind
));
112 void spewWasmValTypeImm(const char* name
, wasm::ValType::Kind kind
) {
113 out_
.printf("%s WasmValTypeKind(%u)", name
, unsigned(kind
));
115 void spewAllocKindImm(const char* name
, gc::AllocKind kind
) {
116 out_
.printf("%s AllocKind(%u)", name
, unsigned(kind
));
118 void spewCompletionKindImm(const char* name
, CompletionKind kind
) {
119 out_
.printf("%s CompletionKind(%u)", name
, unsigned(kind
));
121 void spewRealmFuseIndexImm(const char* name
, RealmFuses::FuseIndex index
) {
122 out_
.printf("%s RealmFuseIndex(%u=%s)", name
, unsigned(index
),
123 RealmFuses::getFuseName(index
));
127 CacheIROpsJitSpewer(GenericPrinter
& out
, const char* prefix
)
128 : out_(out
), prefix_(prefix
) {}
130 void spew(CacheIRReader
& reader
) {
132 switch (reader
.readOp()) {
133 # define SPEW_OP(op, ...) \
137 CACHE_IR_OPS(SPEW_OP
)
141 MOZ_CRASH("Invalid op");
143 } while (reader
.more());
147 void js::jit::SpewCacheIROps(GenericPrinter
& out
, const char* prefix
,
148 const CacheIRStubInfo
* info
) {
149 CacheIRReader
reader(info
);
150 CacheIROpsJitSpewer
spewer(out
, prefix
);
154 // JSON spewer for CacheIR ops. Output looks like this:
158 // "op":"GuardToInt32",
166 // "name":"resultId",
173 // "op":"Int32IncResult",
183 class MOZ_RAII CacheIROpsJSONSpewer
{
186 CACHE_IR_SPEWER_GENERATED
188 void spewOp(CacheOp op
) {
189 const char* opName
= CacheIROpNames
[size_t(op
)];
191 j_
.property("op", opName
);
192 j_
.beginListProperty("args");
199 void spewArgSeparator() {}
201 template <typename T
>
202 void spewArgImpl(const char* name
, const char* type
, T value
) {
204 j_
.property("name", name
);
205 j_
.property("type", type
);
206 j_
.property("value", value
);
210 void spewOperandId(const char* name
, OperandId id
) {
211 spewRawOperandId(name
, id
.id());
213 void spewRawOperandId(const char* name
, uint32_t id
) {
214 spewArgImpl(name
, "Id", id
);
216 void spewField(const char* name
, uint32_t offset
) {
217 spewArgImpl(name
, "Field", offset
);
219 void spewBoolImm(const char* name
, bool b
) { spewArgImpl(name
, "Imm", b
); }
220 void spewByteImm(const char* name
, uint8_t val
) {
221 spewArgImpl(name
, "Imm", val
);
223 void spewJSOpImm(const char* name
, JSOp op
) {
224 spewArgImpl(name
, "JSOp", CodeName(op
));
226 void spewStaticStringImm(const char* name
, const char* str
) {
227 spewArgImpl(name
, "String", str
);
229 void spewInt32Imm(const char* name
, int32_t val
) {
230 spewArgImpl(name
, "Imm", val
);
232 void spewUInt32Imm(const char* name
, uint32_t val
) {
233 spewArgImpl(name
, "Imm", val
);
235 void spewCallFlagsImm(const char* name
, CallFlags flags
) {
236 spewArgImpl(name
, "Imm", flags
.toByte());
238 void spewJSWhyMagicImm(const char* name
, JSWhyMagic magic
) {
239 spewArgImpl(name
, "Imm", unsigned(magic
));
241 void spewScalarTypeImm(const char* name
, Scalar::Type type
) {
242 spewArgImpl(name
, "Imm", unsigned(type
));
244 void spewUnaryMathFunctionImm(const char* name
, UnaryMathFunction fun
) {
245 const char* funName
= GetUnaryMathFunctionName(fun
);
246 spewArgImpl(name
, "MathFunction", funName
);
248 void spewValueTypeImm(const char* name
, ValueType type
) {
249 spewArgImpl(name
, "Imm", unsigned(type
));
251 void spewJSNativeImm(const char* name
, JSNative native
) {
252 spewArgImpl(name
, "Word", uintptr_t(native
));
254 void spewGuardClassKindImm(const char* name
, GuardClassKind kind
) {
255 spewArgImpl(name
, "Imm", unsigned(kind
));
257 void spewArrayBufferViewKindImm(const char* name
, ArrayBufferViewKind kind
) {
258 spewArgImpl(name
, "Imm", unsigned(kind
));
260 void spewRealmFuseIndexImm(const char* name
, RealmFuses::FuseIndex kind
) {
261 spewArgImpl(name
, "Imm", unsigned(kind
));
263 void spewWasmValTypeImm(const char* name
, wasm::ValType::Kind kind
) {
264 spewArgImpl(name
, "Imm", unsigned(kind
));
266 void spewAllocKindImm(const char* name
, gc::AllocKind kind
) {
267 spewArgImpl(name
, "Imm", unsigned(kind
));
269 void spewCompletionKindImm(const char* name
, CompletionKind kind
) {
270 spewArgImpl(name
, "Imm", unsigned(kind
));
274 explicit CacheIROpsJSONSpewer(JSONPrinter
& j
) : j_(j
) {}
276 void spew(CacheIRReader
& reader
) {
278 switch (reader
.readOp()) {
279 # define SPEW_OP(op, ...) \
283 CACHE_IR_OPS(SPEW_OP
)
287 MOZ_CRASH("Invalid op");
289 } while (reader
.more());
293 CacheIRSpewer
CacheIRSpewer::cacheIRspewer
;
295 CacheIRSpewer::CacheIRSpewer()
296 : outputLock_(mutexid::CacheIRSpewer
), guardCount_(0) {
298 getenv("CACHEIR_LOG_FLUSH") ? atoi(getenv("CACHEIR_LOG_FLUSH")) : 10000;
300 if (spewInterval_
< 1) {
305 CacheIRSpewer::~CacheIRSpewer() {
310 json_
.ref().endList();
315 # ifndef JIT_SPEW_DIR
317 # define JIT_SPEW_DIR "."
318 # elif defined(__ANDROID__)
319 # define JIT_SPEW_DIR "/data/local/tmp"
321 # define JIT_SPEW_DIR "/tmp"
325 bool CacheIRSpewer::init(const char* filename
) {
331 uint32_t pid
= getpid();
332 // Default to JIT_SPEW_DIR/cacheir${pid}.json
333 if (filename
[0] == '1') {
334 SprintfLiteral(name
, JIT_SPEW_DIR
"/cacheir%" PRIu32
".json", pid
);
336 SprintfLiteral(name
, "%s%" PRIu32
".json", filename
, pid
);
339 if (!output_
.init(name
)) {
343 json_
.emplace(output_
);
348 void CacheIRSpewer::beginCache(const IRGenerator
& gen
) {
349 MOZ_ASSERT(enabled());
350 JSONPrinter
& j
= json_
.ref();
351 const char* filename
= gen
.script_
->filename();
353 j
.property("name", CacheKindNames
[uint8_t(gen
.cacheKind_
)]);
354 j
.property("file", filename
? filename
: "null");
355 j
.property("mode", int(gen
.mode_
));
356 if (jsbytecode
* pc
= gen
.pc_
) {
357 JS::LimitedColumnNumberOneOrigin column
;
358 j
.property("line", PCToLineNumber(gen
.script_
, pc
, &column
));
359 j
.property("column", column
.oneOriginValue());
360 j
.formatProperty("pc", "%p", pc
);
364 void CacheIRSpewer::valueProperty(const char* name
, const Value
& v
) {
365 MOZ_ASSERT(enabled());
366 JSONPrinter
& j
= json_
.ref();
368 j
.beginObjectProperty(name
);
370 const char* type
= InformalValueTypeName(v
);
374 j
.property("type", type
);
377 j
.property("value", v
.toInt32());
378 } else if (v
.isDouble()) {
379 j
.floatProperty("value", v
.toDouble(), 3);
380 } else if (v
.isString() || v
.isSymbol()) {
381 JSString
* str
= v
.isString() ? v
.toString() : v
.toSymbol()->description();
382 if (str
&& str
->isLinear()) {
383 j
.property("value", &str
->asLinear());
385 } else if (v
.isObject()) {
386 JSObject
& object
= v
.toObject();
387 j
.formatProperty("value", "%p (shape: %p)", &object
, object
.shape());
389 if (object
.is
<JSFunction
>()) {
390 if (JSAtom
* name
= object
.as
<JSFunction
>().maybePartialDisplayAtom()) {
391 j
.property("funName", name
);
395 if (NativeObject
* nobj
=
396 object
.is
<NativeObject
>() ? &object
.as
<NativeObject
>() : nullptr) {
397 j
.beginListProperty("flags");
399 if (nobj
->isIndexed()) {
402 if (nobj
->inDictionaryMode()) {
403 j
.value("dictionaryMode");
407 if (nobj
->isIndexed()) {
408 j
.beginObjectProperty("indexed");
410 j
.property("denseInitializedLength",
411 nobj
->getDenseInitializedLength());
412 j
.property("denseCapacity", nobj
->getDenseCapacity());
413 j
.property("denseElementsAreSealed", nobj
->denseElementsAreSealed());
414 j
.property("denseElementsAreFrozen", nobj
->denseElementsAreFrozen());
424 void CacheIRSpewer::opcodeProperty(const char* name
, const JSOp op
) {
425 MOZ_ASSERT(enabled());
426 JSONPrinter
& j
= json_
.ref();
428 j
.beginStringProperty(name
);
429 output_
.put(CodeName(op
));
430 j
.endStringProperty();
433 void CacheIRSpewer::cacheIRSequence(CacheIRReader
& reader
) {
434 MOZ_ASSERT(enabled());
435 JSONPrinter
& j
= json_
.ref();
437 j
.beginListProperty("cacheIR");
439 CacheIROpsJSONSpewer
spewer(j
);
445 void CacheIRSpewer::attached(const char* name
) {
446 MOZ_ASSERT(enabled());
447 json_
.ref().property("attached", name
);
450 void CacheIRSpewer::endCache() {
451 MOZ_ASSERT(enabled());
452 json_
.ref().endObject();
455 #endif /* JS_CACHEIR_SPEW */