tor-browser

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

WasmStubs.h (11619B)


      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 *
      4 * Copyright 2015 Mozilla Foundation
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 #ifndef wasm_stubs_h
     20 #define wasm_stubs_h
     21 
     22 #include "wasm/WasmFrameIter.h"  // js::wasm::ExitReason
     23 #include "wasm/WasmGenerator.h"
     24 #include "wasm/WasmOpIter.h"
     25 
     26 namespace js {
     27 namespace wasm {
     28 
     29 // ValType and location for a single result: either in a register or on the
     30 // stack.
     31 
     32 class ABIResult {
     33  ValType type_;
     34  enum class Location { Gpr, Gpr64, Fpr, Stack } loc_;
     35  union {
     36    jit::Register gpr_;
     37    jit::Register64 gpr64_;
     38    jit::FloatRegister fpr_;
     39    uint32_t stackOffset_;
     40  };
     41 
     42  void validate() {
     43 #ifdef DEBUG
     44    if (onStack()) {
     45      return;
     46    }
     47    MOZ_ASSERT(inRegister());
     48    switch (type_.kind()) {
     49      case ValType::I32:
     50        MOZ_ASSERT(loc_ == Location::Gpr);
     51        break;
     52      case ValType::I64:
     53        MOZ_ASSERT(loc_ == Location::Gpr64);
     54        break;
     55      case ValType::F32:
     56      case ValType::F64:
     57        MOZ_ASSERT(loc_ == Location::Fpr);
     58        break;
     59      case ValType::Ref:
     60        MOZ_ASSERT(loc_ == Location::Gpr);
     61        break;
     62      case ValType::V128:
     63        MOZ_ASSERT(loc_ == Location::Fpr);
     64        break;
     65    }
     66 #endif
     67  }
     68 
     69  friend class ABIResultIter;
     70  ABIResult() {}
     71 
     72 public:
     73  // Sizes of items in the stack area.
     74  //
     75  // The size values come from the implementations of Push() in
     76  // MacroAssembler-x86-shared.cpp and MacroAssembler-arm-shared.cpp, and from
     77  // VFPRegister::size() in Architecture-arm.h.
     78  //
     79  // On ARM unlike on x86 we push a single for float.
     80 
     81  static constexpr size_t StackSizeOfPtr = sizeof(intptr_t);
     82  static constexpr size_t StackSizeOfInt32 = StackSizeOfPtr;
     83  static constexpr size_t StackSizeOfInt64 = sizeof(int64_t);
     84 #if defined(JS_CODEGEN_ARM)
     85  static constexpr size_t StackSizeOfFloat = sizeof(float);
     86 #else
     87  static constexpr size_t StackSizeOfFloat = sizeof(double);
     88 #endif
     89  static constexpr size_t StackSizeOfDouble = sizeof(double);
     90 #ifdef ENABLE_WASM_SIMD
     91  static constexpr size_t StackSizeOfV128 = sizeof(V128);
     92 #endif
     93 
     94  ABIResult(ValType type, jit::Register gpr)
     95      : type_(type), loc_(Location::Gpr), gpr_(gpr) {
     96    validate();
     97  }
     98  ABIResult(ValType type, jit::Register64 gpr64)
     99      : type_(type), loc_(Location::Gpr64), gpr64_(gpr64) {
    100    validate();
    101  }
    102  ABIResult(ValType type, jit::FloatRegister fpr)
    103      : type_(type), loc_(Location::Fpr), fpr_(fpr) {
    104    validate();
    105  }
    106  ABIResult(ValType type, uint32_t stackOffset)
    107      : type_(type), loc_(Location::Stack), stackOffset_(stackOffset) {
    108    validate();
    109  }
    110 
    111  ValType type() const { return type_; }
    112  bool onStack() const { return loc_ == Location::Stack; }
    113  bool inRegister() const { return !onStack(); }
    114  jit::Register gpr() const {
    115    MOZ_ASSERT(loc_ == Location::Gpr);
    116    return gpr_;
    117  }
    118  jit::Register64 gpr64() const {
    119    MOZ_ASSERT(loc_ == Location::Gpr64);
    120    return gpr64_;
    121  }
    122  jit::FloatRegister fpr() const {
    123    MOZ_ASSERT(loc_ == Location::Fpr);
    124    return fpr_;
    125  }
    126  // Offset from SP.
    127  uint32_t stackOffset() const {
    128    MOZ_ASSERT(loc_ == Location::Stack);
    129    return stackOffset_;
    130  }
    131  uint32_t size() const;
    132 };
    133 
    134 // Just as WebAssembly functions can take multiple arguments, they can also
    135 // return multiple results.  As with a call, a limited number of results will be
    136 // located in registers, and the rest will be stored in a stack area.  The
    137 // |ABIResultIter| computes result locations, given a |ResultType|.
    138 //
    139 // Recall that a |ResultType| represents a sequence of value types t1..tN,
    140 // indexed from 1 to N.  In principle it doesn't matter how we decide which
    141 // results get to be in registers and which go to the stack.  To better
    142 // harmonize with WebAssembly's abstract stack machine, whose properties are
    143 // taken advantage of by the baseline compiler, our strategy is to start
    144 // allocating result locations in "reverse" order: from result N down to 1.
    145 //
    146 // If a result with index I is in a register, then all results with index J > I
    147 // are also in registers.  If a result I is on the stack, then all results with
    148 // index K < I are also on the stack, farther away from the stack pointer than
    149 // result I.
    150 //
    151 // Currently only a single result is ever stored in a register, though this may
    152 // change in the future on register-rich platforms.
    153 //
    154 // NB: The baseline compiler also uses thie ABI for locations of block
    155 // parameters and return values, within individual WebAssembly functions.
    156 
    157 class ABIResultIter {
    158  ResultType type_;
    159  uint32_t count_;
    160  uint32_t index_;
    161  uint32_t nextStackOffset_;
    162  enum { Next, Prev } direction_;
    163  ABIResult cur_;
    164 
    165  void settleRegister(ValType type);
    166  void settleNext();
    167  void settlePrev();
    168 
    169 public:
    170  explicit ABIResultIter(const ResultType& type)
    171      : type_(type), count_(type.length()) {
    172    reset();
    173  }
    174 
    175  void reset() {
    176    index_ = nextStackOffset_ = 0;
    177    direction_ = Next;
    178    if (!done()) {
    179      settleNext();
    180    }
    181  }
    182  bool done() const { return index_ == count_; }
    183  uint32_t index() const { return index_; }
    184  uint32_t count() const { return count_; }
    185  uint32_t remaining() const { return count_ - index_; }
    186  void switchToNext() {
    187    MOZ_ASSERT(direction_ == Prev);
    188    if (!done() && cur().onStack()) {
    189      nextStackOffset_ += cur().size();
    190    }
    191    index_ = count_ - index_;
    192    direction_ = Next;
    193    if (!done()) {
    194      settleNext();
    195    }
    196  }
    197  void switchToPrev() {
    198    MOZ_ASSERT(direction_ == Next);
    199    if (!done() && cur().onStack()) {
    200      nextStackOffset_ -= cur().size();
    201    }
    202    index_ = count_ - index_;
    203    direction_ = Prev;
    204    if (!done()) settlePrev();
    205  }
    206  void next() {
    207    MOZ_ASSERT(direction_ == Next);
    208    MOZ_ASSERT(!done());
    209    index_++;
    210    if (!done()) {
    211      settleNext();
    212    }
    213  }
    214  void prev() {
    215    MOZ_ASSERT(direction_ == Prev);
    216    MOZ_ASSERT(!done());
    217    index_++;
    218    if (!done()) {
    219      settlePrev();
    220    }
    221  }
    222  const ABIResult& cur() const {
    223    MOZ_ASSERT(!done());
    224    return cur_;
    225  }
    226 
    227  uint32_t stackBytesConsumedSoFar() const { return nextStackOffset_; }
    228 
    229  static inline bool HasStackResults(const ResultType& type) {
    230    return type.length() > MaxRegisterResults;
    231  }
    232 
    233  static uint32_t MeasureStackBytes(const ResultType& type) {
    234    if (!HasStackResults(type)) {
    235      return 0;
    236    }
    237    ABIResultIter iter(type);
    238    while (!iter.done()) {
    239      iter.next();
    240    }
    241    return iter.stackBytesConsumedSoFar();
    242  }
    243 };
    244 
    245 extern bool GenerateBuiltinThunk(jit::MacroAssembler& masm,
    246                                 jit::ABIFunctionType abiType,
    247                                 bool switchToMainStack, ExitReason exitReason,
    248                                 void* funcPtr, CallableOffsets* offsets);
    249 
    250 extern bool GenerateStubs(const CodeMetadata& codeMeta,
    251                          const FuncImportVector& imports,
    252                          const FuncExportVector& exports, CompiledCode* code);
    253 
    254 extern bool GenerateEntryStubs(const CodeMetadata& codeMeta,
    255                               const FuncExportVector& exports,
    256                               CompiledCode* code);
    257 
    258 extern bool GenerateEntryStubs(jit::MacroAssembler& masm,
    259                               size_t funcExportIndex, const FuncExport& fe,
    260                               const FuncType& funcType,
    261                               const mozilla::Maybe<jit::ImmPtr>& callee,
    262                               bool isAsmJS, CodeRangeVector* codeRanges);
    263 
    264 extern void GenerateTrapExitRegisterOffsets(jit::RegisterOffsets* offsets,
    265                                            size_t* numWords);
    266 
    267 extern bool GenerateProvisionalLazyJitEntryStub(jit::MacroAssembler& masm,
    268                                                Offsets* offsets);
    269 
    270 // A value that is written into the trap exit frame, which is useful for
    271 // cross-checking during garbage collection.
    272 static constexpr uintptr_t TrapExitDummyValue = 1337;
    273 
    274 // And its offset, in words, down from the highest-addressed word of the trap
    275 // exit frame.  The value is written into the frame using WasmPush.  In the
    276 // case where WasmPush allocates more than one word, the value will therefore
    277 // be written at the lowest-addressed word.
    278 #ifdef JS_CODEGEN_ARM64
    279 static constexpr size_t TrapExitDummyValueOffsetFromTop = 1;
    280 #else
    281 static constexpr size_t TrapExitDummyValueOffsetFromTop = 0;
    282 #endif
    283 
    284 // An argument that will end up on the stack according to the system ABI, to be
    285 // passed to GenerateDirectCallFromJit. Since the direct JIT call creates its
    286 // own frame, it is its responsibility to put stack arguments to their expected
    287 // locations; so the caller of GenerateDirectCallFromJit can put them anywhere.
    288 
    289 class JitCallStackArg {
    290 public:
    291  enum class Tag {
    292    Imm32,
    293    GPR,
    294    FPU,
    295    Address,
    296    Undefined,
    297  };
    298 
    299 private:
    300  Tag tag_;
    301  union U {
    302    int32_t imm32_;
    303    jit::Register gpr_;
    304    jit::FloatRegister fpu_;
    305    jit::Address addr_;
    306    U() {}
    307  } arg;
    308 
    309 public:
    310  JitCallStackArg() : tag_(Tag::Undefined) {}
    311  explicit JitCallStackArg(int32_t imm32) : tag_(Tag::Imm32) {
    312    arg.imm32_ = imm32;
    313  }
    314  explicit JitCallStackArg(jit::Register gpr) : tag_(Tag::GPR) {
    315    arg.gpr_ = gpr;
    316  }
    317  explicit JitCallStackArg(jit::FloatRegister fpu) : tag_(Tag::FPU) {
    318    new (&arg) jit::FloatRegister(fpu);
    319  }
    320  explicit JitCallStackArg(const jit::Address& addr) : tag_(Tag::Address) {
    321    new (&arg) jit::Address(addr);
    322  }
    323 
    324  Tag tag() const { return tag_; }
    325  int32_t imm32() const {
    326    MOZ_ASSERT(tag_ == Tag::Imm32);
    327    return arg.imm32_;
    328  }
    329  jit::Register gpr() const {
    330    MOZ_ASSERT(tag_ == Tag::GPR);
    331    return arg.gpr_;
    332  }
    333  jit::FloatRegister fpu() const {
    334    MOZ_ASSERT(tag_ == Tag::FPU);
    335    return arg.fpu_;
    336  }
    337  const jit::Address& addr() const {
    338    MOZ_ASSERT(tag_ == Tag::Address);
    339    return arg.addr_;
    340  }
    341 };
    342 
    343 using JitCallStackArgVector = Vector<JitCallStackArg, 4, SystemAllocPolicy>;
    344 
    345 // Generates an inline wasm call (during jit compilation) to a specific wasm
    346 // function (as specifed by the given FuncExport).
    347 // This call doesn't go through a wasm entry, but rather creates its own
    348 // inlined exit frame.
    349 // Assumes:
    350 // - all the registers have been preserved by the caller,
    351 // - all arguments passed in registers have been set up at the expected
    352 //   locations,
    353 // - all arguments passed on stack slot are alive as defined by a corresponding
    354 //   JitCallStackArg.
    355 
    356 extern void GenerateDirectCallFromJit(jit::MacroAssembler& masm,
    357                                      const FuncExport& fe,
    358                                      const Instance& inst,
    359                                      const JitCallStackArgVector& stackArgs,
    360                                      jit::Register scratch,
    361                                      uint32_t* callOffset);
    362 
    363 extern void GenerateJumpToCatchHandler(jit::MacroAssembler& masm,
    364                                       jit::Register rfe,
    365                                       jit::Register scratch1,
    366                                       jit::Register scratch2);
    367 
    368 }  // namespace wasm
    369 }  // namespace js
    370 
    371 #endif  // wasm_stubs_h