Bug 1412202 - Part 4: Copy any unaliased locals between stack and GeneratorObject on suspend/resume. r=jandem

No effect yet, as there are no unaliased locals to copy.

Differential Revision: https://phabricator.services.mozilla.com/D93384
This commit is contained in:
Jason Orendorff 2020-10-15 14:05:31 +00:00
Родитель 61a1c23f13
Коммит 250ff7fccb
12 изменённых файлов: 149 добавлений и 111 удалений

Просмотреть файл

@ -5780,12 +5780,10 @@ bool BaselineCodeGen<Handler>::emitSuspend(JSOp op) {
masm.unboxObject(R0, genObj);
}
#ifdef DEBUG
size_t liveFixed;
MOZ_ASSERT_IF(handler.tryCountLiveFixed(&liveFixed), liveFixed == 0);
#endif
if (op == JSOp::InitialYield || frame.hasKnownStackDepth(1)) {
if (frame.hasKnownStackDepth(1) && !handler.canHaveFixedSlots()) {
// If the expression stack is empty, we can inline the Yield. Note that this
// branch is never taken for the interpreter because it doesn't know static
// stack depths.
MOZ_ASSERT_IF(op == JSOp::InitialYield && handler.maybePC(),
GET_RESUMEINDEX(handler.maybePC()) == 0);
Address resumeIndexSlot(genObj,
@ -5794,9 +5792,6 @@ bool BaselineCodeGen<Handler>::emitSuspend(JSOp op) {
if (op == JSOp::InitialYield) {
masm.storeValue(Int32Value(0), resumeIndexSlot);
} else {
// If the expression stack is empty, we can inline the Yield. Note that
// this branch is never taken for the interpreter because it doesn't know
// static stack depths.
jsbytecode* pc = handler.maybePC();
MOZ_ASSERT(pc, "compiler-only code never has a null pc");
masm.move32(Imm32(GET_RESUMEINDEX(pc)), temp);
@ -5814,14 +5809,8 @@ bool BaselineCodeGen<Handler>::emitSuspend(JSOp op) {
masm.branchPtrInNurseryChunk(Assembler::Equal, genObj, temp, &skipBarrier);
masm.branchPtrInNurseryChunk(Assembler::NotEqual, envObj, temp,
&skipBarrier);
if (op == JSOp::InitialYield) {
masm.push(genObj);
}
MOZ_ASSERT(genObj == R2.scratchReg());
masm.call(&postBarrierSlot_);
if (op == JSOp::InitialYield) {
masm.pop(genObj);
}
masm.bind(&skipBarrier);
} else {
masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
@ -5839,8 +5828,8 @@ bool BaselineCodeGen<Handler>::emitSuspend(JSOp op) {
return false;
}
}
masm.loadValue(frame.addressOfStackValue(-1), JSReturnOperand);
masm.loadValue(frame.addressOfStackValue(-1), JSReturnOperand);
if (!emitReturn()) {
return false;
}
@ -6145,7 +6134,7 @@ bool BaselineCodeGen<Handler>::emit_Resume() {
}
masm.bind(&noArgsObj);
// Push expression slots if needed.
// Push locals and expression slots if needed.
Label noStackStorage;
Address stackStorageSlot(genObj,
AbstractGeneratorObject::offsetOfStackStorageSlot());

Просмотреть файл

@ -362,10 +362,7 @@ class BaselineCompilerHandler {
return script()->nslots() > NumSlotsLimit;
}
bool tryCountLiveFixed(size_t* liveFixed) const {
*liveFixed = script()->calculateLiveFixed(pc());
return true;
}
bool canHaveFixedSlots() const { return script()->nfixed() != 0; }
};
using BaselineCompilerCodeGen = BaselineCodeGen<BaselineCompilerHandler>;
@ -488,7 +485,7 @@ class BaselineInterpreterHandler {
// include them.
bool mustIncludeSlotsInStackCheck() const { return true; }
bool tryCountLiveFixed(size_t* liveFixed) const { return false; }
bool canHaveFixedSlots() const { return true; }
};
using BaselineInterpreterCodeGen = BaselineCodeGen<BaselineInterpreterHandler>;

Просмотреть файл

@ -14,6 +14,7 @@
#include "vm/EnvironmentObject-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/NativeObject-inl.h" // js::NativeObject::initDenseElementsFromRange
namespace js {
namespace jit {
@ -39,6 +40,17 @@ inline void BaselineFrame::replaceInnermostEnvironment(EnvironmentObject& env) {
envChain_ = &env;
}
inline bool BaselineFrame::saveGeneratorSlots(JSContext* cx, unsigned nslots,
ArrayObject* dest) const {
// By convention, generator slots are stored in interpreter order,
// which is the reverse of BaselineFrame order.
MOZ_ASSERT(nslots == numValueSlots(debugFrameSize()) - 1);
const Value* end = reinterpret_cast<const Value*>(this);
mozilla::Span<const Value> span{end - nslots, end};
return dest->initDenseElementsFromRange(cx, span.rbegin(), span.rend());
}
inline bool BaselineFrame::pushLexicalEnvironment(JSContext* cx,
Handle<LexicalScope*> scope) {
LexicalEnvironmentObject* env =

Просмотреть файл

@ -195,6 +195,9 @@ class BaselineFrame {
BaselineFrame::Size() + offsetOfArg(0));
}
MOZ_MUST_USE bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
ArrayObject* dest) const;
private:
Value* evalNewTargetAddress() const {
MOZ_ASSERT(isEvalFrame());

Просмотреть файл

@ -1437,33 +1437,13 @@ JSObject* CreateGenerator(JSContext* cx, BaselineFrame* frame) {
bool NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame,
uint32_t frameSize, jsbytecode* pc) {
MOZ_ASSERT(JSOp(*pc) == JSOp::Yield || JSOp(*pc) == JSOp::Await);
MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield ||
JSOp(*pc) == JSOp::Await);
uint32_t numValueSlots = frame->numValueSlots(frameSize);
MOZ_ASSERT(numValueSlots > frame->script()->nfixed());
uint32_t stackDepth = numValueSlots - frame->script()->nfixed();
// Return value is still on the stack.
MOZ_ASSERT(stackDepth >= 1);
// The expression stack slots are stored on the stack in reverse order, so
// we copy them to a Vector and pass a pointer to that instead. We use
// stackDepth - 1 because we don't want to include the return value.
RootedValueVector stackStorage(cx);
if (!stackStorage.reserve(stackDepth - 1)) {
return false;
}
size_t firstSlot = numValueSlots - stackDepth;
for (size_t i = 0; i < stackDepth - 1; i++) {
stackStorage.infallibleAppend(*frame->valueSlot(firstSlot + i));
}
MOZ_ASSERT(stackStorage.length() == stackDepth - 1);
return AbstractGeneratorObject::normalSuspend(
cx, obj, frame, pc, stackStorage.begin(), stackDepth - 1);
// Minus one because we don't want to include the return value.
uint32_t numSlots = frame->numValueSlots(frameSize) - 1;
MOZ_ASSERT(numSlots >= frame->script()->nfixed());
return AbstractGeneratorObject::suspend(cx, obj, frame, pc, numSlots);
}
bool FinalSuspend(JSContext* cx, HandleObject obj, jsbytecode* pc) {

Просмотреть файл

@ -28,7 +28,6 @@ using namespace js;
JSObject* AbstractGeneratorObject::create(JSContext* cx,
AbstractFramePtr frame) {
MOZ_ASSERT(frame.isGeneratorFrame());
MOZ_ASSERT(frame.script()->nfixed() == 0);
MOZ_ASSERT(!frame.isConstructing());
RootedFunction fun(cx, frame.callee());
@ -65,7 +64,7 @@ void AbstractGeneratorObject::trace(JSTracer* trc) {
bool AbstractGeneratorObject::suspend(JSContext* cx, HandleObject obj,
AbstractFramePtr frame, jsbytecode* pc,
Value* vp, unsigned nvalues) {
unsigned nvalues) {
MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield ||
JSOp(*pc) == JSOp::Await);
@ -74,36 +73,24 @@ bool AbstractGeneratorObject::suspend(JSContext* cx, HandleObject obj,
MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Await, genObj->callee().isAsync());
MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Yield, genObj->callee().isGenerator());
ArrayObject* stack = nullptr;
if (nvalues > 0) {
do {
if (genObj->hasStackStorage()) {
MOZ_ASSERT(genObj->stackStorage().getDenseInitializedLength() == 0);
auto result = genObj->stackStorage().setOrExtendDenseElements(
cx, 0, vp, nvalues, ShouldUpdateTypes::DontUpdate);
if (result == DenseElementResult::Success) {
MOZ_ASSERT(genObj->stackStorage().getDenseInitializedLength() ==
nvalues);
break;
}
if (result == DenseElementResult::Failure) {
return false;
}
}
stack = NewDenseCopiedArray(cx, nvalues, vp);
ArrayObject* stack = nullptr;
if (genObj->hasStackStorage()) {
stack = &genObj->stackStorage();
} else {
stack = NewDenseEmptyArray(cx);
if (!stack) {
return false;
}
} while (false);
genObj->setStackStorage(*stack);
}
if (!frame.saveGeneratorSlots(cx, nvalues, stack)) {
return false;
}
}
genObj->setResumeIndex(pc);
genObj->setEnvironmentChain(*frame.environmentChain());
if (stack) {
genObj->setStackStorage(*stack);
}
return true;
}
@ -127,23 +114,13 @@ AbstractGeneratorObject* js::GetGeneratorObjectForFrame(
Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
Value genValue = callObj.getSlot(shape->slot());
// If the `generator; setaliasedvar ".generator"; initialyield` bytecode
// If the `Generator; SetAliasedVar ".generator"; InitialYield` bytecode
// sequence has not run yet, genValue is undefined.
return genValue.isObject()
? &genValue.toObject().as<AbstractGeneratorObject>()
: nullptr;
}
void js::SetGeneratorClosed(JSContext* cx, AbstractFramePtr frame) {
CallObject& callObj = frame.callObj();
// Get the generator object stored on the scope chain and close it.
Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
auto& genObj =
callObj.getSlot(shape->slot()).toObject().as<AbstractGeneratorObject>();
genObj.setClosed();
}
bool js::GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
Handle<AbstractGeneratorObject*> genObj,
HandleValue arg,
@ -181,12 +158,12 @@ bool AbstractGeneratorObject::resume(JSContext* cx,
}
if (genObj->hasStackStorage() && !genObj->isStackStorageEmpty()) {
uint32_t len = genObj->stackStorage().getDenseInitializedLength();
MOZ_ASSERT(activation.regs().spForStackDepth(len));
const Value* src = genObj->stackStorage().getDenseElements();
mozilla::PodCopy(activation.regs().sp, src, len);
activation.regs().sp += len;
genObj->stackStorage().setDenseInitializedLength(0);
JSScript* script = activation.regs().fp()->script();
ArrayObject* storage = &genObj->stackStorage();
uint32_t len = storage->getDenseInitializedLength();
activation.regs().fp()->restoreGeneratorSlots(storage);
activation.regs().sp += len - script->nfixed();
storage->setDenseInitializedLength(0);
}
JSScript* script = callee->nonLazyScript();

Просмотреть файл

@ -38,10 +38,6 @@ class AbstractGeneratorObject : public NativeObject {
RESERVED_SLOTS
};
private:
static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame,
jsbytecode* pc, Value* vp, unsigned nvalues);
public:
static JSObject* create(JSContext* cx, AbstractFramePtr frame);
@ -49,16 +45,8 @@ class AbstractGeneratorObject : public NativeObject {
Handle<AbstractGeneratorObject*> genObj, HandleValue arg,
HandleValue resumeKind);
static bool initialSuspend(JSContext* cx, HandleObject obj,
AbstractFramePtr frame, jsbytecode* pc) {
return suspend(cx, obj, frame, pc, nullptr, 0);
}
static bool normalSuspend(JSContext* cx, HandleObject obj,
AbstractFramePtr frame, jsbytecode* pc, Value* vp,
unsigned nvalues) {
return suspend(cx, obj, frame, pc, vp, nvalues);
}
static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame,
jsbytecode* pc, unsigned nvalues);
static void finalSuspend(HandleObject obj);
@ -210,8 +198,6 @@ bool GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
AbstractGeneratorObject* GetGeneratorObjectForFrame(JSContext* cx,
AbstractFramePtr frame);
void SetGeneratorClosed(JSContext* cx, AbstractFramePtr frame);
inline GeneratorResumeKind IntToResumeKind(int32_t value) {
MOZ_ASSERT(uint32_t(value) <= uint32_t(GeneratorResumeKind::Return));
return static_cast<GeneratorResumeKind>(value);

Просмотреть файл

@ -1274,7 +1274,8 @@ bool js::HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame,
if (cx->isClosingGenerator()) {
cx->clearPendingException();
ok = true;
SetGeneratorClosed(cx, frame);
auto* genObj = GetGeneratorObjectForFrame(cx, frame);
genObj->setClosed();
}
return ok;
}
@ -4265,8 +4266,8 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
POP_RETURN_VALUE();
MOZ_ASSERT(REGS.stackDepth() == 0);
if (!AbstractGeneratorObject::initialSuspend(cx, obj, REGS.fp(),
REGS.pc)) {
if (!AbstractGeneratorObject::suspend(cx, obj, REGS.fp(), REGS.pc,
script->nfixed())) {
goto error;
}
goto successful_return_continuation;
@ -4277,9 +4278,9 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
MOZ_ASSERT(!cx->isExceptionPending());
MOZ_ASSERT(REGS.fp()->isFunctionFrame());
ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
if (!AbstractGeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc,
REGS.spForStackDepth(0),
REGS.stackDepth() - 2)) {
if (!AbstractGeneratorObject::suspend(
cx, obj, REGS.fp(), REGS.pc,
script->nfixed() + REGS.stackDepth() - 2)) {
goto error;
}

Просмотреть файл

@ -229,6 +229,48 @@ inline void NativeObject::initDenseElements(const Value* src, uint32_t count) {
elementsRangePostWriteBarrier(0, count);
}
template <typename Iter>
inline bool NativeObject::initDenseElementsFromRange(JSContext* cx, Iter begin,
Iter end) {
// This method populates the elements of a particular Array that's an
// internal implementation detail of GeneratorObject. Failing any of the
// following means the Array has escaped and/or been mistreated.
MOZ_ASSERT(isExtensible());
MOZ_ASSERT(!isIndexed());
MOZ_ASSERT(is<ArrayObject>());
MOZ_ASSERT(as<ArrayObject>().lengthIsWritable());
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
MOZ_ASSERT(!denseElementsAreFrozen());
MOZ_ASSERT(getElementsHeader()->numShiftedElements() == 0);
MOZ_ASSERT(getDenseInitializedLength() == 0);
auto size = end - begin;
uint32_t count = uint32_t(size);
MOZ_ASSERT(count == size);
MOZ_ASSERT(count <= uint32_t(INT32_MAX));
if (count > getDenseCapacity()) {
if (!growElements(cx, count)) {
return false;
}
}
HeapSlot* sp = elements_;
size_t slot = 0;
for (; begin != end; sp++, begin++) {
Value v = *begin;
#ifdef DEBUG
checkStoredValue(v);
#endif
sp->init(this, HeapSlot::Element, slot++, v);
}
MOZ_ASSERT(slot == count);
getElementsHeader()->initializedLength = count;
as<ArrayObject>().setLengthInt32(count);
return true;
}
inline bool NativeObject::tryShiftDenseElements(uint32_t count) {
MOZ_ASSERT(isExtensible());

Просмотреть файл

@ -1408,9 +1408,24 @@ class NativeObject : public JSObject {
inline void copyDenseElements(uint32_t dstStart, const Value* src,
uint32_t count);
inline void initDenseElements(const Value* src, uint32_t count);
inline void initDenseElements(JSContext* cx, NativeObject* src,
uint32_t srcStart, uint32_t count);
// Store the Values in the range [begin, end) as elements of this array.
//
// Preconditions: This must be a boring ArrayObject with dense initialized
// length 0: no shifted elements, no frozen elements, no fixed "length", not
// indexed, not inextensible, not copy-on-write. Existing capacity is
// optional.
//
// This runs write barriers but does not update types. `end - begin` must
// return the size of the range, which must be >= 0 and fit in an int32_t.
template <typename Iter>
inline MOZ_MUST_USE bool initDenseElementsFromRange(JSContext* cx, Iter begin,
Iter end);
inline void moveDenseElements(uint32_t dstStart, uint32_t srcStart,
uint32_t count);
inline void reverseDenseElementsNoPreBarrier(uint32_t length);

Просмотреть файл

@ -12,6 +12,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h"
#include "builtin/Array.h" // js::NewDenseEmptyArray
#include "jit/BaselineFrame.h"
#include "jit/RematerializedFrame.h"
#include "js/Debug.h"
@ -186,6 +187,19 @@ inline void InterpreterFrame::unsetIsDebuggee() {
flags_ &= ~DEBUGGEE;
}
inline bool InterpreterFrame::saveGeneratorSlots(JSContext* cx, unsigned nslots,
ArrayObject* dest) const {
return dest->initDenseElementsFromRange(cx, slots(), slots() + nslots);
}
inline void InterpreterFrame::restoreGeneratorSlots(ArrayObject* src) {
MOZ_ASSERT(script()->nfixed() <= src->length());
MOZ_ASSERT(src->length() <= script()->nslots());
MOZ_ASSERT(src->getDenseInitializedLength() == src->length());
const Value* srcElements = src->getDenseElements();
mozilla::PodCopy(slots(), srcElements, src->length());
}
/*****************************************************************************/
inline void InterpreterStack::purge(JSRuntime* rt) {
@ -661,6 +675,16 @@ inline bool AbstractFramePtr::isGeneratorFrame() const {
return s->isGenerator() || s->isAsync();
}
inline bool AbstractFramePtr::saveGeneratorSlots(JSContext* cx, unsigned nslots,
ArrayObject* dest) const {
MOZ_ASSERT(isGeneratorFrame());
if (isInterpreterFrame()) {
return asInterpreterFrame()->saveGeneratorSlots(cx, nslots, dest);
}
MOZ_ASSERT(isBaselineFrame(), "unexpected generator frame in Ion");
return asBaselineFrame()->saveGeneratorSlots(cx, nslots, dest);
}
inline Value* AbstractFramePtr::argv() const {
if (isInterpreterFrame()) {
return asInterpreterFrame()->argv();

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/HashFunctions.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Span.h" // for Span
#include "mozilla/Variant.h"
#include <algorithm>
@ -213,6 +214,9 @@ class AbstractFramePtr {
inline bool isFunctionFrame() const;
inline bool isGeneratorFrame() const;
inline bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
ArrayObject* dest) const;
inline unsigned numActualArgs() const;
inline unsigned numFormalArgs() const;
@ -641,6 +645,14 @@ class InterpreterFrame {
markReturnValue();
}
// Copy values from this frame into a private Array, owned by the
// GeneratorObject, for suspending.
MOZ_MUST_USE inline bool saveGeneratorSlots(JSContext* cx, unsigned nslots,
ArrayObject* dest) const;
// Copy values from the Array into this stack frame, for resuming.
inline void restoreGeneratorSlots(ArrayObject* src);
void resumeGeneratorFrame(JSObject* envChain) {
MOZ_ASSERT(script()->isGenerator() || script()->isAsync());
MOZ_ASSERT(isFunctionFrame());