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