Bug 1899500 - Implement explicit resource management in Baseline compiler. r=arai

Differential Revision: https://phabricator.services.mozilla.com/D219406
This commit is contained in:
Debadree Chatterjee 2024-08-20 07:13:16 +00:00
parent 007f725207
commit 6e13466a0a
12 changed files with 194 additions and 33 deletions

View File

@ -239,24 +239,12 @@ set_config(
# JIT support
# =======================================================
@depends(
target,
"--enable-record-tuple",
"--enable-portable-baseline-interp",
"--enable-explicit-resource-management",
)
def jit_default(
target,
enable_record_tuple,
enable_portable_baseline_interp,
enable_explicit_resource_management,
):
@depends(target, "--enable-record-tuple", "--enable-portable-baseline-interp")
def jit_default(target, enable_record_tuple, enable_portable_baseline_interp):
if enable_record_tuple:
return False
if enable_portable_baseline_interp:
return False
if enable_explicit_resource_management:
return False
if target.cpu in (
"x86",
"x86_64",

View File

@ -16,6 +16,53 @@ using namespace js::frontend;
UsingEmitter::UsingEmitter(BytecodeEmitter* bce) : bce_(bce) {}
bool UsingEmitter::emitTakeDisposeCapability() {
if (!bce_->emit1(JSOp::TakeDisposeCapability)) {
// [stack] RESOURCES-OR-UNDEF
return false;
}
if (!bce_->emit1(JSOp::IsNullOrUndefined)) {
// [stack] RESOURCES-OR-UNDEF IS-UNDEF
return false;
}
InternalIfEmitter ifUndefined(bce_);
if (!ifUndefined.emitThenElse()) {
// [stack] UNDEFINED
return false;
}
if (!bce_->emit1(JSOp::Zero)) {
// [stack] UNDEFINED 0
return false;
}
if (!ifUndefined.emitElse()) {
// [stack] RESOURCES
return false;
}
if (!bce_->emit1(JSOp::Dup)) {
// [stack] RESOURCES RESOURCES
return false;
}
if (!bce_->emitAtomOp(JSOp::GetProp,
TaggedParserAtomIndex::WellKnown::length())) {
// [stack] RESOURCES COUNT
return false;
}
if (!ifUndefined.emitEnd()) {
// [stack] RESOURCES COUNT
return false;
}
return true;
}
bool UsingEmitter::emitThrowIfException() {
// [stack] EXC THROWING
@ -128,7 +175,7 @@ bool UsingEmitter::emitDisposeLoop(EmitterScope& es,
// no code is executed in that case.
// Step 6. Set disposeCapability.[[DisposableResourceStack]] to a new empty
// List.
if (!bce_->emit1(JSOp::TakeDisposeCapability)) {
if (!emitTakeDisposeCapability()) {
// [stack] ... RESOURCES COUNT
return false;
}

View File

@ -30,6 +30,8 @@ class MOZ_STACK_CLASS UsingEmitter {
[[nodiscard]] bool emitThrowIfException();
[[nodiscard]] bool emitTakeDisposeCapability();
[[nodiscard]] bool emitDisposeLoop(
EmitterScope& es,
CompletionKind initialCompletion = CompletionKind::Normal);

View File

@ -4877,21 +4877,57 @@ bool BaselineCodeGen<Handler>::emit_LeaveWith() {
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_AddDisposable() {
// TODO: AddDisposable to be implemented for Baseline (Bug 1899500)
MOZ_CRASH("AddDisposable has not been implemented for baseline");
frame.syncStack(0);
prepareVMCall();
masm.loadBaselineFramePtr(FramePointer, R0.scratchReg());
masm.loadValue(frame.addressOfStackValue(-1), R1);
pushUint8BytecodeOperandArg(R2.scratchReg());
pushArg(R1);
pushArg(R0.scratchReg());
using Fn =
bool (*)(JSContext*, BaselineFrame*, JS::Handle<JS::Value>, UsingHint);
return callVM<Fn, jit::AddDisposableResource>();
}
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_TakeDisposeCapability() {
// TODO: TakeDisposeCapability to be implemented for Baseline (Bug 1899500)
MOZ_CRASH("TakeDisposeCapability has not been implemented for baseline");
frame.syncStack(0);
prepareVMCall();
masm.loadBaselineFramePtr(FramePointer, R0.scratchReg());
pushArg(R0.scratchReg());
using Fn = bool (*)(JSContext*, BaselineFrame*, JS::MutableHandle<JS::Value>);
if (!callVM<Fn, jit::TakeDisposeCapability>()) {
return false;
}
frame.push(R0);
return true;
}
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_CreateSuppressedError() {
// TODO: CreateSuppressedError to be implemented for Baseline (Bug
// 1899500)
MOZ_CRASH("CreateSuppressedError has not been implemented for baseline");
frame.popRegsAndSync(2);
prepareVMCall();
masm.loadBaselineFramePtr(FramePointer, R2.scratchReg());
using Fn = bool (*)(JSContext*, BaselineFrame*, JS::Handle<JS::Value>,
JS::Handle<JS::Value>, JS::MutableHandle<JS::Value>);
pushArg(R1); // suppressed
pushArg(R0); // error
pushArg(R2.scratchReg());
if (!callVM<Fn, jit::CreateSuppressedError>()) {
return false;
}
frame.push(R0);
return true;
}
#endif

View File

@ -105,6 +105,35 @@ bool BaselineFrame::pushVarEnvironment(JSContext* cx, Handle<Scope*> scope) {
return js::PushVarEnvironmentObject(cx, scope, this);
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
ArrayObject* BaselineFrame::getOrCreateDisposeCapability(JSContext* cx) {
JSObject* env = environmentChain();
if (env->is<LexicalEnvironmentObject>()) {
return env->as<LexicalEnvironmentObject>().getOrCreateDisposeCapability(cx);
}
MOZ_ASSERT(env->is<ModuleEnvironmentObject>());
return env->as<ModuleEnvironmentObject>().getOrCreateDisposeCapability(cx);
}
bool BaselineFrame::takeDisposeCapability(
JSContext* cx, JS::MutableHandle<JS::Value> capability) {
JSObject* env = environmentChain();
if (env->is<LexicalEnvironmentObject>()) {
// TODO: The get & clear disposables function can be merged. (bug 1907736)
capability.set(env->as<LexicalEnvironmentObject>().getDisposables());
env->as<LexicalEnvironmentObject>().clearDisposables();
return true;
}
MOZ_ASSERT(env->is<ModuleEnvironmentObject>());
capability.set(env->as<ModuleEnvironmentObject>().getDisposables());
env->as<ModuleEnvironmentObject>().clearDisposables();
return true;
}
#endif
void BaselineFrame::setInterpreterFields(JSScript* script, jsbytecode* pc) {
uint32_t pcOffset = script->pcToOffset(pc);
interpreterScript_ = script;

View File

@ -317,6 +317,13 @@ class BaselineFrame {
Handle<ClassBodyScope*> scope);
[[nodiscard]] bool pushVarEnvironment(JSContext* cx, Handle<Scope*> scope);
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
[[nodiscard]] ArrayObject* getOrCreateDisposeCapability(JSContext* cx);
[[nodiscard]] bool takeDisposeCapability(
JSContext* cx, JS::MutableHandle<JS::Value> capability);
#endif
void initArgsObjUnchecked(ArgumentsObject& argsobj) {
flags_ |= HAS_ARGS_OBJ;
argsObj_ = &argsobj;

View File

@ -48,6 +48,8 @@ namespace jit {
// non-argument Values the VM wrapper should pop from the stack. This is used
// for tail calls for Baseline ICs. This list must be sorted on the name field.
#define VMFUNCTION_LIST(_) \
IF_EXPLICIT_RESOURCE_MANAGEMENT( \
_(AddDisposableResource, js::jit::AddDisposableResource)) \
_(AddOrUpdateSparseElementHelper, js::AddOrUpdateSparseElementHelper) \
_(AddSlotAndCallAddPropHook, js::AddSlotAndCallAddPropHook) \
_(ArgumentsObjectCreateForInlinedIon, \
@ -131,6 +133,8 @@ namespace jit {
_(CreateBigIntFromUint64, js::jit::CreateBigIntFromUint64) \
_(CreateGenerator, js::jit::CreateGenerator) \
_(CreateGeneratorFromFrame, js::jit::CreateGeneratorFromFrame) \
IF_EXPLICIT_RESOURCE_MANAGEMENT( \
_(CreateSuppressedError, js::jit::CreateSuppressedError)) \
_(CreateThisFromIC, js::jit::CreateThisFromIC) \
_(CreateThisFromIon, js::jit::CreateThisFromIon) \
_(DebugAfterYield, js::jit::DebugAfterYield) \
@ -329,6 +333,8 @@ namespace jit {
_(StringsEqual, js::jit::StringsEqual<js::jit::EqualityKind::Equal>) \
_(StringsNotEqual, js::jit::StringsEqual<js::jit::EqualityKind::NotEqual>) \
_(SubstringKernel, js::SubstringKernel) \
IF_EXPLICIT_RESOURCE_MANAGEMENT( \
_(TakeDisposeCapability, js::jit::TakeDisposeCapability)) \
_(ThrowBadDerivedReturnOrUninitializedThis, \
js::jit::ThrowBadDerivedReturnOrUninitializedThis) \
_(ThrowCheckIsObject, js::ThrowCheckIsObject) \

View File

@ -1330,6 +1330,36 @@ bool PushVarEnv(JSContext* cx, BaselineFrame* frame, Handle<Scope*> scope) {
return frame->pushVarEnvironment(cx, scope);
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
bool AddDisposableResource(JSContext* cx, BaselineFrame* frame,
JS::Handle<JS::Value> val, UsingHint hint) {
JS::Rooted<ArrayObject*> disposeCapability(
cx, frame->getOrCreateDisposeCapability(cx));
if (!disposeCapability) {
return false;
}
return js::AddDisposableResource(cx, disposeCapability, val, hint,
JS::NothingHandleValue);
}
bool TakeDisposeCapability(JSContext* cx, BaselineFrame* frame,
JS::MutableHandle<JS::Value> capability) {
return frame->takeDisposeCapability(cx, capability);
}
bool CreateSuppressedError(JSContext* cx, BaselineFrame* frame,
JS::Handle<JS::Value> error,
JS::Handle<JS::Value> suppressed,
JS::MutableHandle<JS::Value> rval) {
ErrorObject* errorObj = js::CreateSuppressedError(cx, error, suppressed);
if (!errorObj) {
return false;
}
rval.setObject(*errorObj);
return true;
}
#endif
bool EnterWith(JSContext* cx, BaselineFrame* frame, HandleValue val,
Handle<WithScope*> templ) {
return EnterWithOperation(cx, frame, val, templ);

View File

@ -20,6 +20,7 @@
#include "js/ScalarType.h"
#include "js/TypeDecls.h"
#include "vm/TypeofEqOperand.h"
#include "vm/UsingHint.h"
class JSJitInfo;
class JSLinearString;
@ -496,6 +497,21 @@ ArrayObject* InitRestParameter(JSContext* cx, uint32_t length, Value* rest,
[[nodiscard]] bool PushVarEnv(JSContext* cx, BaselineFrame* frame,
Handle<Scope*> scope);
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
[[nodiscard]] bool AddDisposableResource(JSContext*, BaselineFrame* frame,
JS::Handle<JS::Value> val,
UsingHint hint);
[[nodiscard]] bool CreateSuppressedError(JSContext* cx, BaselineFrame* frame,
JS::Handle<JS::Value> error,
JS::Handle<JS::Value> suppressed,
JS::MutableHandle<JS::Value> rval);
[[nodiscard]] bool TakeDisposeCapability(
JSContext* cx, BaselineFrame* frame,
JS::MutableHandle<JS::Value> capability);
#endif
[[nodiscard]] bool InitBaselineFrameForOsr(BaselineFrame* frame,
InterpreterFrame* interpFrame,
uint32_t numStackValues);

View File

@ -631,7 +631,10 @@ class ModuleEnvironmentObject : public EnvironmentObject {
static const JSClass class_;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
static constexpr uint32_t RESERVED_SLOTS = 3;
// While there are only 3 reserved slots, this needs to be set to 4, given
// there are some code expect the number of fixed slot to be same as the
// number of reserved slots for the lexical environments (bug 1913864).
static constexpr uint32_t RESERVED_SLOTS = 4;
#else
static constexpr uint32_t RESERVED_SLOTS = 2;
#endif
@ -762,7 +765,8 @@ class LexicalEnvironmentObject : public EnvironmentObject {
static const JSClass class_;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
static constexpr uint32_t RESERVED_SLOTS = 3;
// See comment on RESERVED_SLOTS in ModuleEnvironmentObject.
static constexpr uint32_t RESERVED_SLOTS = 4;
#else
static constexpr uint32_t RESERVED_SLOTS = 2;
#endif

View File

@ -2307,12 +2307,8 @@ bool MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER js::Interpret(JSContext* cx,
if (maybeDisposables.isUndefined()) {
PUSH_UNDEFINED();
PUSH_INT32(0);
} else {
PUSH_OBJECT(maybeDisposables.toObject());
PUSH_INT32(maybeDisposables.toObject()
.as<ArrayObject>()
.getDenseInitializedLength());
if (env->is<LexicalEnvironmentObject>()) {
env->as<LexicalEnvironmentObject>().clearDisposables();
} else {

View File

@ -3415,20 +3415,20 @@
*/ \
IF_EXPLICIT_RESOURCE_MANAGEMENT(MACRO(AddDisposable, add_disposable, NULL, 2, 1, 1, JOF_UINT8)) \
/*
* Get the dispose capability of the present environment object and the
* length of the same. In case the dispose capability of the environment
* Get the dispose capability of the present environment object.
* In case the dispose capability of the environment
* has already been cleared or if no disposables have been
* pushed to the capability, it shall push undefined as the dispose
* capability and 0 as the length. After extracting a non-empty dispose
* capability. After extracting a non-empty dispose
* capability, the dispose capability is cleared from the present
* environment object by setting it to undefined value.
*
* Category: Variables and scopes
* Type: Entering and leaving environments
* Operands:
* Stack: => disposeCapability, count
* Stack: => disposeCapability
*/ \
IF_EXPLICIT_RESOURCE_MANAGEMENT(MACRO(TakeDisposeCapability, take_dispose_capability, NULL, 1, 0, 2, JOF_BYTE)) \
IF_EXPLICIT_RESOURCE_MANAGEMENT(MACRO(TakeDisposeCapability, take_dispose_capability, NULL, 1, 0, 1, JOF_BYTE)) \
/*
* Push the current VariableEnvironment (the environment on the environment
* chain designated to receive new variables).