summaryrefslogtreecommitdiffstats
path: root/js/public/experimental/JitInfo.h
blob: 1b070021bd12d07c9ef18fa593e2a1b0762ddec2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
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