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/. */
6 
7 #ifndef js_experimental_JitInfo_h
8 #define js_experimental_JitInfo_h
9 
10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
11 
12 #include <stddef.h>  // size_t
13 #include <stdint.h>  // uint16_t, uint32_t
14 
15 #include "js/CallArgs.h"    // JS::CallArgs, JS::detail::CallArgsBase, JSNative
16 #include "js/RootingAPI.h"  // JS::{,Mutable}Handle, JS::Rooted
17 #include "js/Value.h"       // JS::Value, JSValueType
18 
19 namespace js {
20 
21 namespace jit {
22 
23 enum class InlinableNative : uint16_t;
24 
25 }  // namespace jit
26 
27 }  // namespace js
28 
29 /**
30  * A class, expected to be passed by value, which represents the CallArgs for a
31  * JSJitGetterOp.
32  */
33 class JSJitGetterCallArgs : protected JS::MutableHandle<JS::Value> {
34  public:
JSJitGetterCallArgs(const JS::CallArgs & args)35   explicit JSJitGetterCallArgs(const JS::CallArgs& args)
36       : JS::MutableHandle<JS::Value>(args.rval()) {}
37 
JSJitGetterCallArgs(JS::Rooted<JS::Value> * rooted)38   explicit JSJitGetterCallArgs(JS::Rooted<JS::Value>* rooted)
39       : JS::MutableHandle<JS::Value>(rooted) {}
40 
JSJitGetterCallArgs(JS::MutableHandle<JS::Value> handle)41   explicit JSJitGetterCallArgs(JS::MutableHandle<JS::Value> handle)
42       : JS::MutableHandle<JS::Value>(handle) {}
43 
rval()44   JS::MutableHandle<JS::Value> rval() { return *this; }
45 };
46 
47 /**
48  * A class, expected to be passed by value, which represents the CallArgs for a
49  * JSJitSetterOp.
50  */
51 class JSJitSetterCallArgs : protected JS::MutableHandle<JS::Value> {
52  public:
JSJitSetterCallArgs(const JS::CallArgs & args)53   explicit JSJitSetterCallArgs(const JS::CallArgs& args)
54       : JS::MutableHandle<JS::Value>(args[0]) {}
55 
JSJitSetterCallArgs(JS::Rooted<JS::Value> * rooted)56   explicit JSJitSetterCallArgs(JS::Rooted<JS::Value>* rooted)
57       : JS::MutableHandle<JS::Value>(rooted) {}
58 
59   JS::MutableHandle<JS::Value> operator[](unsigned i) {
60     MOZ_ASSERT(i == 0);
61     return *this;
62   }
63 
length()64   unsigned length() const { return 1; }
65 
66   // Add get() or maybe hasDefined() as needed
67 };
68 
69 struct JSJitMethodCallArgsTraits;
70 
71 /**
72  * A class, expected to be passed by reference, which represents the CallArgs
73  * for a JSJitMethodOp.
74  */
75 class JSJitMethodCallArgs
76     : protected JS::detail::CallArgsBase<JS::detail::NoUsedRval> {
77  private:
78   using Base = JS::detail::CallArgsBase<JS::detail::NoUsedRval>;
79   friend struct JSJitMethodCallArgsTraits;
80 
81  public:
JSJitMethodCallArgs(const JS::CallArgs & args)82   explicit JSJitMethodCallArgs(const JS::CallArgs& args) {
83     argv_ = args.array();
84     argc_ = args.length();
85   }
86 
rval()87   JS::MutableHandle<JS::Value> rval() const { return Base::rval(); }
88 
length()89   unsigned length() const { return Base::length(); }
90 
91   JS::MutableHandle<JS::Value> operator[](unsigned i) const {
92     return Base::operator[](i);
93   }
94 
hasDefined(unsigned i)95   bool hasDefined(unsigned i) const { return Base::hasDefined(i); }
96 
callee()97   JSObject& callee() const {
98     // We can't use Base::callee() because that will try to poke at
99     // this->usedRval_, which we don't have.
100     return argv_[-2].toObject();
101   }
102 
get(unsigned i)103   JS::Handle<JS::Value> get(unsigned i) const { return Base::get(i); }
104 
requireAtLeast(JSContext * cx,const char * fnname,unsigned required)105   bool requireAtLeast(JSContext* cx, const char* fnname,
106                       unsigned required) const {
107     // Can just forward to Base, since it only needs the length and we
108     // forward that already.
109     return Base::requireAtLeast(cx, fnname, required);
110   }
111 };
112 
113 struct JSJitMethodCallArgsTraits {
114   static constexpr size_t offsetOfArgv = offsetof(JSJitMethodCallArgs, argv_);
115   static constexpr size_t offsetOfArgc = offsetof(JSJitMethodCallArgs, argc_);
116 };
117 
118 using JSJitGetterOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
119                                JSJitGetterCallArgs);
120 using JSJitSetterOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
121                                JSJitSetterCallArgs);
122 using JSJitMethodOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
123                                const JSJitMethodCallArgs&);
124 
125 /**
126  * This struct contains metadata passed from the DOM to the JS Engine for JIT
127  * optimizations on DOM property accessors.
128  *
129  * Eventually, this should be made available to general JSAPI users as *not*
130  * experimental and *not* a friend API, but we're not ready to do so yet.
131  */
132 class JSJitInfo {
133  public:
134   enum OpType {
135     Getter,
136     Setter,
137     Method,
138     StaticMethod,
139     InlinableNative,
140     IgnoresReturnValueNative,
141     // Must be last
142     OpTypeCount
143   };
144 
145   enum ArgType {
146     // Basic types
147     String = (1 << 0),
148     Integer = (1 << 1),  // Only 32-bit or less
149     Double = (1 << 2),   // Maybe we want to add Float sometime too
150     Boolean = (1 << 3),
151     Object = (1 << 4),
152     Null = (1 << 5),
153 
154     // And derived types
155     Numeric = Integer | Double,
156     // Should "Primitive" use the WebIDL definition, which
157     // excludes string and null, or the typical JS one that includes them?
158     Primitive = Numeric | Boolean | Null | String,
159     ObjectOrNull = Object | Null,
160     Any = ObjectOrNull | Primitive,
161 
162     // Our sentinel value.
163     ArgTypeListEnd = (1 << 31)
164   };
165 
166   static_assert(Any & String, "Any must include String");
167   static_assert(Any & Integer, "Any must include Integer");
168   static_assert(Any & Double, "Any must include Double");
169   static_assert(Any & Boolean, "Any must include Boolean");
170   static_assert(Any & Object, "Any must include Object");
171   static_assert(Any & Null, "Any must include Null");
172 
173   /**
174    * An enum that describes what this getter/setter/method aliases.  This
175    * determines what things can be hoisted past this call, and if this
176    * call is movable what it can be hoisted past.
177    */
178   enum AliasSet {
179     /**
180      * Alias nothing: a constant value, getting it can't affect any other
181      * values, nothing can affect it.
182      */
183     AliasNone,
184 
185     /**
186      * Alias things that can modify the DOM but nothing else.  Doing the
187      * call can't affect the behavior of any other function.
188      */
189     AliasDOMSets,
190 
191     /**
192      * Alias the world.  Calling this can change arbitrary values anywhere
193      * in the system.  Most things fall in this bucket.
194      */
195     AliasEverything,
196 
197     /** Must be last. */
198     AliasSetCount
199   };
200 
needsOuterizedThisObject()201   bool needsOuterizedThisObject() const {
202     return type() != Getter && type() != Setter;
203   }
204 
isTypedMethodJitInfo()205   bool isTypedMethodJitInfo() const { return isTypedMethod; }
206 
type()207   OpType type() const { return OpType(type_); }
208 
aliasSet()209   AliasSet aliasSet() const { return AliasSet(aliasSet_); }
210 
returnType()211   JSValueType returnType() const { return JSValueType(returnType_); }
212 
213   union {
214     JSJitGetterOp getter;
215     JSJitSetterOp setter;
216     JSJitMethodOp method;
217     /** A DOM static method, used for Promise wrappers */
218     JSNative staticMethod;
219     JSNative ignoresReturnValueMethod;
220   };
221 
offsetOfIgnoresReturnValueNative()222   static unsigned offsetOfIgnoresReturnValueNative() {
223     return offsetof(JSJitInfo, ignoresReturnValueMethod);
224   }
225 
226   union {
227     uint16_t protoID;
228     js::jit::InlinableNative inlinableNative;
229   };
230 
231   union {
232     uint16_t depth;
233 
234     // Additional opcode for some InlinableNative functions.
235     uint16_t nativeOp;
236   };
237 
238   // These fields are carefully packed to take up 4 bytes.  If you need more
239   // bits for whatever reason, please see if you can steal bits from existing
240   // fields before adding more members to this structure.
241   static constexpr size_t OpTypeBits = 4;
242   static constexpr size_t AliasSetBits = 4;
243   static constexpr size_t ReturnTypeBits = 8;
244   static constexpr size_t SlotIndexBits = 10;
245 
246   /** The OpType that says what sort of function we are. */
247   uint32_t type_ : OpTypeBits;
248 
249   /**
250    * The alias set for this op.  This is a _minimal_ alias set; in
251    * particular for a method it does not include whatever argument
252    * conversions might do.  That's covered by argTypes and runtime
253    * analysis of the actual argument types being passed in.
254    */
255   uint32_t aliasSet_ : AliasSetBits;
256 
257   /** The return type tag.  Might be JSVAL_TYPE_UNKNOWN. */
258   uint32_t returnType_ : ReturnTypeBits;
259 
260   static_assert(OpTypeCount <= (1 << OpTypeBits),
261                 "Not enough space for OpType");
262   static_assert(AliasSetCount <= (1 << AliasSetBits),
263                 "Not enough space for AliasSet");
264   static_assert((sizeof(JSValueType) * 8) <= ReturnTypeBits,
265                 "Not enough space for JSValueType");
266 
267   /** Is op fallible? False in setters. */
268   uint32_t isInfallible : 1;
269 
270   /**
271    * Is op movable?  To be movable the op must
272    * not AliasEverything, but even that might
273    * not be enough (e.g. in cases when it can
274    * throw or is explicitly not movable).
275    */
276   uint32_t isMovable : 1;
277 
278   /**
279    * Can op be dead-code eliminated? Again, this
280    * depends on whether the op can throw, in
281    * addition to the alias set.
282    */
283   uint32_t isEliminatable : 1;
284 
285   // XXXbz should we have a JSValueType for the type of the member?
286   /**
287    * True if this is a getter that can always
288    * get the value from a slot of the "this" object.
289    */
290   uint32_t isAlwaysInSlot : 1;
291 
292   /**
293    * True if this is a getter that can sometimes (if the slot doesn't contain
294    * UndefinedValue()) get the value from a slot of the "this" object.
295    */
296   uint32_t isLazilyCachedInSlot : 1;
297 
298   /** True if this is an instance of JSTypedMethodJitInfo. */
299   uint32_t isTypedMethod : 1;
300 
301   /**
302    * If isAlwaysInSlot or isSometimesInSlot is true,
303    * the index of the slot to get the value from.
304    * Otherwise 0.
305    */
306   uint32_t slotIndex : SlotIndexBits;
307 
308   static constexpr size_t maxSlotIndex = (1 << SlotIndexBits) - 1;
309 };
310 
311 static_assert(sizeof(JSJitInfo) == (sizeof(void*) + 2 * sizeof(uint32_t)),
312               "There are several thousand instances of JSJitInfo stored in "
313               "a binary. Please don't increase its space requirements without "
314               "verifying that there is no other way forward (better packing, "
315               "smaller datatypes for fields, subclassing, etc.).");
316 
317 struct JSTypedMethodJitInfo {
318   // We use C-style inheritance here, rather than C++ style inheritance
319   // because not all compilers support brace-initialization for non-aggregate
320   // classes. Using C++ style inheritance and constructors instead of
321   // brace-initialization would also force the creation of static
322   // constructors (on some compilers) when JSJitInfo and JSTypedMethodJitInfo
323   // structures are declared. Since there can be several thousand of these
324   // structures present and we want to have roughly equivalent performance
325   // across a range of compilers, we do things manually.
326   JSJitInfo base;
327 
328   const JSJitInfo::ArgType* const argTypes; /* For a method, a list of sets of
329                                                types that the function
330                                                expects.  This can be used,
331                                                for example, to figure out
332                                                when argument coercions can
333                                                have side-effects. */
334 };
335 
336 #endif  // js_experimental_JitInfo_h
337