diff options
Diffstat (limited to '')
-rw-r--r-- | js/public/experimental/JitInfo.h | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/js/public/experimental/JitInfo.h b/js/public/experimental/JitInfo.h new file mode 100644 index 0000000000..1b070021bd --- /dev/null +++ b/js/public/experimental/JitInfo.h @@ -0,0 +1,336 @@ +/* -*- 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 js_experimental_JitInfo_h +#define js_experimental_JitInfo_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include <stddef.h> // size_t +#include <stdint.h> // uint16_t, uint32_t + +#include "js/CallArgs.h" // JS::CallArgs, JS::detail::CallArgsBase, JSNative +#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted +#include "js/Value.h" // JS::Value, JSValueType + +namespace js { + +namespace jit { + +enum class InlinableNative : uint16_t; + +} // namespace jit + +} // namespace js + +/** + * A class, expected to be passed by value, which represents the CallArgs for a + * JSJitGetterOp. + */ +class JSJitGetterCallArgs : protected JS::MutableHandle<JS::Value> { + public: + explicit JSJitGetterCallArgs(const JS::CallArgs& args) + : JS::MutableHandle<JS::Value>(args.rval()) {} + + explicit JSJitGetterCallArgs(JS::Rooted<JS::Value>* rooted) + : JS::MutableHandle<JS::Value>(rooted) {} + + explicit JSJitGetterCallArgs(JS::MutableHandle<JS::Value> handle) + : JS::MutableHandle<JS::Value>(handle) {} + + JS::MutableHandle<JS::Value> rval() { return *this; } +}; + +/** + * A class, expected to be passed by value, which represents the CallArgs for a + * JSJitSetterOp. + */ +class JSJitSetterCallArgs : protected JS::MutableHandle<JS::Value> { + public: + explicit JSJitSetterCallArgs(const JS::CallArgs& args) + : JS::MutableHandle<JS::Value>(args[0]) {} + + explicit JSJitSetterCallArgs(JS::Rooted<JS::Value>* rooted) + : JS::MutableHandle<JS::Value>(rooted) {} + + JS::MutableHandle<JS::Value> operator[](unsigned i) { + MOZ_ASSERT(i == 0); + return *this; + } + + unsigned length() const { return 1; } + + // Add get() or maybe hasDefined() as needed +}; + +struct JSJitMethodCallArgsTraits; + +/** + * A class, expected to be passed by reference, which represents the CallArgs + * for a JSJitMethodOp. + */ +class JSJitMethodCallArgs + : protected JS::detail::CallArgsBase<JS::detail::NoUsedRval> { + private: + using Base = JS::detail::CallArgsBase<JS::detail::NoUsedRval>; + friend struct JSJitMethodCallArgsTraits; + + public: + explicit JSJitMethodCallArgs(const JS::CallArgs& args) { + argv_ = args.array(); + argc_ = args.length(); + } + + JS::MutableHandle<JS::Value> rval() const { return Base::rval(); } + + unsigned length() const { return Base::length(); } + + JS::MutableHandle<JS::Value> operator[](unsigned i) const { + return Base::operator[](i); + } + + bool hasDefined(unsigned i) const { return Base::hasDefined(i); } + + JSObject& callee() const { + // We can't use Base::callee() because that will try to poke at + // this->usedRval_, which we don't have. + return argv_[-2].toObject(); + } + + JS::Handle<JS::Value> get(unsigned i) const { return Base::get(i); } + + bool requireAtLeast(JSContext* cx, const char* fnname, + unsigned required) const { + // Can just forward to Base, since it only needs the length and we + // forward that already. + return Base::requireAtLeast(cx, fnname, required); + } +}; + +struct JSJitMethodCallArgsTraits { + static constexpr size_t offsetOfArgv = offsetof(JSJitMethodCallArgs, argv_); + static constexpr size_t offsetOfArgc = offsetof(JSJitMethodCallArgs, argc_); +}; + +using JSJitGetterOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*, + JSJitGetterCallArgs); +using JSJitSetterOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*, + JSJitSetterCallArgs); +using JSJitMethodOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*, + const JSJitMethodCallArgs&); + +/** + * This struct contains metadata passed from the DOM to the JS Engine for JIT + * optimizations on DOM property accessors. + * + * Eventually, this should be made available to general JSAPI users as *not* + * experimental and *not* a friend API, but we're not ready to do so yet. + */ +class JSJitInfo { + public: + enum OpType { + Getter, + Setter, + Method, + StaticMethod, + InlinableNative, + IgnoresReturnValueNative, + // Must be last + OpTypeCount + }; + + enum ArgType { + // Basic types + String = (1 << 0), + Integer = (1 << 1), // Only 32-bit or less + Double = (1 << 2), // Maybe we want to add Float sometime too + Boolean = (1 << 3), + Object = (1 << 4), + Null = (1 << 5), + + // And derived types + Numeric = Integer | Double, + // Should "Primitive" use the WebIDL definition, which + // excludes string and null, or the typical JS one that includes them? + Primitive = Numeric | Boolean | Null | String, + ObjectOrNull = Object | Null, + Any = ObjectOrNull | Primitive, + + // Our sentinel value. + ArgTypeListEnd = (1 << 31) + }; + + static_assert(Any & String, "Any must include String"); + static_assert(Any & Integer, "Any must include Integer"); + static_assert(Any & Double, "Any must include Double"); + static_assert(Any & Boolean, "Any must include Boolean"); + static_assert(Any & Object, "Any must include Object"); + static_assert(Any & Null, "Any must include Null"); + + /** + * An enum that describes what this getter/setter/method aliases. This + * determines what things can be hoisted past this call, and if this + * call is movable what it can be hoisted past. + */ + enum AliasSet { + /** + * Alias nothing: a constant value, getting it can't affect any other + * values, nothing can affect it. + */ + AliasNone, + + /** + * Alias things that can modify the DOM but nothing else. Doing the + * call can't affect the behavior of any other function. + */ + AliasDOMSets, + + /** + * Alias the world. Calling this can change arbitrary values anywhere + * in the system. Most things fall in this bucket. + */ + AliasEverything, + + /** Must be last. */ + AliasSetCount + }; + + bool needsOuterizedThisObject() const { + return type() != Getter && type() != Setter; + } + + bool isTypedMethodJitInfo() const { return isTypedMethod; } + + OpType type() const { return OpType(type_); } + + AliasSet aliasSet() const { return AliasSet(aliasSet_); } + + JSValueType returnType() const { return JSValueType(returnType_); } + + union { + JSJitGetterOp getter; + JSJitSetterOp setter; + JSJitMethodOp method; + /** A DOM static method, used for Promise wrappers */ + JSNative staticMethod; + JSNative ignoresReturnValueMethod; + }; + + static unsigned offsetOfIgnoresReturnValueNative() { + return offsetof(JSJitInfo, ignoresReturnValueMethod); + } + + union { + uint16_t protoID; + js::jit::InlinableNative inlinableNative; + }; + + union { + uint16_t depth; + + // Additional opcode for some InlinableNative functions. + uint16_t nativeOp; + }; + + // These fields are carefully packed to take up 4 bytes. If you need more + // bits for whatever reason, please see if you can steal bits from existing + // fields before adding more members to this structure. + static constexpr size_t OpTypeBits = 4; + static constexpr size_t AliasSetBits = 4; + static constexpr size_t ReturnTypeBits = 8; + static constexpr size_t SlotIndexBits = 10; + + /** The OpType that says what sort of function we are. */ + uint32_t type_ : OpTypeBits; + + /** + * The alias set for this op. This is a _minimal_ alias set; in + * particular for a method it does not include whatever argument + * conversions might do. That's covered by argTypes and runtime + * analysis of the actual argument types being passed in. + */ + uint32_t aliasSet_ : AliasSetBits; + + /** The return type tag. Might be JSVAL_TYPE_UNKNOWN. */ + uint32_t returnType_ : ReturnTypeBits; + + static_assert(OpTypeCount <= (1 << OpTypeBits), + "Not enough space for OpType"); + static_assert(AliasSetCount <= (1 << AliasSetBits), + "Not enough space for AliasSet"); + static_assert((sizeof(JSValueType) * 8) <= ReturnTypeBits, + "Not enough space for JSValueType"); + + /** Is op fallible? False in setters. */ + uint32_t isInfallible : 1; + + /** + * Is op movable? To be movable the op must + * not AliasEverything, but even that might + * not be enough (e.g. in cases when it can + * throw or is explicitly not movable). + */ + uint32_t isMovable : 1; + + /** + * Can op be dead-code eliminated? Again, this + * depends on whether the op can throw, in + * addition to the alias set. + */ + uint32_t isEliminatable : 1; + + // XXXbz should we have a JSValueType for the type of the member? + /** + * True if this is a getter that can always + * get the value from a slot of the "this" object. + */ + uint32_t isAlwaysInSlot : 1; + + /** + * True if this is a getter that can sometimes (if the slot doesn't contain + * UndefinedValue()) get the value from a slot of the "this" object. + */ + uint32_t isLazilyCachedInSlot : 1; + + /** True if this is an instance of JSTypedMethodJitInfo. */ + uint32_t isTypedMethod : 1; + + /** + * If isAlwaysInSlot or isSometimesInSlot is true, + * the index of the slot to get the value from. + * Otherwise 0. + */ + uint32_t slotIndex : SlotIndexBits; + + static constexpr size_t maxSlotIndex = (1 << SlotIndexBits) - 1; +}; + +static_assert(sizeof(JSJitInfo) == (sizeof(void*) + 2 * sizeof(uint32_t)), + "There are several thousand instances of JSJitInfo stored in " + "a binary. Please don't increase its space requirements without " + "verifying that there is no other way forward (better packing, " + "smaller datatypes for fields, subclassing, etc.)."); + +struct JSTypedMethodJitInfo { + // We use C-style inheritance here, rather than C++ style inheritance + // because not all compilers support brace-initialization for non-aggregate + // classes. Using C++ style inheritance and constructors instead of + // brace-initialization would also force the creation of static + // constructors (on some compilers) when JSJitInfo and JSTypedMethodJitInfo + // structures are declared. Since there can be several thousand of these + // structures present and we want to have roughly equivalent performance + // across a range of compilers, we do things manually. + JSJitInfo base; + + const JSJitInfo::ArgType* const argTypes; /* For a method, a list of sets of + types that the function + expects. This can be used, + for example, to figure out + when argument coercions can + have side-effects. */ +}; + +#endif // js_experimental_JitInfo_h |