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