WarpBuilderShared.h (12747B)
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 jit_WarpBuilderShared_h 8 #define jit_WarpBuilderShared_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/Maybe.h" 12 13 #include "jit/MIRGraph.h" 14 #include "js/Value.h" 15 16 namespace js { 17 18 class BytecodeLocation; 19 20 namespace jit { 21 22 class MBasicBlock; 23 class MCall; 24 class MConstant; 25 class MInstruction; 26 class MIRGenerator; 27 class TempAllocator; 28 class WarpSnapshot; 29 class WrappedFunction; 30 31 // Helper class to manage call state. 32 class MOZ_STACK_CLASS CallInfo { 33 MDefinition* callee_ = nullptr; 34 MDefinition* thisArg_ = nullptr; 35 MDefinition* newTargetArg_ = nullptr; 36 MDefinitionVector args_; 37 38 bool constructing_; 39 40 // True if the caller does not use the return value. 41 bool ignoresReturnValue_; 42 43 bool inlined_ = false; 44 bool setter_ = false; 45 46 public: 47 // For normal calls and FunCall we can shuffle around definitions in 48 // the CallInfo and use a normal MCall. For others, we need to use a 49 // specialized call. 50 enum class ArgFormat { 51 Standard, 52 Array, 53 FunApplyArgsObj, 54 }; 55 56 private: 57 ArgFormat argFormat_ = ArgFormat::Standard; 58 mozilla::Maybe<ResumeMode> inliningMode_; 59 60 public: 61 CallInfo(TempAllocator& alloc, bool constructing, bool ignoresReturnValue, 62 jsbytecode* pc = nullptr) 63 : args_(alloc), 64 constructing_(constructing), 65 ignoresReturnValue_(ignoresReturnValue) {} 66 67 [[nodiscard]] bool init(MBasicBlock* current, uint32_t argc) { 68 MOZ_ASSERT(args_.empty()); 69 70 // Get the arguments in the right order 71 if (!args_.reserve(argc)) { 72 return false; 73 } 74 75 if (constructing()) { 76 setNewTarget(current->pop()); 77 } 78 79 for (int32_t i = argc; i > 0; i--) { 80 args_.infallibleAppend(current->peek(-i)); 81 } 82 current->popn(argc); 83 84 // Get |this| and |callee| 85 setThis(current->pop()); 86 setCallee(current->pop()); 87 88 return true; 89 } 90 91 void initForSpreadCall(MBasicBlock* current) { 92 MOZ_ASSERT(args_.empty()); 93 94 if (constructing()) { 95 setNewTarget(current->pop()); 96 } 97 98 // Spread calls have one argument, an Array object containing the args. 99 static_assert(decltype(args_)::InlineLength >= 1, 100 "Appending one argument should be infallible"); 101 MOZ_ALWAYS_TRUE(args_.append(current->pop())); 102 103 // Get |this| and |callee| 104 setThis(current->pop()); 105 setCallee(current->pop()); 106 107 argFormat_ = ArgFormat::Array; 108 } 109 110 void initForGetterCall(MDefinition* callee, MDefinition* thisVal) { 111 MOZ_ASSERT(args_.empty()); 112 setCallee(callee); 113 setThis(thisVal); 114 } 115 116 void initForProxyGet(MDefinition* callee, MDefinition* handler, 117 MDefinition* target, MDefinition* id, 118 MDefinition* receiver) { 119 MOZ_ASSERT(args_.empty()); 120 setCallee(callee); 121 setThis(handler); 122 static_assert(decltype(args_)::InlineLength >= 3, 123 "Appending three arguments should be infallible"); 124 MOZ_ALWAYS_TRUE(args_.append(target)); 125 MOZ_ALWAYS_TRUE(args_.append(id)); 126 MOZ_ALWAYS_TRUE(args_.append(receiver)); 127 } 128 129 void initForSetterCall(MDefinition* callee, MDefinition* thisVal, 130 MDefinition* rhs) { 131 MOZ_ASSERT(args_.empty()); 132 markAsSetter(); 133 setCallee(callee); 134 setThis(thisVal); 135 static_assert(decltype(args_)::InlineLength >= 1, 136 "Appending one argument should be infallible"); 137 MOZ_ALWAYS_TRUE(args_.append(rhs)); 138 } 139 140 void initForApplyInlinedArgs(MDefinition* callee, MDefinition* thisVal, 141 uint32_t numActuals) { 142 MOZ_ASSERT(args_.empty()); 143 MOZ_ASSERT(!constructing_); 144 145 setCallee(callee); 146 setThis(thisVal); 147 148 MOZ_ASSERT(numActuals <= ArgumentsObject::MaxInlinedArgs); 149 static_assert( 150 ArgumentsObject::MaxInlinedArgs <= decltype(args_)::InlineLength, 151 "Actual arguments can be infallibly stored inline"); 152 MOZ_ALWAYS_TRUE(args_.reserve(numActuals)); 153 } 154 155 [[nodiscard]] bool initForApplyArray(MDefinition* callee, 156 MDefinition* thisVal, 157 uint32_t numActuals) { 158 MOZ_ASSERT(args_.empty()); 159 MOZ_ASSERT(!constructing_); 160 161 setCallee(callee); 162 setThis(thisVal); 163 164 return args_.reserve(numActuals); 165 } 166 167 [[nodiscard]] bool initForConstructArray(MDefinition* callee, 168 MDefinition* thisVal, 169 MDefinition* newTarget, 170 uint32_t numActuals) { 171 MOZ_ASSERT(args_.empty()); 172 MOZ_ASSERT(constructing_); 173 174 setCallee(callee); 175 setThis(thisVal); 176 setNewTarget(newTarget); 177 178 return args_.reserve(numActuals); 179 } 180 181 void initForCloseIter(MDefinition* iter, MDefinition* callee) { 182 MOZ_ASSERT(args_.empty()); 183 setCallee(callee); 184 setThis(iter); 185 } 186 187 void popCallStack(MBasicBlock* current) { current->popn(numFormals()); } 188 189 [[nodiscard]] bool pushCallStack(MBasicBlock* current) { 190 current->push(callee()); 191 current->push(thisArg()); 192 193 for (uint32_t i = 0; i < argc(); i++) { 194 current->push(getArg(i)); 195 } 196 197 if (constructing()) { 198 current->push(getNewTarget()); 199 } 200 201 return true; 202 } 203 204 uint32_t argc() const { return args_.length(); } 205 uint32_t numFormals() const { return argc() + 2 + constructing(); } 206 207 [[nodiscard]] bool setArgs(const MDefinitionVector& args) { 208 MOZ_ASSERT(args_.empty()); 209 return args_.appendAll(args); 210 } 211 212 MDefinitionVector& argv() { return args_; } 213 214 const MDefinitionVector& argv() const { return args_; } 215 216 MDefinition* getArg(uint32_t i) const { 217 MOZ_ASSERT(i < argc()); 218 return args_[i]; 219 } 220 221 void initArg(uint32_t i, MDefinition* def) { 222 MOZ_ASSERT(i == argc()); 223 args_.infallibleAppend(def); 224 } 225 226 void setArg(uint32_t i, MDefinition* def) { 227 MOZ_ASSERT(i < argc()); 228 args_[i] = def; 229 } 230 231 void removeArg(uint32_t i) { args_.erase(&args_[i]); } 232 233 MDefinition* thisArg() const { 234 MOZ_ASSERT(thisArg_); 235 return thisArg_; 236 } 237 238 void setThis(MDefinition* thisArg) { thisArg_ = thisArg; } 239 240 bool constructing() const { return constructing_; } 241 242 bool ignoresReturnValue() const { return ignoresReturnValue_; } 243 244 void setNewTarget(MDefinition* newTarget) { 245 MOZ_ASSERT(constructing()); 246 newTargetArg_ = newTarget; 247 } 248 MDefinition* getNewTarget() const { 249 MOZ_ASSERT(newTargetArg_); 250 return newTargetArg_; 251 } 252 253 bool isSetter() const { return setter_; } 254 void markAsSetter() { setter_ = true; } 255 256 bool isInlined() const { return inlined_; } 257 void markAsInlined() { inlined_ = true; } 258 259 ResumeMode inliningResumeMode() const { 260 MOZ_ASSERT(isInlined()); 261 return *inliningMode_; 262 } 263 264 void setInliningResumeMode(ResumeMode mode) { 265 MOZ_ASSERT(isInlined()); 266 MOZ_ASSERT(inliningMode_.isNothing()); 267 inliningMode_.emplace(mode); 268 } 269 270 MDefinition* callee() const { 271 MOZ_ASSERT(callee_); 272 return callee_; 273 } 274 275 void setCallee(MDefinition* callee) { callee_ = callee; } 276 277 template <typename Fun> 278 void forEachCallOperand(Fun& f) { 279 f(callee_); 280 f(thisArg_); 281 if (newTargetArg_) { 282 f(newTargetArg_); 283 } 284 for (uint32_t i = 0; i < argc(); i++) { 285 f(getArg(i)); 286 } 287 } 288 289 // Prepend `numArgs` arguments. Calls `f(i)` for each new argument. 290 template <typename Fun> 291 [[nodiscard]] bool prependArgs(size_t numArgs, const Fun& f) { 292 size_t numArgsBefore = args_.length(); 293 if (!args_.growBy(numArgs)) { 294 return false; 295 } 296 for (size_t i = numArgsBefore; i > 0; i--) { 297 args_[numArgs + i - 1] = args_[i - 1]; 298 } 299 for (size_t i = 0; i < numArgs; i++) { 300 args_[i] = f(i); 301 } 302 return true; 303 } 304 305 void setImplicitlyUsedUnchecked() { 306 auto setFlag = [](MDefinition* def) { def->setImplicitlyUsedUnchecked(); }; 307 forEachCallOperand(setFlag); 308 } 309 310 ArgFormat argFormat() const { return argFormat_; } 311 void setArgFormat(ArgFormat argFormat) { argFormat_ = argFormat; } 312 313 MDefinition* arrayArg() const { 314 MOZ_ASSERT(argFormat_ == ArgFormat::Array); 315 // The array argument for a spread call or FunApply is always the last 316 // argument. 317 return getArg(argc() - 1); 318 } 319 }; 320 321 template <typename Undef> 322 MCall* MakeCall(TempAllocator& alloc, Undef addUndefined, CallInfo& callInfo, 323 bool needsThisCheck, WrappedFunction* target, bool isDOMCall, 324 gc::Heap initialHeap = gc::Heap::Default) { 325 MOZ_ASSERT(callInfo.argFormat() == CallInfo::ArgFormat::Standard); 326 MOZ_ASSERT_IF(needsThisCheck, !target); 327 MOZ_ASSERT_IF(isDOMCall, target->jitInfo()->type() == JSJitInfo::Method); 328 329 mozilla::Maybe<DOMObjectKind> objKind; 330 mozilla::Maybe<gc::Heap> heap; 331 if (isDOMCall) { 332 const Shape* shape = callInfo.thisArg()->toGuardShape()->shape(); 333 MOZ_ASSERT(shape->getObjectClass()->isDOMClass()); 334 if (shape->isNative()) { 335 objKind.emplace(DOMObjectKind::Native); 336 } else { 337 MOZ_ASSERT(shape->isProxy()); 338 objKind.emplace(DOMObjectKind::Proxy); 339 } 340 341 heap.emplace(initialHeap); 342 } 343 344 uint32_t targetArgs = callInfo.argc(); 345 346 // Collect number of missing arguments provided that the target is 347 // scripted. Native functions are passed an explicit 'argc' parameter. 348 if (target && target->hasJitEntry()) { 349 targetArgs = std::max<uint32_t>(target->nargs(), callInfo.argc()); 350 } 351 352 MCall* call = 353 MCall::New(alloc, target, targetArgs + 1 + callInfo.constructing(), 354 callInfo.argc(), callInfo.constructing(), 355 callInfo.ignoresReturnValue(), isDOMCall, objKind, heap); 356 if (!call) { 357 return nullptr; 358 } 359 360 if (callInfo.constructing()) { 361 // Note: setThis should have been done by the caller of makeCall. 362 if (needsThisCheck) { 363 call->setNeedsThisCheck(); 364 } 365 366 // Pass |new.target| 367 call->addArg(targetArgs + 1, callInfo.getNewTarget()); 368 } 369 370 // Explicitly pad any missing arguments with |undefined|. 371 MOZ_ASSERT_IF(target && targetArgs > callInfo.argc(), target->hasJitEntry()); 372 373 MConstant* undef = nullptr; 374 for (uint32_t i = targetArgs; i > callInfo.argc(); i--) { 375 if (!undef) { 376 undef = addUndefined(); 377 } 378 if (!alloc.ensureBallast()) { 379 return nullptr; 380 } 381 call->addArg(i, undef); 382 } 383 384 // Add explicit arguments. 385 // Skip addArg(0) because it is reserved for |this|. 386 for (int32_t i = callInfo.argc() - 1; i >= 0; i--) { 387 call->addArg(i + 1, callInfo.getArg(i)); 388 } 389 390 if (isDOMCall) { 391 // Now that we've told it about all the args, compute whether it's movable 392 call->computeMovable(); 393 } 394 395 // Pass |this| and callee. 396 call->addArg(0, callInfo.thisArg()); 397 call->initCallee(callInfo.callee()); 398 399 if (target) { 400 // The callee must be a JSFunction so we don't need a Class check. 401 call->disableClassCheck(); 402 } 403 404 return call; 405 } 406 407 // Base class for code sharing between WarpBuilder and WarpCacheIRTranspiler. 408 // Because this code is used by WarpCacheIRTranspiler we should 409 // generally assume that we only have access to the current basic block. 410 class WarpBuilderShared { 411 WarpSnapshot& snapshot_; 412 MIRGenerator& mirGen_; 413 TempAllocator& alloc_; 414 415 protected: 416 MBasicBlock* current; 417 418 WarpBuilderShared(WarpSnapshot& snapshot, MIRGenerator& mirGen, 419 MBasicBlock* current_); 420 421 [[nodiscard]] bool resumeAfter(MInstruction* ins, BytecodeLocation loc); 422 423 MConstant* constant(const JS::Value& v); 424 void pushConstant(const JS::Value& v); 425 426 // Note: unboxObjectInfallible defaults to adding a non-movable MUnbox to 427 // ensure we don't hoist the infallible unbox before a branch checking the 428 // value type. 429 enum class IsMovable : bool { No, Yes }; 430 MDefinition* unboxObjectInfallible(MDefinition* def, 431 IsMovable movable = IsMovable::No); 432 433 MCall* makeCall(CallInfo& callInfo, bool needsThisCheck, 434 WrappedFunction* target = nullptr, bool isDOMCall = false, 435 gc::Heap initialHeap = gc::Heap::Default); 436 MInstruction* makeSpreadCall(CallInfo& callInfo, bool needsThisCheck, 437 bool isSameRealm = false, 438 WrappedFunction* target = nullptr); 439 440 public: 441 MBasicBlock* currentBlock() const { return current; } 442 WarpSnapshot& snapshot() const { return snapshot_; } 443 MIRGenerator& mirGen() { return mirGen_; } 444 TempAllocator& alloc() { return alloc_; } 445 }; 446 447 } // namespace jit 448 } // namespace js 449 450 #endif