зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1334504 - Baldr: remove baseline's explicit TLS-saving (r=yury)
MozReview-Commit-ID: 3MyiHUo0da2 --HG-- extra : rebase_source : c81c5636f58abed9a2763319a72c3080e695bdc6
This commit is contained in:
Родитель
d4d809f4c4
Коммит
2710499799
|
@ -2980,9 +2980,9 @@ MacroAssembler::loadWasmActivationFromSymbolicAddress(Register dest)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::loadWasmTlsRegFromFrame()
|
||||
MacroAssembler::loadWasmTlsRegFromFrame(Register dest)
|
||||
{
|
||||
loadPtr(Address(getStackPointer(), framePushed() + offsetof(wasm::Frame, tls)), WasmTlsReg);
|
||||
loadPtr(Address(getStackPointer(), framePushed() + offsetof(wasm::Frame, tls)), dest);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1520,7 +1520,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
|
||||
void loadWasmActivationFromTls(Register dest);
|
||||
void loadWasmActivationFromSymbolicAddress(Register dest);
|
||||
void loadWasmTlsRegFromFrame();
|
||||
void loadWasmTlsRegFromFrame(Register dest = WasmTlsReg);
|
||||
|
||||
template<typename T>
|
||||
void loadTypedOrValue(const T& src, TypedOrValueRegister dest) {
|
||||
|
|
|
@ -381,7 +381,6 @@ UNIFIED_SOURCES += [
|
|||
'wasm/WasmCode.cpp',
|
||||
'wasm/WasmCompartment.cpp',
|
||||
'wasm/WasmCompile.cpp',
|
||||
'wasm/WasmDebugFrame.cpp',
|
||||
'wasm/WasmFrameIterator.cpp',
|
||||
'wasm/WasmGenerator.cpp',
|
||||
'wasm/WasmInstance.cpp',
|
||||
|
|
|
@ -2549,7 +2549,7 @@ Debugger::updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObs
|
|||
oldestEnabledFrame.setIsDebuggee();
|
||||
}
|
||||
if (iter.abstractFramePtr().isWasmDebugFrame())
|
||||
iter.abstractFramePtr().asWasmDebugFrame()->observeFrame(cx);
|
||||
iter.abstractFramePtr().asWasmDebugFrame()->observe(cx);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
// Debugger.Frame lifetimes are managed by the debug epilogue,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "js/Debug.h"
|
||||
#include "vm/EnvironmentObject.h"
|
||||
#include "vm/GeneratorObject.h"
|
||||
#include "wasm/WasmDebugFrame.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
@ -455,7 +454,7 @@ AbstractFramePtr::environmentChain() const
|
|||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->environmentChain();
|
||||
if (isWasmDebugFrame())
|
||||
return asWasmDebugFrame()->environmentChain();
|
||||
return &global()->lexicalEnvironment();
|
||||
return asRematerializedFrame()->environmentChain();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "js/GCAPI.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/Opcodes.h"
|
||||
#include "wasm/WasmDebugFrame.h"
|
||||
|
||||
#include "jit/JitFrameIterator-inl.h"
|
||||
#include "vm/EnvironmentObject-inl.h"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "vm/ArgumentsObject.h"
|
||||
#include "vm/SavedFrame.h"
|
||||
#include "wasm/WasmFrameIterator.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
|
||||
struct JSCompartment;
|
||||
|
||||
|
@ -166,6 +167,7 @@ class AbstractFramePtr
|
|||
MOZ_IMPLICIT AbstractFramePtr(wasm::DebugFrame* fp)
|
||||
: ptr_(fp ? uintptr_t(fp) | Tag_WasmDebugFrame : 0)
|
||||
{
|
||||
static_assert(wasm::DebugFrame::Alignment >= TagMask, "aligned");
|
||||
MOZ_ASSERT_IF(fp, asWasmDebugFrame() == fp);
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,6 @@
|
|||
#endif
|
||||
|
||||
#include "wasm/WasmBinaryIterator.h"
|
||||
#include "wasm/WasmDebugFrame.h"
|
||||
#include "wasm/WasmGenerator.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
@ -199,31 +198,19 @@ static constexpr int32_t TlsSlotSize = sizeof(void*);
|
|||
static constexpr int32_t TlsSlotOffset = TlsSlotSize;
|
||||
|
||||
BaseLocalIter::BaseLocalIter(const ValTypeVector& locals,
|
||||
size_t argsLength,
|
||||
bool debugEnabled)
|
||||
size_t argsLength,
|
||||
bool debugEnabled)
|
||||
: locals_(locals),
|
||||
argsLength_(argsLength),
|
||||
argsRange_(locals.begin(), argsLength),
|
||||
argsIter_(argsRange_),
|
||||
index_(0),
|
||||
localSize_(0),
|
||||
localSize_(debugEnabled ? DebugFrame::offsetOfFrame() : 0),
|
||||
reservedSize_(localSize_),
|
||||
done_(false)
|
||||
{
|
||||
MOZ_ASSERT(argsLength <= locals.length());
|
||||
|
||||
// Reserve a stack slot for the TLS pointer outside the locals range so it
|
||||
// isn't zero-filled like the normal locals.
|
||||
DebugOnly<int32_t> tlsSlotOffset = pushLocal(TlsSlotSize);
|
||||
MOZ_ASSERT(tlsSlotOffset == TlsSlotOffset);
|
||||
if (debugEnabled) {
|
||||
// If debug information is generated, constructing DebugFrame record:
|
||||
// reserving some data before TLS pointer. The TLS pointer allocated
|
||||
// above and regular wasm::Frame data starts after locals.
|
||||
localSize_ += DebugFrame::offsetOfTlsData();
|
||||
MOZ_ASSERT(DebugFrame::offsetOfFrame() == localSize_);
|
||||
}
|
||||
reservedSize_ = localSize_;
|
||||
|
||||
settle();
|
||||
}
|
||||
|
||||
|
@ -628,10 +615,6 @@ class BaseCompiler
|
|||
Vector<Local, 8, SystemAllocPolicy> localInfo_;
|
||||
Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_;
|
||||
|
||||
// Index into localInfo_ of the special local used for saving the TLS
|
||||
// pointer. This follows the function's real arguments and locals.
|
||||
uint32_t tlsSlot_;
|
||||
|
||||
// On specific platforms we sometimes need to use specific registers.
|
||||
|
||||
#ifdef JS_CODEGEN_X64
|
||||
|
@ -2229,9 +2212,6 @@ class BaseCompiler
|
|||
|
||||
maxFramePushed_ = localSize_;
|
||||
|
||||
// The TLS pointer is always passed as a hidden argument in WasmTlsReg.
|
||||
// Save it into its assigned local slot.
|
||||
storeToFramePtr(WasmTlsReg, localInfo_[tlsSlot_].offs());
|
||||
if (debugEnabled_) {
|
||||
// Initialize funcIndex and flag fields of DebugFrame.
|
||||
size_t debugFrame = masm.framePushed() - DebugFrame::offsetOfFrame();
|
||||
|
@ -2390,8 +2370,9 @@ class BaseCompiler
|
|||
restoreResult();
|
||||
}
|
||||
|
||||
// Restore the TLS register in case it was overwritten by the function.
|
||||
loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
// The epilogue assumes WasmTlsReg is valid so reload it in case it was
|
||||
// clobbered by the body.
|
||||
masm.loadWasmTlsRegFromFrame();
|
||||
|
||||
GenerateFunctionEpilogue(masm, localSize_, &offsets_);
|
||||
|
||||
|
@ -2481,7 +2462,7 @@ class BaseCompiler
|
|||
// On x86 there are no pinned registers, so don't waste time
|
||||
// reloading the Tls.
|
||||
#ifndef JS_CODEGEN_X86
|
||||
loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame();
|
||||
masm.loadWasmPinnedRegsFromTls();
|
||||
#endif
|
||||
}
|
||||
|
@ -2678,7 +2659,7 @@ class BaseCompiler
|
|||
const FunctionCall& call)
|
||||
{
|
||||
// Builtin method calls assume the TLS register has been set.
|
||||
loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame();
|
||||
|
||||
CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
|
||||
masm.wasmCallBuiltinInstanceMethod(instanceArg, builtin);
|
||||
|
@ -3317,56 +3298,56 @@ class BaseCompiler
|
|||
void loadGlobalVarI32(unsigned globalDataOffset, RegI32 r)
|
||||
{
|
||||
ScratchI32 tmp(*this);
|
||||
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tmp);
|
||||
masm.load32(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
|
||||
}
|
||||
|
||||
void loadGlobalVarI64(unsigned globalDataOffset, RegI64 r)
|
||||
{
|
||||
ScratchI32 tmp(*this);
|
||||
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tmp);
|
||||
masm.load64(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
|
||||
}
|
||||
|
||||
void loadGlobalVarF32(unsigned globalDataOffset, RegF32 r)
|
||||
{
|
||||
ScratchI32 tmp(*this);
|
||||
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tmp);
|
||||
masm.loadFloat32(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
|
||||
}
|
||||
|
||||
void loadGlobalVarF64(unsigned globalDataOffset, RegF64 r)
|
||||
{
|
||||
ScratchI32 tmp(*this);
|
||||
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tmp);
|
||||
masm.loadDouble(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
|
||||
}
|
||||
|
||||
void storeGlobalVarI32(unsigned globalDataOffset, RegI32 r)
|
||||
{
|
||||
ScratchI32 tmp(*this);
|
||||
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tmp);
|
||||
masm.store32(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
|
||||
}
|
||||
|
||||
void storeGlobalVarI64(unsigned globalDataOffset, RegI64 r)
|
||||
{
|
||||
ScratchI32 tmp(*this);
|
||||
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tmp);
|
||||
masm.store64(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
|
||||
}
|
||||
|
||||
void storeGlobalVarF32(unsigned globalDataOffset, RegF32 r)
|
||||
{
|
||||
ScratchI32 tmp(*this);
|
||||
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tmp);
|
||||
masm.storeFloat32(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
|
||||
}
|
||||
|
||||
void storeGlobalVarF64(unsigned globalDataOffset, RegF64 r)
|
||||
{
|
||||
ScratchI32 tmp(*this);
|
||||
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tmp);
|
||||
masm.storeDouble(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
|
||||
}
|
||||
|
||||
|
@ -5804,11 +5785,8 @@ BaseCompiler::emitCallArgs(const ValTypeVector& argTypes, FunctionCall& baseline
|
|||
for (size_t i = 0; i < numArgs; ++i)
|
||||
passArg(baselineCall, argTypes[i], peek(numArgs - 1 - i));
|
||||
|
||||
// Pass the TLS pointer as a hidden argument in WasmTlsReg. Load
|
||||
// it directly out if its stack slot so we don't interfere with
|
||||
// the stk_.
|
||||
if (baselineCall.loadTlsBefore)
|
||||
loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -6450,7 +6428,7 @@ BaseCompiler::maybeLoadTlsForAccess(bool omitBoundsCheck)
|
|||
RegI32 tls = invalidI32();
|
||||
if (needTlsForAccess(omitBoundsCheck)) {
|
||||
tls = needI32();
|
||||
loadFromFramePtr(tls, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
masm.loadWasmTlsRegFromFrame(tls);
|
||||
}
|
||||
return tls;
|
||||
}
|
||||
|
@ -7473,7 +7451,6 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
|
|||
#ifdef DEBUG
|
||||
scratchRegisterTaken_(false),
|
||||
#endif
|
||||
tlsSlot_(0),
|
||||
#ifdef JS_CODEGEN_X64
|
||||
specific_rax(RegI64(Register64(rax))),
|
||||
specific_rcx(RegI64(Register64(rcx))),
|
||||
|
@ -7533,15 +7510,9 @@ BaseCompiler::init()
|
|||
|
||||
const ValTypeVector& args = func_.sig().args();
|
||||
|
||||
// localInfo_ contains an entry for every local in locals_, followed by
|
||||
// entries for special locals. Currently the only special local is the TLS
|
||||
// pointer.
|
||||
tlsSlot_ = locals_.length();
|
||||
if (!localInfo_.resize(locals_.length() + 1))
|
||||
if (!localInfo_.resize(locals_.length()))
|
||||
return false;
|
||||
|
||||
localInfo_[tlsSlot_].init(MIRType::Pointer, TlsSlotOffset);
|
||||
|
||||
BaseLocalIter i(locals_, args.length(), debugEnabled_);
|
||||
varLow_ = i.reservedSize();
|
||||
for (; !i.done() && i.index() < args.length(); i++) {
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2016 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "wasm/WasmDebugFrame.h"
|
||||
|
||||
#include "vm/EnvironmentObject.h"
|
||||
#include "wasm/WasmBaselineCompile.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::wasm;
|
||||
|
||||
Instance*
|
||||
DebugFrame::instance() const
|
||||
{
|
||||
return tlsData_->instance;
|
||||
}
|
||||
|
||||
GlobalObject*
|
||||
DebugFrame::global() const
|
||||
{
|
||||
return &instance()->object()->global();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
DebugFrame::environmentChain() const
|
||||
{
|
||||
return &global()->lexicalEnvironment();
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::observeFrame(JSContext* cx)
|
||||
{
|
||||
if (observing_)
|
||||
return;
|
||||
|
||||
instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ true);
|
||||
observing_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::leaveFrame(JSContext* cx)
|
||||
{
|
||||
if (!observing_)
|
||||
return;
|
||||
|
||||
instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ false);
|
||||
observing_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::clearReturnJSValue()
|
||||
{
|
||||
hasCachedReturnJSValue_ = true;
|
||||
cachedReturnJSValue_.setUndefined();
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::updateReturnJSValue()
|
||||
{
|
||||
hasCachedReturnJSValue_ = true;
|
||||
ExprType returnType = instance()->code().debugGetResultType(funcIndex());
|
||||
switch (returnType) {
|
||||
case ExprType::Void:
|
||||
cachedReturnJSValue_.setUndefined();
|
||||
break;
|
||||
case ExprType::I32:
|
||||
cachedReturnJSValue_.setInt32(resultI32_);
|
||||
break;
|
||||
case ExprType::I64:
|
||||
// Just display as a Number; it's ok if we lose some precision
|
||||
cachedReturnJSValue_.setDouble((double)resultI64_);
|
||||
break;
|
||||
case ExprType::F32:
|
||||
cachedReturnJSValue_.setDouble(JS::CanonicalizeNaN(resultF32_));
|
||||
break;
|
||||
case ExprType::F64:
|
||||
cachedReturnJSValue_.setDouble(JS::CanonicalizeNaN(resultF64_));
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("result type");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DebugFrame::getLocal(uint32_t localIndex, MutableHandleValue vp)
|
||||
{
|
||||
ValTypeVector locals;
|
||||
size_t argsLength;
|
||||
if (!instance()->code().debugGetLocalTypes(funcIndex(), &locals, &argsLength))
|
||||
return false;
|
||||
|
||||
BaseLocalIter iter(locals, argsLength, /* debugEnabled = */ true);
|
||||
while (!iter.done() && iter.index() < localIndex)
|
||||
iter++;
|
||||
MOZ_ALWAYS_TRUE(!iter.done());
|
||||
|
||||
uint8_t* frame = static_cast<uint8_t*>((void*)this) + offsetOfFrame();
|
||||
void* dataPtr = frame - iter.frameOffset();
|
||||
switch (iter.mirType()) {
|
||||
case jit::MIRType::Int32:
|
||||
vp.set(Int32Value(*static_cast<int32_t*>(dataPtr)));
|
||||
break;
|
||||
case jit::MIRType::Int64:
|
||||
// Just display as a Number; it's ok if we lose some precision
|
||||
vp.set(NumberValue((double)*static_cast<int64_t*>(dataPtr)));
|
||||
break;
|
||||
case jit::MIRType::Float32:
|
||||
vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<float*>(dataPtr))));
|
||||
break;
|
||||
case jit::MIRType::Double:
|
||||
vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<double*>(dataPtr))));
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("local type");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2016 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasmdebugframe_js_h
|
||||
#define wasmdebugframe_js_h
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmFunctionCallObject;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
class DebugFrame
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t resultI32_;
|
||||
int64_t resultI64_;
|
||||
float resultF32_;
|
||||
double resultF64_;
|
||||
};
|
||||
|
||||
js::Value cachedReturnJSValue_;
|
||||
|
||||
// The fields below are initialized by the baseline compiler.
|
||||
uint32_t funcIndex_;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool observing_ : 1;
|
||||
bool isDebuggee_ : 1;
|
||||
bool prevUpToDate_ : 1;
|
||||
bool hasCachedSavedFrame_ : 1;
|
||||
bool hasCachedReturnJSValue_ : 1;
|
||||
};
|
||||
void* reserved1_;
|
||||
};
|
||||
|
||||
TlsData* tlsData_;
|
||||
Frame frame_;
|
||||
|
||||
explicit DebugFrame() {}
|
||||
|
||||
void StaticAsserts() {
|
||||
// VS2017 doesn't consider offsetOfResults() etc. to be constexpr, so we have to use
|
||||
// offsetof directly. These asserts can't be at class-level because the type is incomplete.
|
||||
static_assert(offsetof(DebugFrame, resultI32_) == 0, "results shall be at offset 0");
|
||||
static_assert(offsetof(DebugFrame, tlsData_) + sizeof(TlsData*) ==
|
||||
offsetof(DebugFrame, frame_),
|
||||
"TLS pointer must be a field just before the wasm frame");
|
||||
static_assert(sizeof(DebugFrame) % 8 == 0,
|
||||
"DebugFrame is 8-bytes aligned for AbstractFramePtr");
|
||||
}
|
||||
|
||||
public:
|
||||
inline uint32_t funcIndex() const { return funcIndex_; }
|
||||
inline TlsData* tlsData() const { return tlsData_; }
|
||||
inline Frame& frame() { return frame_; }
|
||||
|
||||
Instance* instance() const;
|
||||
GlobalObject* global() const;
|
||||
|
||||
JSObject* environmentChain() const;
|
||||
|
||||
void observeFrame(JSContext* cx);
|
||||
void leaveFrame(JSContext* cx);
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
// These are opaque boolean flags used by the debugger and
|
||||
// saved-frame-chains code.
|
||||
inline bool isDebuggee() const { return isDebuggee_; }
|
||||
inline void setIsDebuggee() { isDebuggee_ = true; }
|
||||
inline void unsetIsDebuggee() { isDebuggee_ = false; }
|
||||
|
||||
inline bool prevUpToDate() const { return prevUpToDate_; }
|
||||
inline void setPrevUpToDate() { prevUpToDate_ = true; }
|
||||
inline void unsetPrevUpToDate() { prevUpToDate_ = false; }
|
||||
|
||||
inline bool hasCachedSavedFrame() const { return hasCachedSavedFrame_; }
|
||||
inline void setHasCachedSavedFrame() { hasCachedSavedFrame_ = true; }
|
||||
|
||||
inline void* resultsPtr() { return &resultI32_; }
|
||||
|
||||
inline HandleValue returnValue() const {
|
||||
MOZ_ASSERT(hasCachedReturnJSValue_);
|
||||
return HandleValue::fromMarkedLocation(&cachedReturnJSValue_);
|
||||
}
|
||||
void updateReturnJSValue();
|
||||
void clearReturnJSValue();
|
||||
|
||||
bool getLocal(uint32_t localIndex, MutableHandleValue vp);
|
||||
|
||||
static constexpr size_t offsetOfResults() { return offsetof(DebugFrame, resultI32_); }
|
||||
static constexpr size_t offsetOfFlagsWord() { return offsetof(DebugFrame, reserved1_); }
|
||||
static constexpr size_t offsetOfFuncIndex() { return offsetof(DebugFrame, funcIndex_); }
|
||||
static constexpr size_t offsetOfTlsData() { return offsetof(DebugFrame, tlsData_); }
|
||||
static constexpr size_t offsetOfFrame() { return offsetof(DebugFrame, frame_); }
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // wasmdebugframe_js_h
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#include "wasm/WasmFrameIterator.h"
|
||||
|
||||
#include "wasm/WasmDebugFrame.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "jit/MacroAssembler.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "wasm/WasmBaselineCompile.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
#include "wasm/WasmSerialize.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
|
@ -115,7 +116,7 @@ WasmHandleDebugTrap()
|
|||
return true;
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->setIsDebuggee();
|
||||
frame->observeFrame(cx);
|
||||
frame->observe(cx);
|
||||
// TODO call onEnterFrame
|
||||
JSTrapStatus status = Debugger::onEnterFrame(cx, frame);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
|
@ -131,7 +132,7 @@ WasmHandleDebugTrap()
|
|||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->updateReturnJSValue();
|
||||
bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, true);
|
||||
frame->leaveFrame(cx);
|
||||
frame->leave(cx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@ -196,7 +197,7 @@ WasmHandleThrow()
|
|||
// TODO properly handle success and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
|
||||
}
|
||||
frame->leaveFrame(cx);
|
||||
frame->leave(cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -998,3 +999,122 @@ wasm::ComputeMappedSize(uint32_t maxSize)
|
|||
}
|
||||
|
||||
#endif // WASM_HUGE_MEMORY
|
||||
|
||||
void
|
||||
DebugFrame::alignmentStaticAsserts()
|
||||
{
|
||||
// VS2017 doesn't consider offsetOfFrame() to be a constexpr, so we have
|
||||
// to use offsetof directly. These asserts can't be at class-level
|
||||
// because the type is incomplete.
|
||||
|
||||
static_assert(WasmStackAlignment >= Alignment,
|
||||
"Aligned by ABI before pushing DebugFrame");
|
||||
static_assert((offsetof(DebugFrame, frame_) + sizeof(Frame)) % Alignment == 0,
|
||||
"Aligned after pushing DebugFrame");
|
||||
}
|
||||
|
||||
GlobalObject*
|
||||
DebugFrame::global() const
|
||||
{
|
||||
return &instance()->object()->global();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
DebugFrame::environmentChain() const
|
||||
{
|
||||
return &global()->lexicalEnvironment();
|
||||
}
|
||||
|
||||
bool
|
||||
DebugFrame::getLocal(uint32_t localIndex, MutableHandleValue vp)
|
||||
{
|
||||
ValTypeVector locals;
|
||||
size_t argsLength;
|
||||
if (!instance()->code().debugGetLocalTypes(funcIndex(), &locals, &argsLength))
|
||||
return false;
|
||||
|
||||
BaseLocalIter iter(locals, argsLength, /* debugEnabled = */ true);
|
||||
while (!iter.done() && iter.index() < localIndex)
|
||||
iter++;
|
||||
MOZ_ALWAYS_TRUE(!iter.done());
|
||||
|
||||
uint8_t* frame = static_cast<uint8_t*>((void*)this) + offsetOfFrame();
|
||||
void* dataPtr = frame - iter.frameOffset();
|
||||
switch (iter.mirType()) {
|
||||
case jit::MIRType::Int32:
|
||||
vp.set(Int32Value(*static_cast<int32_t*>(dataPtr)));
|
||||
break;
|
||||
case jit::MIRType::Int64:
|
||||
// Just display as a Number; it's ok if we lose some precision
|
||||
vp.set(NumberValue((double)*static_cast<int64_t*>(dataPtr)));
|
||||
break;
|
||||
case jit::MIRType::Float32:
|
||||
vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<float*>(dataPtr))));
|
||||
break;
|
||||
case jit::MIRType::Double:
|
||||
vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<double*>(dataPtr))));
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("local type");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::updateReturnJSValue()
|
||||
{
|
||||
hasCachedReturnJSValue_ = true;
|
||||
ExprType returnType = instance()->code().debugGetResultType(funcIndex());
|
||||
switch (returnType) {
|
||||
case ExprType::Void:
|
||||
cachedReturnJSValue_.setUndefined();
|
||||
break;
|
||||
case ExprType::I32:
|
||||
cachedReturnJSValue_.setInt32(resultI32_);
|
||||
break;
|
||||
case ExprType::I64:
|
||||
// Just display as a Number; it's ok if we lose some precision
|
||||
cachedReturnJSValue_.setDouble((double)resultI64_);
|
||||
break;
|
||||
case ExprType::F32:
|
||||
cachedReturnJSValue_.setDouble(JS::CanonicalizeNaN(resultF32_));
|
||||
break;
|
||||
case ExprType::F64:
|
||||
cachedReturnJSValue_.setDouble(JS::CanonicalizeNaN(resultF64_));
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("result type");
|
||||
}
|
||||
}
|
||||
|
||||
HandleValue
|
||||
DebugFrame::returnValue() const
|
||||
{
|
||||
MOZ_ASSERT(hasCachedReturnJSValue_);
|
||||
return HandleValue::fromMarkedLocation(&cachedReturnJSValue_);
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::clearReturnJSValue()
|
||||
{
|
||||
hasCachedReturnJSValue_ = true;
|
||||
cachedReturnJSValue_.setUndefined();
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::observe(JSContext* cx)
|
||||
{
|
||||
if (!observing_) {
|
||||
instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ true);
|
||||
observing_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::leave(JSContext* cx)
|
||||
{
|
||||
if (observing_) {
|
||||
instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ false);
|
||||
observing_ = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
namespace js {
|
||||
|
||||
class PropertyName;
|
||||
class WasmFunctionCallObject;
|
||||
namespace jit {
|
||||
struct BaselineScript;
|
||||
enum class RoundingMode;
|
||||
|
@ -1465,6 +1466,116 @@ struct Frame
|
|||
// The return address pushed by the call (in the case of ARM/MIPS the return
|
||||
// address is pushed by the first instruction of the prologue).
|
||||
void* returnAddress;
|
||||
|
||||
// Helper functions:
|
||||
|
||||
Instance* instance() const { return tls->instance; }
|
||||
};
|
||||
|
||||
// A DebugFrame is a Frame with additional fields that are added after the
|
||||
// normal function prologue by the baseline compiler. If a Module is compiled
|
||||
// with debugging enabled, then all its code creates DebugFrames on the stack
|
||||
// instead of just Frames. These extra fields are used by the Debugger API.
|
||||
|
||||
class DebugFrame
|
||||
{
|
||||
// The results field left uninitialized and only used during the baseline
|
||||
// compiler's return sequence to allow the debugger to inspect and modify
|
||||
// the return value of a frame being debugged.
|
||||
union
|
||||
{
|
||||
int32_t resultI32_;
|
||||
int64_t resultI64_;
|
||||
float resultF32_;
|
||||
double resultF64_;
|
||||
};
|
||||
|
||||
// The returnValue() method returns a HandleValue pointing to this field.
|
||||
js::Value cachedReturnJSValue_;
|
||||
|
||||
// The function index of this frame. Technically, this could be derived
|
||||
// given a PC into this frame (which could lookup the CodeRange which has
|
||||
// the function index), but this isn't always readily available.
|
||||
uint32_t funcIndex_;
|
||||
|
||||
// Flags whose meaning are described below.
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool observing_ : 1;
|
||||
bool isDebuggee_ : 1;
|
||||
bool prevUpToDate_ : 1;
|
||||
bool hasCachedSavedFrame_ : 1;
|
||||
bool hasCachedReturnJSValue_ : 1;
|
||||
};
|
||||
void* flagsWord_;
|
||||
};
|
||||
|
||||
// Padding so that DebugFrame has Alignment.
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
void* padding_;
|
||||
#endif
|
||||
|
||||
// The Frame goes at the end since the stack grows down.
|
||||
Frame frame_;
|
||||
|
||||
public:
|
||||
Frame& frame() { return frame_; }
|
||||
uint32_t funcIndex() const { return funcIndex_; }
|
||||
Instance* instance() const { return frame_.instance(); }
|
||||
GlobalObject* global() const;
|
||||
JSObject* environmentChain() const;
|
||||
bool getLocal(uint32_t localIndex, MutableHandleValue vp);
|
||||
|
||||
// The return value must be written from the unboxed representation in the
|
||||
// results union into cachedReturnJSValue_ by updateReturnJSValue() before
|
||||
// returnValue() can return a Handle to it.
|
||||
|
||||
void updateReturnJSValue();
|
||||
HandleValue returnValue() const;
|
||||
void clearReturnJSValue();
|
||||
|
||||
// Once the debugger observes a frame, it must be notified via
|
||||
// onLeaveFrame() before the frame is popped. Calling observe() ensures the
|
||||
// leave frame traps are enabled. Both methods are idempotent so the caller
|
||||
// doesn't have to worry about calling them more than once.
|
||||
|
||||
void observe(JSContext* cx);
|
||||
void leave(JSContext* cx);
|
||||
|
||||
// The 'isDebugge' bit is initialized to false and set by the WebAssembly
|
||||
// runtime right before a frame is exposed to the debugger, as required by
|
||||
// the Debugger API. The bit is then used for Debugger-internal purposes
|
||||
// afterwards.
|
||||
|
||||
bool isDebuggee() const { return isDebuggee_; }
|
||||
void setIsDebuggee() { isDebuggee_ = true; }
|
||||
void unsetIsDebuggee() { isDebuggee_ = false; }
|
||||
|
||||
// These are opaque boolean flags used by the debugger to implement
|
||||
// AbstractFramePtr. They are initialized to false and not otherwise read or
|
||||
// written by wasm code or runtime.
|
||||
|
||||
bool prevUpToDate() const { return prevUpToDate_; }
|
||||
void setPrevUpToDate() { prevUpToDate_ = true; }
|
||||
void unsetPrevUpToDate() { prevUpToDate_ = false; }
|
||||
|
||||
bool hasCachedSavedFrame() const { return hasCachedSavedFrame_; }
|
||||
void setHasCachedSavedFrame() { hasCachedSavedFrame_ = true; }
|
||||
|
||||
// DebugFrame is accessed directly by JIT code.
|
||||
|
||||
static constexpr size_t offsetOfResults() { return offsetof(DebugFrame, resultI32_); }
|
||||
static constexpr size_t offsetOfFlagsWord() { return offsetof(DebugFrame, flagsWord_); }
|
||||
static constexpr size_t offsetOfFuncIndex() { return offsetof(DebugFrame, funcIndex_); }
|
||||
static constexpr size_t offsetOfFrame() { return offsetof(DebugFrame, frame_); }
|
||||
|
||||
// DebugFrames are aligned to 8-byte aligned, allowing them to be placed in
|
||||
// an AbstractFramePtr.
|
||||
|
||||
static const unsigned Alignment = 8;
|
||||
static void alignmentStaticAsserts();
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
|
|
Загрузка…
Ссылка в новой задаче