340 lines
11 KiB
C++
340 lines
11 KiB
C++
/* -*- 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 "mozilla/Attributes.h" // MOZ_NON_PARAM
|
|
|
|
#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;
|
|
enum class TrampolineNative : 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 MOZ_NON_PARAM 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,
|
|
TrampolineNative,
|
|
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;
|
|
js::jit::TrampolineNative trampolineNative;
|
|
};
|
|
|
|
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
|