tor-browser

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

WasmDebugFrame.h (7316B)


      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 2021 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_debugframe_h
     20 #define wasm_debugframe_h
     21 
     22 #include "mozilla/Assertions.h"
     23 
     24 #include <stddef.h>
     25 #include <stdint.h>
     26 
     27 #include "js/TypeDecls.h"
     28 #include "js/Value.h"
     29 #include "wasm/WasmCodegenConstants.h"
     30 #include "wasm/WasmFrame.h"
     31 #include "wasm/WasmValType.h"
     32 #include "wasm/WasmValue.h"
     33 
     34 namespace js {
     35 
     36 class GlobalObject;
     37 
     38 namespace wasm {
     39 
     40 class Instance;
     41 
     42 // A DebugFrame is a Frame with additional fields that are added after the
     43 // normal function prologue by the baseline compiler. If a Module is compiled
     44 // with debugging enabled, then all its code creates DebugFrames on the stack
     45 // instead of just Frames. These extra fields are used by the Debugger API.
     46 
     47 class DebugFrame {
     48  // The register results field.  Initialized only during the baseline
     49  // compiler's return sequence to allow the debugger to inspect and
     50  // modify the return values of a frame being debugged.
     51  union SpilledRegisterResult {
     52   private:
     53    int32_t i32_;
     54    int64_t i64_;
     55    float f32_;
     56    double f64_;
     57 #ifdef ENABLE_WASM_SIMD
     58    V128 v128_;
     59 #endif
     60    AnyRef anyref_;
     61 
     62 #ifdef DEBUG
     63    // Should we add a new value representation, this will remind us to update
     64    // SpilledRegisterResult.
     65    static inline void assertAllValueTypesHandled(ValType type) {
     66      switch (type.kind()) {
     67        case ValType::I32:
     68        case ValType::I64:
     69        case ValType::F32:
     70        case ValType::F64:
     71        case ValType::V128:
     72        case ValType::Ref:
     73          return;
     74      }
     75    }
     76 #endif
     77  };
     78  SpilledRegisterResult registerResults_[MaxRegisterResults];
     79 
     80  // The returnValue() method returns a HandleValue pointing to this field.
     81  JS::Value cachedReturnJSValue_;
     82 
     83  // If the function returns multiple results, this field is initialized
     84  // to a pointer to the stack results.
     85  void* stackResultsPointer_;
     86 
     87  // The function index of this frame. Technically, this could be derived
     88  // given a PC into this frame (which could lookup the CodeRange which has
     89  // the function index), but this isn't always readily available.
     90  uint32_t funcIndex_;
     91 
     92  // Flags whose meaning are described below.
     93  union Flags {
     94    struct {
     95      uint32_t observing : 1;
     96      uint32_t isDebuggee : 1;
     97      uint32_t prevUpToDate : 1;
     98      uint32_t hasCachedSavedFrame : 1;
     99      uint32_t hasCachedReturnJSValue : 1;
    100      uint32_t hasSpilledRefRegisterResult : MaxRegisterResults;
    101    };
    102    uint32_t allFlags;
    103  } flags_;
    104 
    105  // Avoid -Wunused-private-field warnings.
    106 protected:
    107 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_X86) || defined(__wasi__)
    108  // See alignmentStaticAsserts().  For ARM32 and X86 DebugFrame is only
    109  // 4-byte aligned, so we add another word to get up to 8-byte
    110  // alignment.
    111  uint32_t padding_;
    112 #endif
    113 #if defined(ENABLE_WASM_SIMD) && defined(JS_CODEGEN_ARM64)
    114  uint64_t padding_;
    115 #endif
    116 
    117 private:
    118  // The Frame goes at the end since the stack grows down.
    119  Frame frame_;
    120 
    121 public:
    122  static DebugFrame* from(Frame* fp);
    123  Frame& frame() { return frame_; }
    124  uint32_t funcIndex() const { return funcIndex_; }
    125  Instance* instance();
    126  const Instance* instance() const;
    127  GlobalObject* global();
    128  bool hasGlobal(const GlobalObject* global) const;
    129  JSObject* environmentChain();
    130  bool getLocal(uint32_t localIndex, JS::MutableHandleValue vp);
    131 
    132  // The return value must be written from the unboxed representation in the
    133  // results union into cachedReturnJSValue_ by updateReturnJSValue() before
    134  // returnValue() can return a Handle to it.
    135 
    136  bool hasCachedReturnJSValue() const { return flags_.hasCachedReturnJSValue; }
    137  [[nodiscard]] bool updateReturnJSValue(JSContext* cx);
    138  void discardReturnJSValue();
    139  JS::HandleValue returnValue() const;
    140  void clearReturnJSValue();
    141 
    142  // Once the debugger observes a frame, it must be notified via
    143  // onLeaveFrame() before the frame is popped. Calling observe() ensures the
    144  // leave frame traps are enabled. Both methods are idempotent so the caller
    145  // doesn't have to worry about calling them more than once.
    146 
    147  void observe(JSContext* cx);
    148  void leave(JSContext* cx);
    149 
    150  // The 'isDebugge' bit is initialized to false and set by the WebAssembly
    151  // runtime right before a frame is exposed to the debugger, as required by
    152  // the Debugger API. The bit is then used for Debugger-internal purposes
    153  // afterwards.
    154 
    155  bool isDebuggee() const { return flags_.isDebuggee; }
    156  void setIsDebuggee() { flags_.isDebuggee = true; }
    157  void unsetIsDebuggee() { flags_.isDebuggee = false; }
    158 
    159  // These are opaque boolean flags used by the debugger to implement
    160  // AbstractFramePtr. They are initialized to false and not otherwise read or
    161  // written by wasm code or runtime.
    162 
    163  bool prevUpToDate() const { return flags_.prevUpToDate; }
    164  void setPrevUpToDate() { flags_.prevUpToDate = true; }
    165  void unsetPrevUpToDate() { flags_.prevUpToDate = false; }
    166 
    167  bool hasCachedSavedFrame() const { return flags_.hasCachedSavedFrame; }
    168  void setHasCachedSavedFrame() { flags_.hasCachedSavedFrame = true; }
    169  void clearHasCachedSavedFrame() { flags_.hasCachedSavedFrame = false; }
    170 
    171  bool hasSpilledRegisterRefResult(size_t n) const {
    172    uint32_t mask = hasSpilledRegisterRefResultBitMask(n);
    173    return (flags_.allFlags & mask) != 0;
    174  }
    175 
    176  // DebugFrame is accessed directly by JIT code.
    177 
    178  static constexpr size_t offsetOfRegisterResults() {
    179    return offsetof(DebugFrame, registerResults_);
    180  }
    181  static constexpr size_t offsetOfRegisterResult(size_t n) {
    182    MOZ_ASSERT(n < MaxRegisterResults);
    183    return offsetOfRegisterResults() + n * sizeof(SpilledRegisterResult);
    184  }
    185  static constexpr size_t offsetOfCachedReturnJSValue() {
    186    return offsetof(DebugFrame, cachedReturnJSValue_);
    187  }
    188  static constexpr size_t offsetOfStackResultsPointer() {
    189    return offsetof(DebugFrame, stackResultsPointer_);
    190  }
    191  static constexpr size_t offsetOfFlags() {
    192    return offsetof(DebugFrame, flags_);
    193  }
    194  static constexpr uint32_t hasSpilledRegisterRefResultBitMask(size_t n) {
    195    MOZ_ASSERT(n < MaxRegisterResults);
    196    union Flags flags = {.allFlags = 0};
    197    flags.hasSpilledRefRegisterResult = 1 << n;
    198    MOZ_ASSERT(flags.allFlags != 0);
    199    return flags.allFlags;
    200  }
    201  static constexpr size_t offsetOfFuncIndex() {
    202    return offsetof(DebugFrame, funcIndex_);
    203  }
    204  static constexpr size_t offsetOfFrame() {
    205    return offsetof(DebugFrame, frame_);
    206  }
    207 
    208  // DebugFrames are aligned to 8-byte aligned, allowing them to be placed in
    209  // an AbstractFramePtr.
    210 
    211  static const unsigned Alignment = 8;
    212  static void alignmentStaticAsserts();
    213 };
    214 
    215 }  // namespace wasm
    216 }  // namespace js
    217 
    218 #endif  // wasm_debugframe_h