From 6e13466a0a47fb97c50f8aff7a5b2ee7eca74ac2 Mon Sep 17 00:00:00 2001 From: Debadree Chatterjee Date: Tue, 20 Aug 2024 07:13:16 +0000 Subject: [PATCH] Bug 1899500 - Implement explicit resource management in Baseline compiler. r=arai Differential Revision: https://phabricator.services.mozilla.com/D219406 --- js/moz.configure | 16 ++-------- js/src/frontend/UsingEmitter.cpp | 49 ++++++++++++++++++++++++++++++- js/src/frontend/UsingEmitter.h | 2 ++ js/src/jit/BaselineCodeGen.cpp | 50 +++++++++++++++++++++++++++----- js/src/jit/BaselineFrame.cpp | 29 ++++++++++++++++++ js/src/jit/BaselineFrame.h | 7 +++++ js/src/jit/VMFunctionList-inl.h | 6 ++++ js/src/jit/VMFunctions.cpp | 30 +++++++++++++++++++ js/src/jit/VMFunctions.h | 16 ++++++++++ js/src/vm/EnvironmentObject.h | 8 +++-- js/src/vm/Interpreter.cpp | 4 --- js/src/vm/Opcodes.h | 10 +++---- 12 files changed, 194 insertions(+), 33 deletions(-) diff --git a/js/moz.configure b/js/moz.configure index 501106f440c5..bef557549eea 100644 --- a/js/moz.configure +++ b/js/moz.configure @@ -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", diff --git a/js/src/frontend/UsingEmitter.cpp b/js/src/frontend/UsingEmitter.cpp index 1574f96788bd..d07db9c96303 100644 --- a/js/src/frontend/UsingEmitter.cpp +++ b/js/src/frontend/UsingEmitter.cpp @@ -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; } diff --git a/js/src/frontend/UsingEmitter.h b/js/src/frontend/UsingEmitter.h index 4b43931576a2..34a2f9bb3359 100644 --- a/js/src/frontend/UsingEmitter.h +++ b/js/src/frontend/UsingEmitter.h @@ -30,6 +30,8 @@ class MOZ_STACK_CLASS UsingEmitter { [[nodiscard]] bool emitThrowIfException(); + [[nodiscard]] bool emitTakeDisposeCapability(); + [[nodiscard]] bool emitDisposeLoop( EmitterScope& es, CompletionKind initialCompletion = CompletionKind::Normal); diff --git a/js/src/jit/BaselineCodeGen.cpp b/js/src/jit/BaselineCodeGen.cpp index b924f5647639..b74886e77806 100644 --- a/js/src/jit/BaselineCodeGen.cpp +++ b/js/src/jit/BaselineCodeGen.cpp @@ -4877,21 +4877,57 @@ bool BaselineCodeGen::emit_LeaveWith() { #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT template bool BaselineCodeGen::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, UsingHint); + return callVM(); } template bool BaselineCodeGen::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); + if (!callVM()) { + return false; + } + frame.push(R0); + return true; } template bool BaselineCodeGen::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::Handle, JS::MutableHandle); + + pushArg(R1); // suppressed + pushArg(R0); // error + pushArg(R2.scratchReg()); + + if (!callVM()) { + return false; + } + + frame.push(R0); + return true; } #endif diff --git a/js/src/jit/BaselineFrame.cpp b/js/src/jit/BaselineFrame.cpp index 49c70c373586..ac42796054ca 100644 --- a/js/src/jit/BaselineFrame.cpp +++ b/js/src/jit/BaselineFrame.cpp @@ -105,6 +105,35 @@ bool BaselineFrame::pushVarEnvironment(JSContext* cx, Handle scope) { return js::PushVarEnvironmentObject(cx, scope, this); } +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT +ArrayObject* BaselineFrame::getOrCreateDisposeCapability(JSContext* cx) { + JSObject* env = environmentChain(); + + if (env->is()) { + return env->as().getOrCreateDisposeCapability(cx); + } + MOZ_ASSERT(env->is()); + return env->as().getOrCreateDisposeCapability(cx); +} + +bool BaselineFrame::takeDisposeCapability( + JSContext* cx, JS::MutableHandle capability) { + JSObject* env = environmentChain(); + + if (env->is()) { + // TODO: The get & clear disposables function can be merged. (bug 1907736) + capability.set(env->as().getDisposables()); + env->as().clearDisposables(); + return true; + } + + MOZ_ASSERT(env->is()); + capability.set(env->as().getDisposables()); + env->as().clearDisposables(); + return true; +} +#endif + void BaselineFrame::setInterpreterFields(JSScript* script, jsbytecode* pc) { uint32_t pcOffset = script->pcToOffset(pc); interpreterScript_ = script; diff --git a/js/src/jit/BaselineFrame.h b/js/src/jit/BaselineFrame.h index f2a6811177b8..9b75e2e8e9b1 100644 --- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -317,6 +317,13 @@ class BaselineFrame { Handle scope); [[nodiscard]] bool pushVarEnvironment(JSContext* cx, Handle scope); +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + [[nodiscard]] ArrayObject* getOrCreateDisposeCapability(JSContext* cx); + + [[nodiscard]] bool takeDisposeCapability( + JSContext* cx, JS::MutableHandle capability); +#endif + void initArgsObjUnchecked(ArgumentsObject& argsobj) { flags_ |= HAS_ARGS_OBJ; argsObj_ = &argsobj; diff --git a/js/src/jit/VMFunctionList-inl.h b/js/src/jit/VMFunctionList-inl.h index 5423d6413970..bac9a8289f8e 100644 --- a/js/src/jit/VMFunctionList-inl.h +++ b/js/src/jit/VMFunctionList-inl.h @@ -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) \ _(StringsNotEqual, js::jit::StringsEqual) \ _(SubstringKernel, js::SubstringKernel) \ + IF_EXPLICIT_RESOURCE_MANAGEMENT( \ + _(TakeDisposeCapability, js::jit::TakeDisposeCapability)) \ _(ThrowBadDerivedReturnOrUninitializedThis, \ js::jit::ThrowBadDerivedReturnOrUninitializedThis) \ _(ThrowCheckIsObject, js::ThrowCheckIsObject) \ diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 5109af73d3b2..e56920e0a973 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1330,6 +1330,36 @@ bool PushVarEnv(JSContext* cx, BaselineFrame* frame, Handle scope) { return frame->pushVarEnvironment(cx, scope); } +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT +bool AddDisposableResource(JSContext* cx, BaselineFrame* frame, + JS::Handle val, UsingHint hint) { + JS::Rooted 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 capability) { + return frame->takeDisposeCapability(cx, capability); +} + +bool CreateSuppressedError(JSContext* cx, BaselineFrame* frame, + JS::Handle error, + JS::Handle suppressed, + JS::MutableHandle 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 templ) { return EnterWithOperation(cx, frame, val, templ); diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 23947ae31b77..430681deb251 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -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); +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT +[[nodiscard]] bool AddDisposableResource(JSContext*, BaselineFrame* frame, + JS::Handle val, + UsingHint hint); + +[[nodiscard]] bool CreateSuppressedError(JSContext* cx, BaselineFrame* frame, + JS::Handle error, + JS::Handle suppressed, + JS::MutableHandle rval); + +[[nodiscard]] bool TakeDisposeCapability( + JSContext* cx, BaselineFrame* frame, + JS::MutableHandle capability); +#endif + [[nodiscard]] bool InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame, uint32_t numStackValues); diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h index f7b5346a90af..174587ce1906 100644 --- a/js/src/vm/EnvironmentObject.h +++ b/js/src/vm/EnvironmentObject.h @@ -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 diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 9f6b7b433631..b8d4de1be8c3 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -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() - .getDenseInitializedLength()); if (env->is()) { env->as().clearDisposables(); } else { diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 131ca5b33411..2746f68f67f7 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -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).