summaryrefslogtreecommitdiffstats
path: root/js/src/jit/CacheIRSpewer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/CacheIRSpewer.cpp')
-rw-r--r--js/src/jit/CacheIRSpewer.cpp468
1 files changed, 468 insertions, 0 deletions
diff --git a/js/src/jit/CacheIRSpewer.cpp b/js/src/jit/CacheIRSpewer.cpp
new file mode 100644
index 0000000000..63d9fa754f
--- /dev/null
+++ b/js/src/jit/CacheIRSpewer.cpp
@@ -0,0 +1,468 @@
+/* -*- 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/. */
+
+#ifdef JS_CACHEIR_SPEW
+
+# include "jit/CacheIRSpewer.h"
+
+# include "mozilla/Sprintf.h"
+
+# include <algorithm>
+
+# ifdef XP_WIN
+# include <process.h>
+# define getpid _getpid
+# else
+# include <unistd.h>
+# endif
+# include <stdarg.h>
+
+# include "jsmath.h"
+
+# include "js/ScalarType.h" // js::Scalar::Type
+# include "util/Text.h"
+# include "vm/JSFunction.h"
+# include "vm/JSObject.h"
+# include "vm/JSScript.h"
+
+# include "vm/JSObject-inl.h"
+# include "vm/Realm-inl.h"
+
+using namespace js;
+using namespace js::jit;
+
+// Text spewer for CacheIR ops that can be used with JitSpew.
+// Output looks like this:
+//
+// GuardToInt32 inputId 0, resultId 2
+// GuardToInt32 inputId 1, resultId 3
+// CompareInt32Result op JSOp::Lt, lhsId 2, rhsId 3
+// ReturnFromIC
+class MOZ_RAII CacheIROpsJitSpewer {
+ GenericPrinter& out_;
+
+ // String prepended to each line. Can be used for indentation.
+ const char* prefix_;
+
+ CACHE_IR_SPEWER_GENERATED
+
+ void spewOp(CacheOp op) {
+ const char* opName = CacheIROpNames[size_t(op)];
+ out_.printf("%s%-30s", prefix_, opName);
+ }
+ void spewOpEnd() { out_.printf("\n"); }
+
+ void spewArgSeparator() { out_.printf(", "); }
+
+ void spewOperandId(const char* name, OperandId id) {
+ spewRawOperandId(name, id.id());
+ }
+ void spewRawOperandId(const char* name, uint32_t id) {
+ out_.printf("%s %u", name, id);
+ }
+ void spewField(const char* name, uint32_t offset) {
+ out_.printf("%s %u", name, offset);
+ }
+ void spewBoolImm(const char* name, bool b) {
+ out_.printf("%s %s", name, b ? "true" : "false");
+ }
+ void spewByteImm(const char* name, uint8_t val) {
+ out_.printf("%s %u", name, val);
+ }
+ void spewJSOpImm(const char* name, JSOp op) {
+ out_.printf("%s JSOp::%s", name, CodeName(op));
+ }
+ void spewStaticStringImm(const char* name, const char* str) {
+ out_.printf("%s \"%s\"", name, str);
+ }
+ void spewInt32Imm(const char* name, int32_t val) {
+ out_.printf("%s %d", name, val);
+ }
+ void spewUInt32Imm(const char* name, uint32_t val) {
+ out_.printf("%s %u", name, val);
+ }
+ void spewCallFlagsImm(const char* name, CallFlags flags) {
+ out_.printf(
+ "%s (format %u%s%s%s)", name, flags.getArgFormat(),
+ flags.isConstructing() ? ", isConstructing" : "",
+ flags.isSameRealm() ? ", isSameRealm" : "",
+ flags.needsUninitializedThis() ? ", needsUninitializedThis" : "");
+ }
+ void spewJSWhyMagicImm(const char* name, JSWhyMagic magic) {
+ out_.printf("%s JSWhyMagic(%u)", name, unsigned(magic));
+ }
+ void spewScalarTypeImm(const char* name, Scalar::Type type) {
+ out_.printf("%s Scalar::Type(%u)", name, unsigned(type));
+ }
+ void spewUnaryMathFunctionImm(const char* name, UnaryMathFunction fun) {
+ const char* funName = GetUnaryMathFunctionName(fun);
+ out_.printf("%s UnaryMathFunction::%s", name, funName);
+ }
+ void spewValueTypeImm(const char* name, ValueType type) {
+ out_.printf("%s ValueType(%u)", name, unsigned(type));
+ }
+ void spewJSNativeImm(const char* name, JSNative native) {
+ out_.printf("%s %p", name, native);
+ }
+ void spewGuardClassKindImm(const char* name, GuardClassKind kind) {
+ out_.printf("%s GuardClassKind(%u)", name, unsigned(kind));
+ }
+ void spewWasmValTypeImm(const char* name, wasm::ValType::Kind kind) {
+ out_.printf("%s WasmValTypeKind(%u)", name, unsigned(kind));
+ }
+
+ public:
+ CacheIROpsJitSpewer(GenericPrinter& out, const char* prefix)
+ : out_(out), prefix_(prefix) {}
+
+ void spew(CacheIRReader& reader) {
+ do {
+ switch (reader.readOp()) {
+# define SPEW_OP(op, ...) \
+ case CacheOp::op: \
+ spew##op(reader); \
+ break;
+ CACHE_IR_OPS(SPEW_OP)
+# undef SPEW_OP
+
+ default:
+ MOZ_CRASH("Invalid op");
+ }
+ } while (reader.more());
+ }
+};
+
+void js::jit::SpewCacheIROps(GenericPrinter& out, const char* prefix,
+ const CacheIRStubInfo* info) {
+ CacheIRReader reader(info);
+ CacheIROpsJitSpewer spewer(out, prefix);
+ spewer.spew(reader);
+}
+
+// JSON spewer for CacheIR ops. Output looks like this:
+//
+// ...
+// {
+// "op":"GuardToInt32",
+// "args":[
+// {
+// "name":"inputId",
+// "type":"Id",
+// "value":0
+// },
+// {
+// "name":"resultId",
+// "type":"Id",
+// "value":1
+// }
+// ]
+// },
+// {
+// "op":"Int32IncResult",
+// "args":[
+// {
+// "name":"inputId",
+// "type":"Id",
+// "value":1
+// }
+// ]
+// }
+// ...
+class MOZ_RAII CacheIROpsJSONSpewer {
+ JSONPrinter& j_;
+
+ CACHE_IR_SPEWER_GENERATED
+
+ void spewOp(CacheOp op) {
+ const char* opName = CacheIROpNames[size_t(op)];
+ j_.beginObject();
+ j_.property("op", opName);
+ j_.beginListProperty("args");
+ }
+ void spewOpEnd() {
+ j_.endList();
+ j_.endObject();
+ }
+
+ void spewArgSeparator() {}
+
+ template <typename T>
+ void spewArgImpl(const char* name, const char* type, T value) {
+ j_.beginObject();
+ j_.property("name", name);
+ j_.property("type", type);
+ j_.property("value", value);
+ j_.endObject();
+ }
+
+ void spewOperandId(const char* name, OperandId id) {
+ spewRawOperandId(name, id.id());
+ }
+ void spewRawOperandId(const char* name, uint32_t id) {
+ spewArgImpl(name, "Id", id);
+ }
+ void spewField(const char* name, uint32_t offset) {
+ spewArgImpl(name, "Field", offset);
+ }
+ void spewBoolImm(const char* name, bool b) { spewArgImpl(name, "Imm", b); }
+ void spewByteImm(const char* name, uint8_t val) {
+ spewArgImpl(name, "Imm", val);
+ }
+ void spewJSOpImm(const char* name, JSOp op) {
+ spewArgImpl(name, "JSOp", CodeName(op));
+ }
+ void spewStaticStringImm(const char* name, const char* str) {
+ spewArgImpl(name, "String", str);
+ }
+ void spewInt32Imm(const char* name, int32_t val) {
+ spewArgImpl(name, "Imm", val);
+ }
+ void spewUInt32Imm(const char* name, uint32_t val) {
+ spewArgImpl(name, "Imm", val);
+ }
+ void spewCallFlagsImm(const char* name, CallFlags flags) {
+ spewArgImpl(name, "Imm", flags.toByte());
+ }
+ void spewJSWhyMagicImm(const char* name, JSWhyMagic magic) {
+ spewArgImpl(name, "Imm", unsigned(magic));
+ }
+ void spewScalarTypeImm(const char* name, Scalar::Type type) {
+ spewArgImpl(name, "Imm", unsigned(type));
+ }
+ void spewUnaryMathFunctionImm(const char* name, UnaryMathFunction fun) {
+ const char* funName = GetUnaryMathFunctionName(fun);
+ spewArgImpl(name, "MathFunction", funName);
+ }
+ void spewValueTypeImm(const char* name, ValueType type) {
+ spewArgImpl(name, "Imm", unsigned(type));
+ }
+ void spewJSNativeImm(const char* name, JSNative native) {
+ spewArgImpl(name, "Word", uintptr_t(native));
+ }
+ void spewGuardClassKindImm(const char* name, GuardClassKind kind) {
+ spewArgImpl(name, "Imm", unsigned(kind));
+ }
+ void spewWasmValTypeImm(const char* name, wasm::ValType::Kind kind) {
+ spewArgImpl(name, "Imm", unsigned(kind));
+ }
+
+ public:
+ explicit CacheIROpsJSONSpewer(JSONPrinter& j) : j_(j) {}
+
+ void spew(CacheIRReader& reader) {
+ do {
+ switch (reader.readOp()) {
+# define SPEW_OP(op, ...) \
+ case CacheOp::op: \
+ spew##op(reader); \
+ break;
+ CACHE_IR_OPS(SPEW_OP)
+# undef SPEW_OP
+
+ default:
+ MOZ_CRASH("Invalid op");
+ }
+ } while (reader.more());
+ }
+};
+
+CacheIRSpewer CacheIRSpewer::cacheIRspewer;
+
+CacheIRSpewer::CacheIRSpewer()
+ : outputLock_(mutexid::CacheIRSpewer), guardCount_(0) {
+ spewInterval_ =
+ getenv("CACHEIR_LOG_FLUSH") ? atoi(getenv("CACHEIR_LOG_FLUSH")) : 10000;
+
+ if (spewInterval_ < 1) {
+ spewInterval_ = 1;
+ }
+}
+
+CacheIRSpewer::~CacheIRSpewer() {
+ if (!enabled()) {
+ return;
+ }
+
+ json_.ref().endList();
+ output_.flush();
+ output_.finish();
+}
+
+# ifndef JIT_SPEW_DIR
+# if defined(_WIN32)
+# define JIT_SPEW_DIR "."
+# elif defined(__ANDROID__)
+# define JIT_SPEW_DIR "/data/local/tmp"
+# else
+# define JIT_SPEW_DIR "/tmp"
+# endif
+# endif
+
+bool CacheIRSpewer::init(const char* filename) {
+ if (enabled()) {
+ return true;
+ }
+
+ char name[256];
+ uint32_t pid = getpid();
+ // Default to JIT_SPEW_DIR/cacheir${pid}.json
+ if (filename[0] == '1') {
+ SprintfLiteral(name, JIT_SPEW_DIR "/cacheir%" PRIu32 ".json", pid);
+ } else {
+ SprintfLiteral(name, "%s%" PRIu32 ".json", filename, pid);
+ }
+
+ if (!output_.init(name)) {
+ return false;
+ }
+
+ json_.emplace(output_);
+ json_->beginList();
+ return true;
+}
+
+void CacheIRSpewer::beginCache(const IRGenerator& gen) {
+ MOZ_ASSERT(enabled());
+ JSONPrinter& j = json_.ref();
+ const char* filename = gen.script_->filename();
+ j.beginObject();
+ j.property("name", CacheKindNames[uint8_t(gen.cacheKind_)]);
+ j.property("file", filename ? filename : "null");
+ j.property("mode", int(gen.mode_));
+ if (jsbytecode* pc = gen.pc_) {
+ unsigned column;
+ j.property("line", PCToLineNumber(gen.script_, pc, &column));
+ j.property("column", column);
+ j.formatProperty("pc", "%p", pc);
+ }
+}
+
+template <typename CharT>
+static void QuoteString(GenericPrinter& out, const CharT* s, size_t length) {
+ const CharT* end = s + length;
+ for (const CharT* t = s; t < end; s = ++t) {
+ // This quote implementation is probably correct,
+ // but uses \u even when not strictly necessary.
+ char16_t c = *t;
+ if (c == '"' || c == '\\') {
+ out.printf("\\");
+ out.printf("%c", char(c));
+ } else if (!IsAsciiPrintable(c)) {
+ out.printf("\\u%04x", c);
+ } else {
+ out.printf("%c", char(c));
+ }
+ }
+}
+
+static void QuoteString(GenericPrinter& out, JSLinearString* str) {
+ JS::AutoCheckCannotGC nogc;
+
+ // Limit the string length to reduce the JSON file size.
+ size_t length = std::min(str->length(), size_t(128));
+ if (str->hasLatin1Chars()) {
+ QuoteString(out, str->latin1Chars(nogc), length);
+ } else {
+ QuoteString(out, str->twoByteChars(nogc), length);
+ }
+}
+
+void CacheIRSpewer::valueProperty(const char* name, const Value& v) {
+ MOZ_ASSERT(enabled());
+ JSONPrinter& j = json_.ref();
+
+ j.beginObjectProperty(name);
+
+ const char* type = InformalValueTypeName(v);
+ if (v.isInt32()) {
+ type = "int32";
+ }
+ j.property("type", type);
+
+ if (v.isInt32()) {
+ j.property("value", v.toInt32());
+ } else if (v.isDouble()) {
+ j.floatProperty("value", v.toDouble(), 3);
+ } else if (v.isString() || v.isSymbol()) {
+ JSString* str = v.isString() ? v.toString() : v.toSymbol()->description();
+ if (str && str->isLinear()) {
+ j.beginStringProperty("value");
+ QuoteString(output_, &str->asLinear());
+ j.endStringProperty();
+ }
+ } else if (v.isObject()) {
+ JSObject& object = v.toObject();
+ j.formatProperty("value", "%p (shape: %p)", &object, object.shape());
+
+ if (object.is<JSFunction>()) {
+ if (JSAtom* name = object.as<JSFunction>().displayAtom()) {
+ j.beginStringProperty("funName");
+ QuoteString(output_, name);
+ j.endStringProperty();
+ }
+ }
+
+ if (NativeObject* nobj =
+ object.isNative() ? &object.as<NativeObject>() : nullptr) {
+ j.beginListProperty("flags");
+ {
+ if (nobj->isIndexed()) {
+ j.value("indexed");
+ }
+ if (nobj->inDictionaryMode()) {
+ j.value("dictionaryMode");
+ }
+ }
+ j.endList();
+ if (nobj->isIndexed()) {
+ j.beginObjectProperty("indexed");
+ {
+ j.property("denseInitializedLength",
+ nobj->getDenseInitializedLength());
+ j.property("denseCapacity", nobj->getDenseCapacity());
+ j.property("denseElementsAreSealed", nobj->denseElementsAreSealed());
+ j.property("denseElementsAreFrozen", nobj->denseElementsAreFrozen());
+ }
+ j.endObject();
+ }
+ }
+ }
+
+ j.endObject();
+}
+
+void CacheIRSpewer::opcodeProperty(const char* name, const JSOp op) {
+ MOZ_ASSERT(enabled());
+ JSONPrinter& j = json_.ref();
+
+ j.beginStringProperty(name);
+ output_.put(CodeName(op));
+ j.endStringProperty();
+}
+
+void CacheIRSpewer::cacheIRSequence(CacheIRReader& reader) {
+ MOZ_ASSERT(enabled());
+ JSONPrinter& j = json_.ref();
+
+ j.beginListProperty("cacheIR");
+
+ CacheIROpsJSONSpewer spewer(j);
+ spewer.spew(reader);
+
+ j.endList();
+}
+
+void CacheIRSpewer::attached(const char* name) {
+ MOZ_ASSERT(enabled());
+ json_.ref().property("attached", name);
+}
+
+void CacheIRSpewer::endCache() {
+ MOZ_ASSERT(enabled());
+ json_.ref().endObject();
+}
+
+#endif /* JS_CACHEIR_SPEW */