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