tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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