summaryrefslogtreecommitdiffstats
path: root/js/src/jit/WarpBuilderShared.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/jit/WarpBuilderShared.h
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/WarpBuilderShared.h')
-rw-r--r--js/src/jit/WarpBuilderShared.h446
1 files changed, 446 insertions, 0 deletions
diff --git a/js/src/jit/WarpBuilderShared.h b/js/src/jit/WarpBuilderShared.h
new file mode 100644
index 0000000000..651164865d
--- /dev/null
+++ b/js/src/jit/WarpBuilderShared.h
@@ -0,0 +1,446 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_WarpBuilderShared_h
+#define jit_WarpBuilderShared_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include "jit/MIRGraph.h"
+#include "js/Value.h"
+
+namespace js {
+
+class BytecodeLocation;
+
+namespace jit {
+
+class MBasicBlock;
+class MCall;
+class MConstant;
+class MInstruction;
+class MIRGenerator;
+class TempAllocator;
+class WarpSnapshot;
+class WrappedFunction;
+
+// Helper class to manage call state.
+class MOZ_STACK_CLASS CallInfo {
+ MDefinition* callee_ = nullptr;
+ MDefinition* thisArg_ = nullptr;
+ MDefinition* newTargetArg_ = nullptr;
+ MDefinitionVector args_;
+
+ bool constructing_;
+
+ // True if the caller does not use the return value.
+ bool ignoresReturnValue_;
+
+ bool inlined_ = false;
+ bool setter_ = false;
+
+ public:
+ // For normal calls and FunCall we can shuffle around definitions in
+ // the CallInfo and use a normal MCall. For others, we need to use a
+ // specialized call.
+ enum class ArgFormat {
+ Standard,
+ Array,
+ FunApplyArgsObj,
+ };
+
+ private:
+ ArgFormat argFormat_ = ArgFormat::Standard;
+ mozilla::Maybe<ResumeMode> inliningMode_;
+
+ public:
+ CallInfo(TempAllocator& alloc, bool constructing, bool ignoresReturnValue,
+ jsbytecode* pc = nullptr)
+ : args_(alloc),
+ constructing_(constructing),
+ ignoresReturnValue_(ignoresReturnValue) {}
+
+ [[nodiscard]] bool init(MBasicBlock* current, uint32_t argc) {
+ MOZ_ASSERT(args_.empty());
+
+ // Get the arguments in the right order
+ if (!args_.reserve(argc)) {
+ return false;
+ }
+
+ if (constructing()) {
+ setNewTarget(current->pop());
+ }
+
+ for (int32_t i = argc; i > 0; i--) {
+ args_.infallibleAppend(current->peek(-i));
+ }
+ current->popn(argc);
+
+ // Get |this| and |callee|
+ setThis(current->pop());
+ setCallee(current->pop());
+
+ return true;
+ }
+
+ void initForSpreadCall(MBasicBlock* current) {
+ MOZ_ASSERT(args_.empty());
+
+ if (constructing()) {
+ setNewTarget(current->pop());
+ }
+
+ // Spread calls have one argument, an Array object containing the args.
+ static_assert(decltype(args_)::InlineLength >= 1,
+ "Appending one argument should be infallible");
+ MOZ_ALWAYS_TRUE(args_.append(current->pop()));
+
+ // Get |this| and |callee|
+ setThis(current->pop());
+ setCallee(current->pop());
+
+ argFormat_ = ArgFormat::Array;
+ }
+
+ void initForGetterCall(MDefinition* callee, MDefinition* thisVal) {
+ MOZ_ASSERT(args_.empty());
+ setCallee(callee);
+ setThis(thisVal);
+ }
+
+ void initForProxyGet(MDefinition* callee, MDefinition* handler,
+ MDefinition* target, MDefinition* id,
+ MDefinition* receiver) {
+ MOZ_ASSERT(args_.empty());
+ setCallee(callee);
+ setThis(handler);
+ static_assert(decltype(args_)::InlineLength >= 3,
+ "Appending three arguments should be infallible");
+ MOZ_ALWAYS_TRUE(args_.append(target));
+ MOZ_ALWAYS_TRUE(args_.append(id));
+ MOZ_ALWAYS_TRUE(args_.append(receiver));
+ }
+
+ void initForSetterCall(MDefinition* callee, MDefinition* thisVal,
+ MDefinition* rhs) {
+ MOZ_ASSERT(args_.empty());
+ markAsSetter();
+ setCallee(callee);
+ setThis(thisVal);
+ static_assert(decltype(args_)::InlineLength >= 1,
+ "Appending one argument should be infallible");
+ MOZ_ALWAYS_TRUE(args_.append(rhs));
+ }
+
+ void initForApplyInlinedArgs(MDefinition* callee, MDefinition* thisVal,
+ uint32_t numActuals) {
+ MOZ_ASSERT(args_.empty());
+ MOZ_ASSERT(!constructing_);
+
+ setCallee(callee);
+ setThis(thisVal);
+
+ MOZ_ASSERT(numActuals <= ArgumentsObject::MaxInlinedArgs);
+ static_assert(
+ ArgumentsObject::MaxInlinedArgs <= decltype(args_)::InlineLength,
+ "Actual arguments can be infallibly stored inline");
+ MOZ_ALWAYS_TRUE(args_.reserve(numActuals));
+ }
+
+ [[nodiscard]] bool initForApplyArray(MDefinition* callee,
+ MDefinition* thisVal,
+ uint32_t numActuals) {
+ MOZ_ASSERT(args_.empty());
+ MOZ_ASSERT(!constructing_);
+
+ setCallee(callee);
+ setThis(thisVal);
+
+ return args_.reserve(numActuals);
+ }
+
+ [[nodiscard]] bool initForConstructArray(MDefinition* callee,
+ MDefinition* thisVal,
+ MDefinition* newTarget,
+ uint32_t numActuals) {
+ MOZ_ASSERT(args_.empty());
+ MOZ_ASSERT(constructing_);
+
+ setCallee(callee);
+ setThis(thisVal);
+ setNewTarget(newTarget);
+
+ return args_.reserve(numActuals);
+ }
+
+ void initForCloseIter(MDefinition* iter, MDefinition* callee) {
+ MOZ_ASSERT(args_.empty());
+ setCallee(callee);
+ setThis(iter);
+ }
+
+ void popCallStack(MBasicBlock* current) { current->popn(numFormals()); }
+
+ [[nodiscard]] bool pushCallStack(MBasicBlock* current) {
+ current->push(callee());
+ current->push(thisArg());
+
+ for (uint32_t i = 0; i < argc(); i++) {
+ current->push(getArg(i));
+ }
+
+ if (constructing()) {
+ current->push(getNewTarget());
+ }
+
+ return true;
+ }
+
+ uint32_t argc() const { return args_.length(); }
+ uint32_t numFormals() const { return argc() + 2 + constructing(); }
+
+ [[nodiscard]] bool setArgs(const MDefinitionVector& args) {
+ MOZ_ASSERT(args_.empty());
+ return args_.appendAll(args);
+ }
+
+ MDefinitionVector& argv() { return args_; }
+
+ const MDefinitionVector& argv() const { return args_; }
+
+ MDefinition* getArg(uint32_t i) const {
+ MOZ_ASSERT(i < argc());
+ return args_[i];
+ }
+
+ void initArg(uint32_t i, MDefinition* def) {
+ MOZ_ASSERT(i == argc());
+ args_.infallibleAppend(def);
+ }
+
+ void setArg(uint32_t i, MDefinition* def) {
+ MOZ_ASSERT(i < argc());
+ args_[i] = def;
+ }
+
+ void removeArg(uint32_t i) { args_.erase(&args_[i]); }
+
+ MDefinition* thisArg() const {
+ MOZ_ASSERT(thisArg_);
+ return thisArg_;
+ }
+
+ void setThis(MDefinition* thisArg) { thisArg_ = thisArg; }
+
+ bool constructing() const { return constructing_; }
+
+ bool ignoresReturnValue() const { return ignoresReturnValue_; }
+
+ void setNewTarget(MDefinition* newTarget) {
+ MOZ_ASSERT(constructing());
+ newTargetArg_ = newTarget;
+ }
+ MDefinition* getNewTarget() const {
+ MOZ_ASSERT(newTargetArg_);
+ return newTargetArg_;
+ }
+
+ bool isSetter() const { return setter_; }
+ void markAsSetter() { setter_ = true; }
+
+ bool isInlined() const { return inlined_; }
+ void markAsInlined() { inlined_ = true; }
+
+ ResumeMode inliningResumeMode() const {
+ MOZ_ASSERT(isInlined());
+ return *inliningMode_;
+ }
+
+ void setInliningResumeMode(ResumeMode mode) {
+ MOZ_ASSERT(isInlined());
+ MOZ_ASSERT(inliningMode_.isNothing());
+ inliningMode_.emplace(mode);
+ }
+
+ MDefinition* callee() const {
+ MOZ_ASSERT(callee_);
+ return callee_;
+ }
+
+ void setCallee(MDefinition* callee) { callee_ = callee; }
+
+ template <typename Fun>
+ void forEachCallOperand(Fun& f) {
+ f(callee_);
+ f(thisArg_);
+ if (newTargetArg_) {
+ f(newTargetArg_);
+ }
+ for (uint32_t i = 0; i < argc(); i++) {
+ f(getArg(i));
+ }
+ }
+
+ // Prepend `numArgs` arguments. Calls `f(i)` for each new argument.
+ template <typename Fun>
+ [[nodiscard]] bool prependArgs(size_t numArgs, const Fun& f) {
+ size_t numArgsBefore = args_.length();
+ if (!args_.growBy(numArgs)) {
+ return false;
+ }
+ for (size_t i = numArgsBefore; i > 0; i--) {
+ args_[numArgs + i - 1] = args_[i - 1];
+ }
+ for (size_t i = 0; i < numArgs; i++) {
+ args_[i] = f(i);
+ }
+ return true;
+ }
+
+ void setImplicitlyUsedUnchecked() {
+ auto setFlag = [](MDefinition* def) { def->setImplicitlyUsedUnchecked(); };
+ forEachCallOperand(setFlag);
+ }
+
+ ArgFormat argFormat() const { return argFormat_; }
+ void setArgFormat(ArgFormat argFormat) { argFormat_ = argFormat; }
+
+ MDefinition* arrayArg() const {
+ MOZ_ASSERT(argFormat_ == ArgFormat::Array);
+ // The array argument for a spread call or FunApply is always the last
+ // argument.
+ return getArg(argc() - 1);
+ }
+};
+
+template <typename Undef>
+MCall* MakeCall(TempAllocator& alloc, Undef addUndefined, CallInfo& callInfo,
+ bool needsThisCheck, WrappedFunction* target, bool isDOMCall) {
+ MOZ_ASSERT(callInfo.argFormat() == CallInfo::ArgFormat::Standard);
+ MOZ_ASSERT_IF(needsThisCheck, !target);
+ MOZ_ASSERT_IF(isDOMCall, target->jitInfo()->type() == JSJitInfo::Method);
+
+ mozilla::Maybe<DOMObjectKind> objKind;
+ if (isDOMCall) {
+ const Shape* shape = callInfo.thisArg()->toGuardShape()->shape();
+ MOZ_ASSERT(shape->getObjectClass()->isDOMClass());
+ if (shape->isNative()) {
+ objKind.emplace(DOMObjectKind::Native);
+ } else {
+ MOZ_ASSERT(shape->isProxy());
+ objKind.emplace(DOMObjectKind::Proxy);
+ }
+ }
+
+ uint32_t targetArgs = callInfo.argc();
+
+ // Collect number of missing arguments provided that the target is
+ // scripted. Native functions are passed an explicit 'argc' parameter.
+ if (target && target->hasJitEntry()) {
+ targetArgs = std::max<uint32_t>(target->nargs(), callInfo.argc());
+ }
+
+ MCall* call =
+ MCall::New(alloc, target, targetArgs + 1 + callInfo.constructing(),
+ callInfo.argc(), callInfo.constructing(),
+ callInfo.ignoresReturnValue(), isDOMCall, objKind);
+ if (!call) {
+ return nullptr;
+ }
+
+ if (callInfo.constructing()) {
+ // Note: setThis should have been done by the caller of makeCall.
+ if (needsThisCheck) {
+ call->setNeedsThisCheck();
+ }
+
+ // Pass |new.target|
+ call->addArg(targetArgs + 1, callInfo.getNewTarget());
+ }
+
+ // Explicitly pad any missing arguments with |undefined|.
+ // This permits skipping the argumentsRectifier.
+ MOZ_ASSERT_IF(target && targetArgs > callInfo.argc(), target->hasJitEntry());
+
+ MConstant* undef = nullptr;
+ for (uint32_t i = targetArgs; i > callInfo.argc(); i--) {
+ if (!undef) {
+ undef = addUndefined();
+ }
+ if (!alloc.ensureBallast()) {
+ return nullptr;
+ }
+ call->addArg(i, undef);
+ }
+
+ // Add explicit arguments.
+ // Skip addArg(0) because it is reserved for |this|.
+ for (int32_t i = callInfo.argc() - 1; i >= 0; i--) {
+ call->addArg(i + 1, callInfo.getArg(i));
+ }
+
+ if (isDOMCall) {
+ // Now that we've told it about all the args, compute whether it's movable
+ call->computeMovable();
+ }
+
+ // Pass |this| and callee.
+ call->addArg(0, callInfo.thisArg());
+ call->initCallee(callInfo.callee());
+
+ if (target) {
+ // The callee must be a JSFunction so we don't need a Class check.
+ call->disableClassCheck();
+ }
+
+ return call;
+}
+
+// Base class for code sharing between WarpBuilder and WarpCacheIRTranspiler.
+// Because this code is used by WarpCacheIRTranspiler we should
+// generally assume that we only have access to the current basic block.
+class WarpBuilderShared {
+ WarpSnapshot& snapshot_;
+ MIRGenerator& mirGen_;
+ TempAllocator& alloc_;
+
+ protected:
+ MBasicBlock* current;
+
+ WarpBuilderShared(WarpSnapshot& snapshot, MIRGenerator& mirGen,
+ MBasicBlock* current_);
+
+ [[nodiscard]] bool resumeAfter(MInstruction* ins, BytecodeLocation loc);
+
+ MConstant* constant(const JS::Value& v);
+ void pushConstant(const JS::Value& v);
+
+ // Note: unboxObjectInfallible defaults to adding a non-movable MUnbox to
+ // ensure we don't hoist the infallible unbox before a branch checking the
+ // value type.
+ enum class IsMovable : bool { No, Yes };
+ MDefinition* unboxObjectInfallible(MDefinition* def,
+ IsMovable movable = IsMovable::No);
+
+ MCall* makeCall(CallInfo& callInfo, bool needsThisCheck,
+ WrappedFunction* target = nullptr, bool isDOMCall = false);
+ MInstruction* makeSpreadCall(CallInfo& callInfo, bool needsThisCheck,
+ bool isSameRealm = false,
+ WrappedFunction* target = nullptr);
+
+ public:
+ MBasicBlock* currentBlock() const { return current; }
+ WarpSnapshot& snapshot() const { return snapshot_; }
+ MIRGenerator& mirGen() { return mirGen_; }
+ TempAllocator& alloc() { return alloc_; }
+};
+
+} // namespace jit
+} // namespace js
+
+#endif