Bug 1439855 - Fast lookup for BinAST string constants, shared among parsers;r?arai draft
authorDavid Teller <dteller@mozilla.com>
Thu, 05 Apr 2018 14:31:39 +0200
changeset 784327 bb8367cd28b26bc64f7f9bda7692c720388648d8
parent 784326 f44c887b9e74918150a359dd16ea4448639fa0be
child 784328 ec1dd6feef68f7d8fa55683cd9a5d72b24b1d44d
push id106893
push userdteller@mozilla.com
push dateWed, 18 Apr 2018 11:19:13 +0000
reviewersarai
bugs1439855
milestone61.0a1
Bug 1439855 - Fast lookup for BinAST string constants, shared among parsers;r?arai BinAST parsers need to perform frequent lookup for string values, e.g. once for each `let`, `var`, `const`, `+`, `-`, `instanceof` (string enums), as well as a number of startup lookups for string values such as "LiteralNullExpression", etc. (ast table) This patch introduces zero-copy lookup tables for both of these. These tables are shared among instances of parsers in a JSRuntime. MozReview-Commit-ID: 75BasAxLoha
js/src/frontend/BinSourceRuntimeSupport.h
js/src/frontend/BinToken.cpp
js/src/vm/Runtime.h
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/BinSourceRuntimeSupport.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 frontend_BinSourceSupport_h
+#define frontend_BinSourceSupport_h
+
+#include "mozilla/HashFunctions.h"
+#include "mozilla/Maybe.h"
+
+#include "jsapi.h"
+
+#include "frontend/BinToken.h"
+
+#include "js/HashTable.h"
+#include "js/Result.h"
+
+namespace js {
+
+// Support for parsing JS Binary ASTs.
+struct BinaryASTSupport {
+    using BinVariant  = js::frontend::BinVariant;
+    using BinField = js::frontend::BinField;
+    using BinKind  = js::frontend::BinKind;
+
+    // A structure designed to perform fast char* + length lookup
+    // without copies.
+    struct CharSlice {
+        const char* start_;
+        uint32_t byteLen_;
+        CharSlice(const CharSlice& other)
+            : start_(other.start_)
+            , byteLen_(other.byteLen_)
+        {  }
+        CharSlice(const char* start, const uint32_t byteLen)
+            : start_(start)
+            , byteLen_(byteLen)
+        { }
+        const char* begin() const {
+            return start_;
+        }
+        const char* end() const {
+            return start_ + byteLen_;
+        }
+#ifdef DEBUG
+        void dump() const {
+            for (auto c: *this)
+                fprintf(stderr, "%c", c);
+
+            fprintf(stderr, " (%d)", byteLen_);
+        }
+#endif // DEBUG
+
+        typedef const CharSlice Lookup;
+        static js::HashNumber hash(Lookup l) {
+            return mozilla::HashString(l.start_, l.byteLen_);
+        }
+        static bool match(const Lookup key, Lookup lookup) {
+            if (key.byteLen_ != lookup.byteLen_)
+                return false;
+            return strncmp(key.start_, lookup.start_, key.byteLen_) == 0;
+        }
+    };
+
+    JS::Result<const BinVariant*>  binVariant(JSContext*, const CharSlice);
+    JS::Result<const BinField*> binField(JSContext*, const CharSlice);
+    JS::Result<const BinKind*> binKind(JSContext*,  const CharSlice);
+
+  private:
+    // A HashMap that can be queried without copies from a CharSlice key.
+    // Initialized on first call. Keys are CharSlices into static strings.
+    using BinKindMap = js::HashMap<const CharSlice, BinKind, CharSlice, js::SystemAllocPolicy>;
+    BinKindMap binKindMap_;
+
+    using BinFieldMap = js::HashMap<const CharSlice, BinField, CharSlice, js::SystemAllocPolicy>;
+    BinFieldMap binFieldMap_;
+
+    using BinVariantMap = js::HashMap<const CharSlice, BinVariant, CharSlice, js::SystemAllocPolicy>;
+    BinVariantMap binVariantMap_;
+
+};
+
+} // namespace js
+
+#endif // frontend_BinSourceSupport_h
--- a/js/src/frontend/BinToken.cpp
+++ b/js/src/frontend/BinToken.cpp
@@ -1,35 +1,126 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 #include "frontend/BinToken.h"
 
+#include "jsapi.h"
+
+#include "mozilla/Maybe.h"
+
+#include "frontend/BinSourceRuntimeSupport.h"
+#include "frontend/TokenStream.h"
+#include "gc/Zone.h"
+
 #include <sys/types.h>
 
 namespace js {
 namespace frontend {
 
-const char* BINKIND_DESCRIPTIONS[] = {
-#define WITH_KIND(_, SPEC_NAME) #SPEC_NAME,
-    FOR_EACH_BIN_KIND(WITH_KIND)
-#undef WITH_KIND
+const BinaryASTSupport::CharSlice BINKIND_DESCRIPTIONS[] = {
+#define WITH_VARIANT(_, SPEC_NAME) BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
+    FOR_EACH_BIN_KIND(WITH_VARIANT)
+#undef WITH_VARIANT
+};
+
+const BinaryASTSupport::CharSlice BINFIELD_DESCRIPTIONS[] = {
+#define WITH_VARIANT(_, SPEC_NAME) BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
+    FOR_EACH_BIN_FIELD(WITH_VARIANT)
+#undef WITH_VARIANT
+};
+
+const BinaryASTSupport::CharSlice BINVARIANT_DESCRIPTIONS[] = {
+#define WITH_VARIANT(_, SPEC_NAME) BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
+    FOR_EACH_BIN_VARIANT(WITH_VARIANT)
+#undef WITH_VARIANT
 };
 
-const char* BINFIELD_DESCRIPTIONS[] = {
-    #define WITH_FIELD(_, SPEC_NAME) #SPEC_NAME,
-        FOR_EACH_BIN_FIELD(WITH_FIELD)
-    #undef WITH_FIELD
-};
+const BinaryASTSupport::CharSlice&
+getBinKind(const BinKind& variant)
+{
+    return BINKIND_DESCRIPTIONS[static_cast<size_t>(variant)];
+}
 
-const char* describeBinKind(const BinKind& kind) {
-    return BINKIND_DESCRIPTIONS[static_cast<size_t>(kind)];
+const BinaryASTSupport::CharSlice&
+getBinVariant(const BinVariant& variant)
+{
+    return BINVARIANT_DESCRIPTIONS[static_cast<size_t>(variant)];
 }
 
-const char* describeBinField(const BinField& field) {
-    return BINFIELD_DESCRIPTIONS[static_cast<size_t>(field)];
+const BinaryASTSupport::CharSlice&
+getBinField(const BinField& variant)
+{
+    return BINFIELD_DESCRIPTIONS[static_cast<size_t>(variant)];
+}
+
+const char* describeBinKind(const BinKind& variant)
+{
+    return getBinKind(variant).begin();
+}
+
+const char* describeBinField(const BinField& variant)
+{
+    return getBinField(variant).begin();
+}
+
+const char* describeBinVariant(const BinVariant& variant)
+{
+    return getBinVariant(variant).begin();
 }
 
 } // namespace frontend
+
+
+JS::Result<const js::frontend::BinKind*>
+BinaryASTSupport::binKind(JSContext* cx, const CharSlice key)
+{
+    if (!binKindMap_.initialized()) {
+        // Initialize lazily.
+        if (!binKindMap_.init(frontend::BINKIND_LIMIT))
+            return cx->alreadyReportedError();
+
+        for (size_t i = 0; i < frontend::BINKIND_LIMIT; ++i) {
+            const BinKind variant = static_cast<BinKind>(i);
+            const CharSlice& key = getBinKind(variant);
+            auto ptr = binKindMap_.lookupForAdd(key);
+            MOZ_ASSERT(!ptr);
+            if (!binKindMap_.add(ptr, key, variant))
+                return cx->alreadyReportedError();
+        }
+    }
+
+    auto ptr = binKindMap_.lookup(key);
+    if (!ptr)
+        return nullptr;
+
+    return &ptr->value();
+}
+
+JS::Result<const js::frontend::BinVariant*>
+BinaryASTSupport::binVariant(JSContext* cx, const CharSlice key) {
+    if (!binVariantMap_.initialized()) {
+        // Initialize lazily.
+        if (!binVariantMap_.init(frontend::BINVARIANT_LIMIT))
+            return cx->alreadyReportedError();
+
+        for (size_t i = 0; i < frontend::BINVARIANT_LIMIT; ++i) {
+            const BinVariant variant = static_cast<BinVariant>(i);
+            const CharSlice& key = getBinVariant(variant);
+            auto ptr = binVariantMap_.lookupForAdd(key);
+            MOZ_ASSERT(!ptr);
+            if (!binVariantMap_.add(ptr, key, variant))
+                return cx->alreadyReportedError();
+        }
+    }
+
+
+    auto ptr = binVariantMap_.lookup(key);
+    if (!ptr)
+        return nullptr;
+
+    return &ptr->value();
+}
+
 } // namespace js
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -19,16 +19,17 @@
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Vector.h"
 
 #include <setjmp.h>
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/Promise.h"
+#include "frontend/BinSourceRuntimeSupport.h"
 #include "frontend/NameCollections.h"
 #include "gc/GCRuntime.h"
 #include "gc/Tracer.h"
 #include "irregexp/RegExpStack.h"
 #include "js/Debug.h"
 #include "js/GCVector.h"
 #include "js/HashTable.h"
 #ifdef DEBUG
@@ -932,16 +933,25 @@ struct JSRuntime : public js::MallocProv
     js::RuntimeCaches& caches() { return caches_.ref(); }
 
     // List of all the live wasm::Instances in the runtime. Equal to the union
     // of all instances registered in all JSCompartments. Accessed from watchdog
     // threads for purposes of wasm::InterruptRunningCode().
     js::ExclusiveData<js::wasm::InstanceVector> wasmInstances;
 
   public:
+#if defined(JS_BUILD_BINAST)
+    js::BinaryASTSupport& binast() {
+        return binast_;
+    }
+  private:
+    js::BinaryASTSupport binast_;
+#endif // defined(JS_BUILD_BINAST)
+
+  public:
 #if defined(NIGHTLY_BUILD)
     // Support for informing the embedding of any error thrown.
     // This mechanism is designed to let the embedding
     // log/report/fail in case certain errors are thrown
     // (e.g. SyntaxError, ReferenceError or TypeError
     // in critical code).
     struct ErrorInterceptionSupport {
         ErrorInterceptionSupport()