summaryrefslogtreecommitdiffstats
path: root/js/src/jit/BaselineIC.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/jit/BaselineIC.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/jit/BaselineIC.cpp')
-rw-r--r--js/src/jit/BaselineIC.cpp8719
1 files changed, 8719 insertions, 0 deletions
diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp
new file mode 100644
index 000000000..863c61161
--- /dev/null
+++ b/js/src/jit/BaselineIC.cpp
@@ -0,0 +1,8719 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#include "jit/BaselineIC.h"
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/SizePrintfMacros.h"
+#include "mozilla/TemplateLib.h"
+
+#include "jslibmath.h"
+#include "jstypes.h"
+
+#include "builtin/Eval.h"
+#include "builtin/SIMD.h"
+#include "gc/Policy.h"
+#include "jit/BaselineDebugModeOSR.h"
+#include "jit/BaselineJIT.h"
+#include "jit/InlinableNatives.h"
+#include "jit/JitSpewer.h"
+#include "jit/Linker.h"
+#include "jit/Lowering.h"
+#ifdef JS_ION_PERF
+# include "jit/PerfSpewer.h"
+#endif
+#include "jit/SharedICHelpers.h"
+#include "jit/VMFunctions.h"
+#include "js/Conversions.h"
+#include "js/GCVector.h"
+#include "vm/Opcodes.h"
+#include "vm/SelfHosting.h"
+#include "vm/TypedArrayCommon.h"
+#include "vm/TypedArrayObject.h"
+
+#include "jsboolinlines.h"
+#include "jsscriptinlines.h"
+
+#include "jit/JitFrames-inl.h"
+#include "jit/MacroAssembler-inl.h"
+#include "jit/shared/Lowering-shared-inl.h"
+#include "vm/EnvironmentObject-inl.h"
+#include "vm/Interpreter-inl.h"
+#include "vm/StringObject-inl.h"
+#include "vm/UnboxedObject-inl.h"
+
+using mozilla::DebugOnly;
+
+namespace js {
+namespace jit {
+
+//
+// WarmUpCounter_Fallback
+//
+
+
+//
+// The following data is kept in a temporary heap-allocated buffer, stored in
+// JitRuntime (high memory addresses at top, low at bottom):
+//
+// +----->+=================================+ -- <---- High Address
+// | | | |
+// | | ...BaselineFrame... | |-- Copy of BaselineFrame + stack values
+// | | | |
+// | +---------------------------------+ |
+// | | | |
+// | | ...Locals/Stack... | |
+// | | | |
+// | +=================================+ --
+// | | Padding(Maybe Empty) |
+// | +=================================+ --
+// +------|-- baselineFrame | |-- IonOsrTempData
+// | jitcode | |
+// +=================================+ -- <---- Low Address
+//
+// A pointer to the IonOsrTempData is returned.
+
+struct IonOsrTempData
+{
+ void* jitcode;
+ uint8_t* baselineFrame;
+};
+
+static IonOsrTempData*
+PrepareOsrTempData(JSContext* cx, ICWarmUpCounter_Fallback* stub, BaselineFrame* frame,
+ HandleScript script, jsbytecode* pc, void* jitcode)
+{
+ size_t numLocalsAndStackVals = frame->numValueSlots();
+
+ // Calculate the amount of space to allocate:
+ // BaselineFrame space:
+ // (sizeof(Value) * (numLocals + numStackVals))
+ // + sizeof(BaselineFrame)
+ //
+ // IonOsrTempData space:
+ // sizeof(IonOsrTempData)
+
+ size_t frameSpace = sizeof(BaselineFrame) + sizeof(Value) * numLocalsAndStackVals;
+ size_t ionOsrTempDataSpace = sizeof(IonOsrTempData);
+
+ size_t totalSpace = AlignBytes(frameSpace, sizeof(Value)) +
+ AlignBytes(ionOsrTempDataSpace, sizeof(Value));
+
+ IonOsrTempData* info = (IonOsrTempData*)cx->runtime()->getJitRuntime(cx)->allocateOsrTempData(totalSpace);
+ if (!info)
+ return nullptr;
+
+ memset(info, 0, totalSpace);
+
+ info->jitcode = jitcode;
+
+ // Copy the BaselineFrame + local/stack Values to the buffer. Arguments and
+ // |this| are not copied but left on the stack: the Baseline and Ion frame
+ // share the same frame prefix and Ion won't clobber these values. Note
+ // that info->baselineFrame will point to the *end* of the frame data, like
+ // the frame pointer register in baseline frames.
+ uint8_t* frameStart = (uint8_t*)info + AlignBytes(ionOsrTempDataSpace, sizeof(Value));
+ info->baselineFrame = frameStart + frameSpace;
+
+ memcpy(frameStart, (uint8_t*)frame - numLocalsAndStackVals * sizeof(Value), frameSpace);
+
+ JitSpew(JitSpew_BaselineOSR, "Allocated IonOsrTempData at %p", (void*) info);
+ JitSpew(JitSpew_BaselineOSR, "Jitcode is %p", info->jitcode);
+
+ // All done.
+ return info;
+}
+
+static bool
+DoWarmUpCounterFallbackOSR(JSContext* cx, BaselineFrame* frame, ICWarmUpCounter_Fallback* stub,
+ IonOsrTempData** infoPtr)
+{
+ MOZ_ASSERT(infoPtr);
+ *infoPtr = nullptr;
+
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = stub->icEntry()->pc(script);
+ MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
+
+ FallbackICSpew(cx, stub, "WarmUpCounter(%d)", int(script->pcToOffset(pc)));
+
+ if (!IonCompileScriptForBaseline(cx, frame, pc))
+ return false;
+
+ if (!script->hasIonScript() || script->ionScript()->osrPc() != pc ||
+ script->ionScript()->bailoutExpected() ||
+ frame->isDebuggee())
+ {
+ return true;
+ }
+
+ IonScript* ion = script->ionScript();
+ MOZ_ASSERT(cx->runtime()->spsProfiler.enabled() == ion->hasProfilingInstrumentation());
+ MOZ_ASSERT(ion->osrPc() == pc);
+
+ JitSpew(JitSpew_BaselineOSR, " OSR possible!");
+ void* jitcode = ion->method()->raw() + ion->osrEntryOffset();
+
+ // Prepare the temporary heap copy of the fake InterpreterFrame and actual args list.
+ JitSpew(JitSpew_BaselineOSR, "Got jitcode. Preparing for OSR into ion.");
+ IonOsrTempData* info = PrepareOsrTempData(cx, stub, frame, script, pc, jitcode);
+ if (!info)
+ return false;
+ *infoPtr = info;
+
+ return true;
+}
+
+typedef bool (*DoWarmUpCounterFallbackOSRFn)(JSContext*, BaselineFrame*,
+ ICWarmUpCounter_Fallback*, IonOsrTempData** infoPtr);
+static const VMFunction DoWarmUpCounterFallbackOSRInfo =
+ FunctionInfo<DoWarmUpCounterFallbackOSRFn>(DoWarmUpCounterFallbackOSR,
+ "DoWarmUpCounterFallbackOSR");
+
+bool
+ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, R1.scratchReg());
+
+ Label noCompiledCode;
+ // Call DoWarmUpCounterFallbackOSR to compile/check-for Ion-compiled function
+ {
+ // Push IonOsrTempData pointer storage
+ masm.subFromStackPtr(Imm32(sizeof(void*)));
+ masm.push(masm.getStackPointer());
+
+ // Push stub pointer.
+ masm.push(ICStubReg);
+
+ pushStubPayload(masm, R0.scratchReg());
+
+ if (!callVM(DoWarmUpCounterFallbackOSRInfo, masm))
+ return false;
+
+ // Pop IonOsrTempData pointer.
+ masm.pop(R0.scratchReg());
+
+ leaveStubFrame(masm);
+
+ // If no JitCode was found, then skip just exit the IC.
+ masm.branchPtr(Assembler::Equal, R0.scratchReg(), ImmPtr(nullptr), &noCompiledCode);
+ }
+
+ // Get a scratch register.
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+ Register osrDataReg = R0.scratchReg();
+ regs.take(osrDataReg);
+ regs.takeUnchecked(OsrFrameReg);
+
+ Register scratchReg = regs.takeAny();
+
+ // At this point, stack looks like:
+ // +-> [...Calling-Frame...]
+ // | [...Actual-Args/ThisV/ArgCount/Callee...]
+ // | [Descriptor]
+ // | [Return-Addr]
+ // +---[Saved-FramePtr] <-- BaselineFrameReg points here.
+ // [...Baseline-Frame...]
+
+ // Restore the stack pointer to point to the saved frame pointer.
+ masm.moveToStackPtr(BaselineFrameReg);
+
+ // Discard saved frame pointer, so that the return address is on top of
+ // the stack.
+ masm.pop(scratchReg);
+
+#ifdef DEBUG
+ // If profiler instrumentation is on, ensure that lastProfilingFrame is
+ // the frame currently being OSR-ed
+ {
+ Label checkOk;
+ AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
+ masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &checkOk);
+ masm.loadPtr(AbsoluteAddress((void*)&cx->runtime()->jitActivation), scratchReg);
+ masm.loadPtr(Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()), scratchReg);
+
+ // It may be the case that we entered the baseline frame with
+ // profiling turned off on, then in a call within a loop (i.e. a
+ // callee frame), turn on profiling, then return to this frame,
+ // and then OSR with profiling turned on. In this case, allow for
+ // lastProfilingFrame to be null.
+ masm.branchPtr(Assembler::Equal, scratchReg, ImmWord(0), &checkOk);
+
+ masm.branchStackPtr(Assembler::Equal, scratchReg, &checkOk);
+ masm.assumeUnreachable("Baseline OSR lastProfilingFrame mismatch.");
+ masm.bind(&checkOk);
+ }
+#endif
+
+ // Jump into Ion.
+ masm.loadPtr(Address(osrDataReg, offsetof(IonOsrTempData, jitcode)), scratchReg);
+ masm.loadPtr(Address(osrDataReg, offsetof(IonOsrTempData, baselineFrame)), OsrFrameReg);
+ masm.jump(scratchReg);
+
+ // No jitcode available, do nothing.
+ masm.bind(&noCompiledCode);
+ EmitReturnFromIC(masm);
+ return true;
+}
+
+//
+// TypeUpdate_Fallback
+//
+static bool
+DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, HandleValue objval,
+ HandleValue value)
+{
+ // This can get called from optimized stubs. Therefore it is not allowed to gc.
+ JS::AutoCheckCannotGC nogc;
+
+ FallbackICSpew(cx, stub->getChainFallback(), "TypeUpdate(%s)",
+ ICStub::KindString(stub->kind()));
+
+ RootedScript script(cx, frame->script());
+ RootedObject obj(cx, &objval.toObject());
+ RootedId id(cx);
+
+ switch(stub->kind()) {
+ case ICStub::SetElem_DenseOrUnboxedArray:
+ case ICStub::SetElem_DenseOrUnboxedArrayAdd: {
+ id = JSID_VOID;
+ AddTypePropertyId(cx, obj, id, value);
+ break;
+ }
+ case ICStub::SetProp_Native:
+ case ICStub::SetProp_NativeAdd:
+ case ICStub::SetProp_Unboxed: {
+ MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
+ jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script);
+ if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL)
+ id = NameToId(EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc));
+ else
+ id = NameToId(script->getName(pc));
+ AddTypePropertyId(cx, obj, id, value);
+ break;
+ }
+ case ICStub::SetProp_TypedObject: {
+ MOZ_ASSERT(obj->is<TypedObject>());
+ jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script);
+ id = NameToId(script->getName(pc));
+ if (stub->toSetProp_TypedObject()->isObjectReference()) {
+ // Ignore all values being written except plain objects. Null
+ // is included implicitly in type information for this property,
+ // and non-object non-null values will cause the stub to fail to
+ // match shortly and we will end up doing the assignment in the VM.
+ if (value.isObject())
+ AddTypePropertyId(cx, obj, id, value);
+ } else {
+ // Ignore undefined values, which are included implicitly in type
+ // information for this property.
+ if (!value.isUndefined())
+ AddTypePropertyId(cx, obj, id, value);
+ }
+ break;
+ }
+ default:
+ MOZ_CRASH("Invalid stub");
+ }
+
+ return stub->addUpdateStubForValue(cx, script /* = outerScript */, obj, id, value);
+}
+
+typedef bool (*DoTypeUpdateFallbackFn)(JSContext*, BaselineFrame*, ICUpdatedStub*, HandleValue,
+ HandleValue);
+const VMFunction DoTypeUpdateFallbackInfo =
+ FunctionInfo<DoTypeUpdateFallbackFn>(DoTypeUpdateFallback, "DoTypeUpdateFallback", NonTailCall);
+
+bool
+ICTypeUpdate_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ // Just store false into R1.scratchReg() and return.
+ masm.move32(Imm32(0), R1.scratchReg());
+ EmitReturnFromIC(masm);
+ return true;
+}
+
+bool
+ICTypeUpdate_PrimitiveSet::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label success;
+ if ((flags_ & TypeToFlag(JSVAL_TYPE_INT32)) && !(flags_ & TypeToFlag(JSVAL_TYPE_DOUBLE)))
+ masm.branchTestInt32(Assembler::Equal, R0, &success);
+
+ if (flags_ & TypeToFlag(JSVAL_TYPE_DOUBLE))
+ masm.branchTestNumber(Assembler::Equal, R0, &success);
+
+ if (flags_ & TypeToFlag(JSVAL_TYPE_UNDEFINED))
+ masm.branchTestUndefined(Assembler::Equal, R0, &success);
+
+ if (flags_ & TypeToFlag(JSVAL_TYPE_BOOLEAN))
+ masm.branchTestBoolean(Assembler::Equal, R0, &success);
+
+ if (flags_ & TypeToFlag(JSVAL_TYPE_STRING))
+ masm.branchTestString(Assembler::Equal, R0, &success);
+
+ if (flags_ & TypeToFlag(JSVAL_TYPE_SYMBOL))
+ masm.branchTestSymbol(Assembler::Equal, R0, &success);
+
+ // Currently, we will never generate primitive stub checks for object. However,
+ // when we do get to the point where we want to collapse our monitor chains of
+ // objects and singletons down (when they get too long) to a generic "any object"
+ // in coordination with the typeset doing the same thing, this will need to
+ // be re-enabled.
+ /*
+ if (flags_ & TypeToFlag(JSVAL_TYPE_OBJECT))
+ masm.branchTestObject(Assembler::Equal, R0, &success);
+ */
+ MOZ_ASSERT(!(flags_ & TypeToFlag(JSVAL_TYPE_OBJECT)));
+
+ if (flags_ & TypeToFlag(JSVAL_TYPE_NULL))
+ masm.branchTestNull(Assembler::Equal, R0, &success);
+
+ EmitStubGuardFailure(masm);
+
+ // Type matches, load true into R1.scratchReg() and return.
+ masm.bind(&success);
+ masm.mov(ImmWord(1), R1.scratchReg());
+ EmitReturnFromIC(masm);
+
+ return true;
+}
+
+bool
+ICTypeUpdate_SingleObject::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ // Guard on the object's identity.
+ Register obj = masm.extractObject(R0, R1.scratchReg());
+ Address expectedObject(ICStubReg, ICTypeUpdate_SingleObject::offsetOfObject());
+ masm.branchPtr(Assembler::NotEqual, expectedObject, obj, &failure);
+
+ // Identity matches, load true into R1.scratchReg() and return.
+ masm.mov(ImmWord(1), R1.scratchReg());
+ EmitReturnFromIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICTypeUpdate_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ // Guard on the object's ObjectGroup.
+ Register obj = masm.extractObject(R0, R1.scratchReg());
+ masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), R1.scratchReg());
+
+ Address expectedGroup(ICStubReg, ICTypeUpdate_ObjectGroup::offsetOfGroup());
+ masm.branchPtr(Assembler::NotEqual, expectedGroup, R1.scratchReg(), &failure);
+
+ // Group matches, load true into R1.scratchReg() and return.
+ masm.mov(ImmWord(1), R1.scratchReg());
+ EmitReturnFromIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
+static const VMFunction DoCallNativeGetterInfo =
+ FunctionInfo<DoCallNativeGetterFn>(DoCallNativeGetter, "DoCallNativeGetter");
+
+//
+// ToBool_Fallback
+//
+
+static bool
+DoToBoolFallback(JSContext* cx, BaselineFrame* frame, ICToBool_Fallback* stub, HandleValue arg,
+ MutableHandleValue ret)
+{
+ FallbackICSpew(cx, stub, "ToBool");
+
+ bool cond = ToBoolean(arg);
+ ret.setBoolean(cond);
+
+ // Check to see if a new stub should be generated.
+ if (stub->numOptimizedStubs() >= ICToBool_Fallback::MAX_OPTIMIZED_STUBS) {
+ // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
+ // But for now we just bail.
+ return true;
+ }
+
+ MOZ_ASSERT(!arg.isBoolean());
+
+ JSScript* script = frame->script();
+
+ // Try to generate new stubs.
+ if (arg.isInt32()) {
+ JitSpew(JitSpew_BaselineIC, " Generating ToBool(Int32) stub.");
+ ICToBool_Int32::Compiler compiler(cx);
+ ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(script));
+ if (!int32Stub)
+ return false;
+
+ stub->addNewStub(int32Stub);
+ return true;
+ }
+
+ if (arg.isDouble() && cx->runtime()->jitSupportsFloatingPoint) {
+ JitSpew(JitSpew_BaselineIC, " Generating ToBool(Double) stub.");
+ ICToBool_Double::Compiler compiler(cx);
+ ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!doubleStub)
+ return false;
+
+ stub->addNewStub(doubleStub);
+ return true;
+ }
+
+ if (arg.isString()) {
+ JitSpew(JitSpew_BaselineIC, " Generating ToBool(String) stub");
+ ICToBool_String::Compiler compiler(cx);
+ ICStub* stringStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!stringStub)
+ return false;
+
+ stub->addNewStub(stringStub);
+ return true;
+ }
+
+ if (arg.isNull() || arg.isUndefined()) {
+ ICToBool_NullUndefined::Compiler compiler(cx);
+ ICStub* nilStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!nilStub)
+ return false;
+
+ stub->addNewStub(nilStub);
+ return true;
+ }
+
+ if (arg.isObject()) {
+ JitSpew(JitSpew_BaselineIC, " Generating ToBool(Object) stub.");
+ ICToBool_Object::Compiler compiler(cx);
+ ICStub* objStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!objStub)
+ return false;
+
+ stub->addNewStub(objStub);
+ return true;
+ }
+
+ return true;
+}
+
+typedef bool (*pf)(JSContext*, BaselineFrame*, ICToBool_Fallback*, HandleValue,
+ MutableHandleValue);
+static const VMFunction fun = FunctionInfo<pf>(DoToBoolFallback, "DoToBoolFallback", TailCall);
+
+bool
+ICToBool_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+ MOZ_ASSERT(R0 == JSReturnOperand);
+
+ // Restore the tail call register.
+ EmitRestoreTailCallReg(masm);
+
+ // Push arguments.
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(fun, masm);
+}
+
+//
+// ToBool_Int32
+//
+
+bool
+ICToBool_Int32::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
+
+ Label ifFalse;
+ masm.branchTestInt32Truthy(false, R0, &ifFalse);
+
+ masm.moveValue(BooleanValue(true), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&ifFalse);
+ masm.moveValue(BooleanValue(false), R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// ToBool_String
+//
+
+bool
+ICToBool_String::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ masm.branchTestString(Assembler::NotEqual, R0, &failure);
+
+ Label ifFalse;
+ masm.branchTestStringTruthy(false, R0, &ifFalse);
+
+ masm.moveValue(BooleanValue(true), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&ifFalse);
+ masm.moveValue(BooleanValue(false), R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// ToBool_NullUndefined
+//
+
+bool
+ICToBool_NullUndefined::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure, ifFalse;
+ masm.branchTestNull(Assembler::Equal, R0, &ifFalse);
+ masm.branchTestUndefined(Assembler::NotEqual, R0, &failure);
+
+ masm.bind(&ifFalse);
+ masm.moveValue(BooleanValue(false), R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// ToBool_Double
+//
+
+bool
+ICToBool_Double::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure, ifTrue;
+ masm.branchTestDouble(Assembler::NotEqual, R0, &failure);
+ masm.unboxDouble(R0, FloatReg0);
+ masm.branchTestDoubleTruthy(true, FloatReg0, &ifTrue);
+
+ masm.moveValue(BooleanValue(false), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&ifTrue);
+ masm.moveValue(BooleanValue(true), R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// ToBool_Object
+//
+
+bool
+ICToBool_Object::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure, ifFalse, slowPath;
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ Register objReg = masm.extractObject(R0, ExtractTemp0);
+ Register scratch = R1.scratchReg();
+ masm.branchTestObjectTruthy(false, objReg, scratch, &slowPath, &ifFalse);
+
+ // If object doesn't emulate undefined, it evaulates to true.
+ masm.moveValue(BooleanValue(true), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&ifFalse);
+ masm.moveValue(BooleanValue(false), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&slowPath);
+ masm.setupUnalignedABICall(scratch);
+ masm.passABIArg(objReg);
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::EmulatesUndefined));
+ masm.convertBoolToInt32(ReturnReg, ReturnReg);
+ masm.xor32(Imm32(1), ReturnReg);
+ masm.tagValue(JSVAL_TYPE_BOOLEAN, ReturnReg, R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// ToNumber_Fallback
+//
+
+static bool
+DoToNumberFallback(JSContext* cx, ICToNumber_Fallback* stub, HandleValue arg, MutableHandleValue ret)
+{
+ FallbackICSpew(cx, stub, "ToNumber");
+ ret.set(arg);
+ return ToNumber(cx, ret);
+}
+
+typedef bool (*DoToNumberFallbackFn)(JSContext*, ICToNumber_Fallback*, HandleValue, MutableHandleValue);
+static const VMFunction DoToNumberFallbackInfo =
+ FunctionInfo<DoToNumberFallbackFn>(DoToNumberFallback, "DoToNumberFallback", TailCall,
+ PopValues(1));
+
+bool
+ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+ MOZ_ASSERT(R0 == JSReturnOperand);
+
+ // Restore the tail call register.
+ EmitRestoreTailCallReg(masm);
+
+ // Ensure stack is fully synced for the expression decompiler.
+ masm.pushValue(R0);
+
+ // Push arguments.
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+
+ return tailCallVM(DoToNumberFallbackInfo, masm);
+}
+
+//
+// GetElem_Fallback
+//
+
+static Shape*
+LastPropertyForSetProp(JSObject* obj)
+{
+ if (obj->isNative())
+ return obj->as<NativeObject>().lastProperty();
+
+ if (obj->is<UnboxedPlainObject>()) {
+ UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
+ return expando ? expando->lastProperty() : nullptr;
+ }
+
+ return nullptr;
+}
+
+static bool
+IsCacheableSetPropWriteSlot(JSObject* obj, Shape* oldShape, Shape* propertyShape)
+{
+ // Object shape must not have changed during the property set.
+ if (LastPropertyForSetProp(obj) != oldShape)
+ return false;
+
+ if (!propertyShape->hasSlot() ||
+ !propertyShape->hasDefaultSetter() ||
+ !propertyShape->writable())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape,
+ jsid id, Shape* propertyShape, size_t* protoChainDepth)
+{
+ // The property must be the last added property of the object.
+ if (LastPropertyForSetProp(obj) != propertyShape)
+ return false;
+
+ // Object must be extensible, oldShape must be immediate parent of current shape.
+ if (!obj->nonProxyIsExtensible() || propertyShape->previous() != oldShape)
+ return false;
+
+ // Basic shape checks.
+ if (propertyShape->inDictionary() ||
+ !propertyShape->hasSlot() ||
+ !propertyShape->hasDefaultSetter() ||
+ !propertyShape->writable())
+ {
+ return false;
+ }
+
+ // Watch out for resolve or addProperty hooks.
+ if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj) ||
+ obj->getClass()->getAddProperty())
+ {
+ return false;
+ }
+
+ size_t chainDepth = 0;
+ // Walk up the object prototype chain and ensure that all prototypes are
+ // native, and that all prototypes have no setter defined on the property.
+ for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) {
+ chainDepth++;
+ // if prototype is non-native, don't optimize
+ if (!proto->isNative())
+ return false;
+
+ MOZ_ASSERT(proto->hasStaticPrototype());
+
+ // if prototype defines this property in a non-plain way, don't optimize
+ Shape* protoShape = proto->as<NativeObject>().lookup(cx, id);
+ if (protoShape && !protoShape->hasDefaultSetter())
+ return false;
+
+ // Otherwise, if there's no such property, watch out for a resolve hook
+ // that would need to be invoked and thus prevent inlining of property
+ // addition.
+ if (ClassMayResolveId(cx->names(), proto->getClass(), id, proto))
+ return false;
+ }
+
+ // Only add a IC entry if the dynamic slots didn't change when the shapes
+ // changed. Need to ensure that a shape change for a subsequent object
+ // won't involve reallocating the slot array.
+ if (NativeObject::dynamicSlotsCount(propertyShape) != NativeObject::dynamicSlotsCount(oldShape))
+ return false;
+
+ *protoChainDepth = chainDepth;
+ return true;
+}
+
+static bool
+IsCacheableSetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape,
+ bool* isScripted, bool* isTemporarilyUnoptimizable)
+{
+ MOZ_ASSERT(isScripted);
+
+ if (!shape || !IsCacheableProtoChain(obj, holder))
+ return false;
+
+ if (shape->hasSlot() || shape->hasDefaultSetter())
+ return false;
+
+ if (!shape->hasSetterValue())
+ return false;
+
+ if (!shape->setterValue().isObject() || !shape->setterObject()->is<JSFunction>())
+ return false;
+
+ JSFunction* func = &shape->setterObject()->as<JSFunction>();
+
+ if (IsWindow(obj)) {
+ if (!func->isNative())
+ return false;
+
+ if (!func->jitInfo() || func->jitInfo()->needsOuterizedThisObject())
+ return false;
+ }
+
+ if (func->isNative()) {
+ *isScripted = false;
+ return true;
+ }
+
+ if (!func->hasJITCode()) {
+ *isTemporarilyUnoptimizable = true;
+ return false;
+ }
+
+ *isScripted = true;
+ return true;
+}
+
+template <class T>
+static bool
+GetElemNativeStubExists(ICGetElem_Fallback* stub, HandleObject obj, HandleObject holder,
+ Handle<T> key, bool needsAtomize)
+{
+ bool indirect = (obj.get() != holder.get());
+ MOZ_ASSERT_IF(indirect, holder->isNative());
+
+ for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
+ if (iter->kind() != ICStub::GetElem_NativeSlotName &&
+ iter->kind() != ICStub::GetElem_NativeSlotSymbol &&
+ iter->kind() != ICStub::GetElem_NativePrototypeSlotName &&
+ iter->kind() != ICStub::GetElem_NativePrototypeSlotSymbol &&
+ iter->kind() != ICStub::GetElem_NativePrototypeCallNativeName &&
+ iter->kind() != ICStub::GetElem_NativePrototypeCallNativeSymbol &&
+ iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedName &&
+ iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedSymbol)
+ {
+ continue;
+ }
+
+ if (indirect && (iter->kind() != ICStub::GetElem_NativePrototypeSlotName &&
+ iter->kind() != ICStub::GetElem_NativePrototypeSlotSymbol &&
+ iter->kind() != ICStub::GetElem_NativePrototypeCallNativeName &&
+ iter->kind() != ICStub::GetElem_NativePrototypeCallNativeSymbol &&
+ iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedName &&
+ iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedSymbol))
+ {
+ continue;
+ }
+
+ if(mozilla::IsSame<T, JS::Symbol*>::value !=
+ static_cast<ICGetElemNativeStub*>(*iter)->isSymbol())
+ {
+ continue;
+ }
+
+ ICGetElemNativeStubImpl<T>* getElemNativeStub =
+ reinterpret_cast<ICGetElemNativeStubImpl<T>*>(*iter);
+ if (key != getElemNativeStub->key())
+ continue;
+
+ if (ReceiverGuard(obj) != getElemNativeStub->receiverGuard())
+ continue;
+
+ // If the new stub needs atomization, and the old stub doesn't atomize, then
+ // an appropriate stub doesn't exist.
+ if (needsAtomize && !getElemNativeStub->needsAtomize())
+ continue;
+
+ // For prototype gets, check the holder and holder shape.
+ if (indirect) {
+ if (iter->isGetElem_NativePrototypeSlotName() ||
+ iter->isGetElem_NativePrototypeSlotSymbol()) {
+ ICGetElem_NativePrototypeSlot<T>* protoStub =
+ reinterpret_cast<ICGetElem_NativePrototypeSlot<T>*>(*iter);
+
+ if (holder != protoStub->holder())
+ continue;
+
+ if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape())
+ continue;
+ } else {
+ MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNativeName() ||
+ iter->isGetElem_NativePrototypeCallNativeSymbol() ||
+ iter->isGetElem_NativePrototypeCallScriptedName() ||
+ iter->isGetElem_NativePrototypeCallScriptedSymbol());
+
+ ICGetElemNativePrototypeCallStub<T>* protoStub =
+ reinterpret_cast<ICGetElemNativePrototypeCallStub<T>*>(*iter);
+
+ if (holder != protoStub->holder())
+ continue;
+
+ if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape())
+ continue;
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
+template <class T>
+static void
+RemoveExistingGetElemNativeStubs(JSContext* cx, ICGetElem_Fallback* stub, HandleObject obj,
+ HandleObject holder, Handle<T> key, bool needsAtomize)
+{
+ bool indirect = (obj.get() != holder.get());
+ MOZ_ASSERT_IF(indirect, holder->isNative());
+
+ for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
+ switch (iter->kind()) {
+ case ICStub::GetElem_NativeSlotName:
+ case ICStub::GetElem_NativeSlotSymbol:
+ if (indirect)
+ continue;
+ MOZ_FALLTHROUGH;
+ case ICStub::GetElem_NativePrototypeSlotName:
+ case ICStub::GetElem_NativePrototypeSlotSymbol:
+ case ICStub::GetElem_NativePrototypeCallNativeName:
+ case ICStub::GetElem_NativePrototypeCallNativeSymbol:
+ case ICStub::GetElem_NativePrototypeCallScriptedName:
+ case ICStub::GetElem_NativePrototypeCallScriptedSymbol:
+ break;
+ default:
+ continue;
+ }
+
+ if(mozilla::IsSame<T, JS::Symbol*>::value !=
+ static_cast<ICGetElemNativeStub*>(*iter)->isSymbol())
+ {
+ continue;
+ }
+
+ ICGetElemNativeStubImpl<T>* getElemNativeStub =
+ reinterpret_cast<ICGetElemNativeStubImpl<T>*>(*iter);
+ if (key != getElemNativeStub->key())
+ continue;
+
+ if (ReceiverGuard(obj) != getElemNativeStub->receiverGuard())
+ continue;
+
+ // For prototype gets, check the holder and holder shape.
+ if (indirect) {
+ if (iter->isGetElem_NativePrototypeSlotName() ||
+ iter->isGetElem_NativePrototypeSlotSymbol()) {
+ ICGetElem_NativePrototypeSlot<T>* protoStub =
+ reinterpret_cast<ICGetElem_NativePrototypeSlot<T>*>(*iter);
+
+ if (holder != protoStub->holder())
+ continue;
+
+ // If the holder matches, but the holder's lastProperty doesn't match, then
+ // this stub is invalid anyway. Unlink it.
+ if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape()) {
+ iter.unlink(cx);
+ continue;
+ }
+ } else {
+ MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNativeName() ||
+ iter->isGetElem_NativePrototypeCallNativeSymbol() ||
+ iter->isGetElem_NativePrototypeCallScriptedName() ||
+ iter->isGetElem_NativePrototypeCallScriptedSymbol());
+ ICGetElemNativePrototypeCallStub<T>* protoStub =
+ reinterpret_cast<ICGetElemNativePrototypeCallStub<T>*>(*iter);
+
+ if (holder != protoStub->holder())
+ continue;
+
+ // If the holder matches, but the holder's lastProperty doesn't match, then
+ // this stub is invalid anyway. Unlink it.
+ if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape()) {
+ iter.unlink(cx);
+ continue;
+ }
+ }
+ }
+
+ // If the new stub needs atomization, and the old stub doesn't atomize, then
+ // remove the old stub.
+ if (needsAtomize && !getElemNativeStub->needsAtomize()) {
+ iter.unlink(cx);
+ continue;
+ }
+
+ // Should never get here, because this means a matching stub exists, and if
+ // a matching stub exists, this procedure should never have been called.
+ MOZ_CRASH("Procedure should never have been called.");
+ }
+}
+
+static bool
+TypedArrayGetElemStubExists(ICGetElem_Fallback* stub, HandleObject obj)
+{
+ for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
+ if (!iter->isGetElem_TypedArray())
+ continue;
+ if (obj->maybeShape() == iter->toGetElem_TypedArray()->shape())
+ return true;
+ }
+ return false;
+}
+
+static bool
+ArgumentsGetElemStubExists(ICGetElem_Fallback* stub, ICGetElem_Arguments::Which which)
+{
+ for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
+ if (!iter->isGetElem_Arguments())
+ continue;
+ if (iter->toGetElem_Arguments()->which() == which)
+ return true;
+ }
+ return false;
+}
+
+template <class T>
+static T
+getKey(jsid id)
+{
+ MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
+ return false;
+}
+
+template <>
+JS::Symbol* getKey<JS::Symbol*>(jsid id)
+{
+ if (!JSID_IS_SYMBOL(id))
+ return nullptr;
+ return JSID_TO_SYMBOL(id);
+}
+
+template <>
+PropertyName* getKey<PropertyName*>(jsid id)
+{
+ uint32_t dummy;
+ if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
+ return nullptr;
+ return JSID_TO_ATOM(id)->asPropertyName();
+}
+
+static bool
+IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId idp)
+{
+ if (!key.isString())
+ return false;
+
+ // Convert to interned property name.
+ if (!ValueToId<CanGC>(cx, key, idp))
+ return false;
+
+ uint32_t dummy;
+ if (!JSID_IS_ATOM(idp) || JSID_TO_ATOM(idp)->isIndex(&dummy))
+ return false;
+
+ return true;
+}
+
+template <class T>
+static bool
+checkAtomize(HandleValue key)
+{
+ MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
+ return false;
+}
+
+template <>
+bool checkAtomize<JS::Symbol*>(HandleValue key)
+{
+ return false;
+}
+
+template <>
+bool checkAtomize<PropertyName*>(HandleValue key)
+{
+ return !key.toString()->isAtom();
+}
+
+template <class T>
+static bool
+TryAttachNativeOrUnboxedGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* pc,
+ ICGetElem_Fallback* stub, HandleObject obj,
+ HandleValue keyVal, bool* attached)
+{
+ MOZ_ASSERT(keyVal.isString() || keyVal.isSymbol());
+
+ // Convert to id.
+ RootedId id(cx);
+ if (!ValueToId<CanGC>(cx, keyVal, &id))
+ return false;
+
+ Rooted<T> key(cx, getKey<T>(id));
+ if (!key)
+ return true;
+ bool needsAtomize = checkAtomize<T>(keyVal);
+
+ RootedShape shape(cx);
+ RootedObject holder(cx);
+ if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
+ return false;
+ if (!holder || (holder != obj && !holder->isNative()))
+ return true;
+
+ // If a suitable stub already exists, nothing else to do.
+ if (GetElemNativeStubExists<T>(stub, obj, holder, key, needsAtomize))
+ return true;
+
+ // Remove any existing stubs that may interfere with the new stub being added.
+ RemoveExistingGetElemNativeStubs<T>(cx, stub, obj, holder, key, needsAtomize);
+
+ ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
+
+ if (obj->is<UnboxedPlainObject>() && holder == obj) {
+ const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
+
+ // Once unboxed objects support symbol-keys, we need to change the following accordingly
+ MOZ_ASSERT_IF(!keyVal.isString(), !property);
+
+ if (property) {
+ if (!cx->runtime()->jitSupportsFloatingPoint)
+ return true;
+
+ RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
+ ICGetElemNativeCompiler<PropertyName*> compiler(cx, ICStub::GetElem_UnboxedPropertyName,
+ monitorStub, obj, holder,
+ name,
+ ICGetElemNativeStub::UnboxedProperty,
+ needsAtomize, property->offset +
+ UnboxedPlainObject::offsetOfData(),
+ property->type);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ Shape* shape = obj->as<UnboxedPlainObject>().maybeExpando()->lookup(cx, id);
+ if (!shape->hasDefaultGetter() || !shape->hasSlot())
+ return true;
+
+ bool isFixedSlot;
+ uint32_t offset;
+ GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
+
+ ICGetElemNativeStub::AccessType acctype =
+ isFixedSlot ? ICGetElemNativeStub::FixedSlot
+ : ICGetElemNativeStub::DynamicSlot;
+ ICGetElemNativeCompiler<T> compiler(cx, getGetElemStubKind<T>(ICStub::GetElem_NativeSlotName),
+ monitorStub, obj, holder, key,
+ acctype, needsAtomize, offset);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ if (!holder->isNative())
+ return true;
+
+ if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
+ bool isFixedSlot;
+ uint32_t offset;
+ GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
+
+ ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlotName
+ : ICStub::GetElem_NativePrototypeSlotName;
+ kind = getGetElemStubKind<T>(kind);
+
+ JitSpew(JitSpew_BaselineIC, " Generating GetElem(Native %s%s slot) stub "
+ "(obj=%p, holder=%p, holderShape=%p)",
+ (obj == holder) ? "direct" : "prototype",
+ needsAtomize ? " atomizing" : "",
+ obj.get(), holder.get(), holder->as<NativeObject>().lastProperty());
+
+ AccType acctype = isFixedSlot ? ICGetElemNativeStub::FixedSlot
+ : ICGetElemNativeStub::DynamicSlot;
+ ICGetElemNativeCompiler<T> compiler(cx, kind, monitorStub, obj, holder, key,
+ acctype, needsAtomize, offset);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ return true;
+}
+
+template <class T>
+static bool
+TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecode* pc,
+ ICGetElem_Fallback* stub, HandleNativeObject obj,
+ HandleValue keyVal, bool* attached,
+ bool* isTemporarilyUnoptimizable)
+{
+ MOZ_ASSERT(!*attached);
+ MOZ_ASSERT(keyVal.isString() || keyVal.isSymbol());
+
+ RootedId id(cx);
+ if (!ValueToId<CanGC>(cx, keyVal, &id))
+ return false;
+
+ Rooted<T> key(cx, getKey<T>(id));
+ if (!key)
+ return true;
+ bool needsAtomize = checkAtomize<T>(keyVal);
+
+ RootedShape shape(cx);
+ RootedObject baseHolder(cx);
+ if (!EffectlesslyLookupProperty(cx, obj, id, &baseHolder, &shape))
+ return false;
+ if (!baseHolder || !baseHolder->isNative())
+ return true;
+
+ HandleNativeObject holder = baseHolder.as<NativeObject>();
+
+ bool getterIsScripted = false;
+ if (IsCacheableGetPropCall(cx, obj, baseHolder, shape, &getterIsScripted,
+ isTemporarilyUnoptimizable, /*isDOMProxy=*/false))
+ {
+ RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
+
+ // For now, we do not handle own property getters
+ if (obj == holder)
+ return true;
+
+ // If a suitable stub already exists, nothing else to do.
+ if (GetElemNativeStubExists<T>(stub, obj, holder, key, needsAtomize))
+ return true;
+
+ // Remove any existing stubs that may interfere with the new stub being added.
+ RemoveExistingGetElemNativeStubs<T>(cx, stub, obj, holder, key, needsAtomize);
+
+ ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
+ ICStub::Kind kind = getterIsScripted ? ICStub::GetElem_NativePrototypeCallScriptedName
+ : ICStub::GetElem_NativePrototypeCallNativeName;
+ kind = getGetElemStubKind<T>(kind);
+
+ if (getterIsScripted) {
+ JitSpew(JitSpew_BaselineIC,
+ " Generating GetElem(Native %s%s call scripted %s:%" PRIuSIZE ") stub "
+ "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
+ (obj == holder) ? "direct" : "prototype",
+ needsAtomize ? " atomizing" : "",
+ getter->nonLazyScript()->filename(), getter->nonLazyScript()->lineno(),
+ obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty());
+ } else {
+ JitSpew(JitSpew_BaselineIC,
+ " Generating GetElem(Native %s%s call native) stub "
+ "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
+ (obj == holder) ? "direct" : "prototype",
+ needsAtomize ? " atomizing" : "",
+ obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty());
+ }
+
+ AccType acctype = getterIsScripted ? ICGetElemNativeStub::ScriptedGetter
+ : ICGetElemNativeStub::NativeGetter;
+ ICGetElemNativeCompiler<T> compiler(cx, kind, monitorStub, obj, holder, key, acctype,
+ needsAtomize, getter, script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ return true;
+}
+
+static bool
+IsPrimitiveArrayTypedObject(JSObject* obj)
+{
+ if (!obj->is<TypedObject>())
+ return false;
+ TypeDescr& descr = obj->as<TypedObject>().typeDescr();
+ return descr.is<ArrayTypeDescr>() &&
+ descr.as<ArrayTypeDescr>().elementType().is<ScalarTypeDescr>();
+}
+
+static Scalar::Type
+PrimitiveArrayTypedObjectType(JSObject* obj)
+{
+ MOZ_ASSERT(IsPrimitiveArrayTypedObject(obj));
+ TypeDescr& descr = obj->as<TypedObject>().typeDescr();
+ return descr.as<ArrayTypeDescr>().elementType().as<ScalarTypeDescr>().type();
+}
+
+static Scalar::Type
+TypedThingElementType(JSObject* obj)
+{
+ return obj->is<TypedArrayObject>()
+ ? obj->as<TypedArrayObject>().type()
+ : PrimitiveArrayTypedObjectType(obj);
+}
+
+static bool
+TypedThingRequiresFloatingPoint(JSObject* obj)
+{
+ Scalar::Type type = TypedThingElementType(obj);
+ return type == Scalar::Uint32 ||
+ type == Scalar::Float32 ||
+ type == Scalar::Float64;
+}
+
+static bool
+IsNativeDenseElementAccess(HandleObject obj, HandleValue key)
+{
+ if (obj->isNative() && key.isInt32() && key.toInt32() >= 0 && !obj->is<TypedArrayObject>())
+ return true;
+ return false;
+}
+
+static bool
+IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key)
+{
+ if (!obj->isNative() && !obj->is<UnboxedArrayObject>())
+ return false;
+ if (key.isInt32() && key.toInt32() >= 0 && !obj->is<TypedArrayObject>())
+ return true;
+ return false;
+}
+
+static bool
+TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_Fallback* stub,
+ HandleValue lhs, HandleValue rhs, HandleValue res, bool* attached)
+{
+ // Check for String[i] => Char accesses.
+ if (lhs.isString() && rhs.isInt32() && res.isString() &&
+ !stub->hasStub(ICStub::GetElem_String))
+ {
+ // NoSuchMethod handling doesn't apply to string targets.
+
+ JitSpew(JitSpew_BaselineIC, " Generating GetElem(String[Int32]) stub");
+ ICGetElem_String::Compiler compiler(cx);
+ ICStub* stringStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!stringStub)
+ return false;
+
+ stub->addNewStub(stringStub);
+ *attached = true;
+ return true;
+ }
+
+ if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS) && rhs.isInt32() &&
+ !ArgumentsGetElemStubExists(stub, ICGetElem_Arguments::Magic))
+ {
+ JitSpew(JitSpew_BaselineIC, " Generating GetElem(MagicArgs[Int32]) stub");
+ ICGetElem_Arguments::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ ICGetElem_Arguments::Magic);
+ ICStub* argsStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!argsStub)
+ return false;
+
+ stub->addNewStub(argsStub);
+ *attached = true;
+ return true;
+ }
+
+ // Otherwise, GetElem is only optimized on objects.
+ if (!lhs.isObject())
+ return true;
+ RootedObject obj(cx, &lhs.toObject());
+
+ // Check for ArgumentsObj[int] accesses
+ if (obj->is<ArgumentsObject>() && rhs.isInt32() &&
+ !obj->as<ArgumentsObject>().hasOverriddenElement())
+ {
+ ICGetElem_Arguments::Which which = ICGetElem_Arguments::Mapped;
+ if (obj->is<UnmappedArgumentsObject>())
+ which = ICGetElem_Arguments::Unmapped;
+ if (!ArgumentsGetElemStubExists(stub, which)) {
+ JitSpew(JitSpew_BaselineIC, " Generating GetElem(ArgsObj[Int32]) stub");
+ ICGetElem_Arguments::Compiler compiler(
+ cx, stub->fallbackMonitorStub()->firstMonitorStub(), which);
+ ICStub* argsStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!argsStub)
+ return false;
+
+ stub->addNewStub(argsStub);
+ *attached = true;
+ return true;
+ }
+ }
+
+ // Check for NativeObject[int] dense accesses.
+ if (IsNativeDenseElementAccess(obj, rhs)) {
+ JitSpew(JitSpew_BaselineIC, " Generating GetElem(Native[Int32] dense) stub");
+ ICGetElem_Dense::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ obj->as<NativeObject>().lastProperty());
+ ICStub* denseStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!denseStub)
+ return false;
+
+ stub->addNewStub(denseStub);
+ *attached = true;
+ return true;
+ }
+
+ // Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses.
+ if (obj->isNative() || obj->is<UnboxedPlainObject>()) {
+ RootedScript rootedScript(cx, script);
+ if (rhs.isString()) {
+ if (!TryAttachNativeOrUnboxedGetValueElemStub<PropertyName*>(cx, rootedScript, pc, stub,
+ obj, rhs, attached))
+ {
+ return false;
+ }
+ } else if (rhs.isSymbol()) {
+ if (!TryAttachNativeOrUnboxedGetValueElemStub<JS::Symbol*>(cx, rootedScript, pc, stub,
+ obj, rhs, attached))
+ {
+ return false;
+ }
+ }
+ if (*attached)
+ return true;
+ script = rootedScript;
+ }
+
+ // Check for UnboxedArray[int] accesses.
+ if (obj->is<UnboxedArrayObject>() && rhs.isInt32() && rhs.toInt32() >= 0) {
+ JitSpew(JitSpew_BaselineIC, " Generating GetElem(UnboxedArray[Int32]) stub");
+ ICGetElem_UnboxedArray::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ obj->group());
+ ICStub* unboxedStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!unboxedStub)
+ return false;
+
+ stub->addNewStub(unboxedStub);
+ *attached = true;
+ return true;
+ }
+
+ // Check for TypedArray[int] => Number and TypedObject[int] => Number accesses.
+ if ((obj->is<TypedArrayObject>() || IsPrimitiveArrayTypedObject(obj)) &&
+ rhs.isNumber() &&
+ res.isNumber() &&
+ !TypedArrayGetElemStubExists(stub, obj))
+ {
+ if (!cx->runtime()->jitSupportsFloatingPoint &&
+ (TypedThingRequiresFloatingPoint(obj) || rhs.isDouble()))
+ {
+ return true;
+ }
+
+ // Don't attach typed object stubs if the underlying storage could be
+ // detached, as the stub will always bail out.
+ if (IsPrimitiveArrayTypedObject(obj) && cx->compartment()->detachedTypedObjects)
+ return true;
+
+ JitSpew(JitSpew_BaselineIC, " Generating GetElem(TypedArray[Int32]) stub");
+ ICGetElem_TypedArray::Compiler compiler(cx, obj->maybeShape(), TypedThingElementType(obj));
+ ICStub* typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!typedArrayStub)
+ return false;
+
+ stub->addNewStub(typedArrayStub);
+ *attached = true;
+ return true;
+ }
+
+ // GetElem operations on non-native objects cannot be cached by either
+ // Baseline or Ion. Indicate this in the cache so that Ion does not
+ // generate a cache for this op.
+ if (!obj->isNative())
+ stub->noteNonNativeAccess();
+
+ // GetElem operations which could access negative indexes generally can't
+ // be optimized without the potential for bailouts, as we can't statically
+ // determine that an object has no properties on such indexes.
+ if (rhs.isNumber() && rhs.toNumber() < 0)
+ stub->noteNegativeIndex();
+
+ return true;
+}
+
+static bool
+DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_, HandleValue lhs,
+ HandleValue rhs, MutableHandleValue res)
+{
+ SharedStubInfo info(cx, frame, stub_->icEntry());
+
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICGetElem_Fallback*> stub(frame, stub_);
+
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = stub->icEntry()->pc(frame->script());
+ JSOp op = JSOp(*pc);
+ FallbackICSpew(cx, stub, "GetElem(%s)", CodeName[op]);
+
+ MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
+
+ // Don't pass lhs directly, we need it when generating stubs.
+ RootedValue lhsCopy(cx, lhs);
+
+ bool isOptimizedArgs = false;
+ if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS)) {
+ // Handle optimized arguments[i] access.
+ if (!GetElemOptimizedArguments(cx, frame, &lhsCopy, rhs, res, &isOptimizedArgs))
+ return false;
+ if (isOptimizedArgs)
+ TypeScript::Monitor(cx, frame->script(), pc, res);
+ }
+
+ bool attached = false;
+ if (stub->numOptimizedStubs() >= ICGetElem_Fallback::MAX_OPTIMIZED_STUBS) {
+ // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
+ // But for now we just bail.
+ stub->noteUnoptimizableAccess();
+ attached = true;
+ }
+
+ // Try to attach an optimized getter stub.
+ bool isTemporarilyUnoptimizable = false;
+ if (!attached && lhs.isObject() && lhs.toObject().isNative()){
+ if (rhs.isString()) {
+ RootedScript rootedScript(cx, frame->script());
+ RootedNativeObject obj(cx, &lhs.toObject().as<NativeObject>());
+ if (!TryAttachNativeGetAccessorElemStub<PropertyName*>(cx, rootedScript, pc, stub,
+ obj, rhs, &attached,
+ &isTemporarilyUnoptimizable))
+ {
+ return false;
+ }
+ script = rootedScript;
+ } else if (rhs.isSymbol()) {
+ RootedScript rootedScript(cx, frame->script());
+ RootedNativeObject obj(cx, &lhs.toObject().as<NativeObject>());
+ if (!TryAttachNativeGetAccessorElemStub<JS::Symbol*>(cx, rootedScript, pc, stub,
+ obj, rhs, &attached,
+ &isTemporarilyUnoptimizable))
+ {
+ return false;
+ }
+ script = rootedScript;
+ }
+ }
+
+ if (!isOptimizedArgs) {
+ if (!GetElementOperation(cx, op, &lhsCopy, rhs, res))
+ return false;
+ TypeScript::Monitor(cx, frame->script(), pc, res);
+ }
+
+ // Check if debug mode toggling made the stub invalid.
+ if (stub.invalid())
+ return true;
+
+ // Add a type monitor stub for the resulting value.
+ if (!stub->addMonitorStubForValue(cx, &info, res))
+ {
+ return false;
+ }
+
+ if (attached)
+ return true;
+
+ // Try to attach an optimized stub.
+ if (!TryAttachGetElemStub(cx, frame->script(), pc, stub, lhs, rhs, res, &attached))
+ return false;
+
+ if (!attached && !isTemporarilyUnoptimizable)
+ stub->noteUnoptimizableAccess();
+
+ return true;
+}
+
+typedef bool (*DoGetElemFallbackFn)(JSContext*, BaselineFrame*, ICGetElem_Fallback*,
+ HandleValue, HandleValue, MutableHandleValue);
+static const VMFunction DoGetElemFallbackInfo =
+ FunctionInfo<DoGetElemFallbackFn>(DoGetElemFallback, "DoGetElemFallback", TailCall,
+ PopValues(2));
+
+bool
+ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+ MOZ_ASSERT(R0 == JSReturnOperand);
+
+ // Restore the tail call register.
+ EmitRestoreTailCallReg(masm);
+
+ // Ensure stack is fully synced for the expression decompiler.
+ masm.pushValue(R0);
+ masm.pushValue(R1);
+
+ // Push arguments.
+ masm.pushValue(R1);
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoGetElemFallbackInfo, masm);
+}
+
+//
+// GetElem_NativeSlot
+//
+
+static bool
+DoAtomizeString(JSContext* cx, HandleString string, MutableHandleValue result)
+{
+ JitSpew(JitSpew_BaselineIC, " AtomizeString called");
+
+ RootedValue key(cx, StringValue(string));
+
+ // Convert to interned property name.
+ RootedId id(cx);
+ if (!ValueToId<CanGC>(cx, key, &id))
+ return false;
+
+ if (!JSID_IS_ATOM(id)) {
+ result.set(key);
+ return true;
+ }
+
+ result.set(StringValue(JSID_TO_ATOM(id)));
+ return true;
+}
+
+typedef bool (*DoAtomizeStringFn)(JSContext*, HandleString, MutableHandleValue);
+static const VMFunction DoAtomizeStringInfo = FunctionInfo<DoAtomizeStringFn>(DoAtomizeString,
+ "DoAtomizeString");
+
+template <class T>
+bool
+ICGetElemNativeCompiler<T>::emitCallNative(MacroAssembler& masm, Register objReg)
+{
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+ regs.takeUnchecked(objReg);
+ regs.takeUnchecked(ICTailCallReg);
+
+ enterStubFrame(masm, regs.getAny());
+
+ // Push object.
+ masm.push(objReg);
+
+ // Push native callee.
+ masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()), objReg);
+ masm.push(objReg);
+
+ regs.add(objReg);
+
+ // Call helper.
+ if (!callVM(DoCallNativeGetterInfo, masm))
+ return false;
+
+ leaveStubFrame(masm);
+
+ return true;
+}
+
+template <class T>
+bool
+ICGetElemNativeCompiler<T>::emitCallScripted(MacroAssembler& masm, Register objReg)
+{
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+ regs.takeUnchecked(objReg);
+ regs.takeUnchecked(ICTailCallReg);
+
+ // Enter stub frame.
+ enterStubFrame(masm, regs.getAny());
+
+ // Align the stack such that the JitFrameLayout is aligned on
+ // JitStackAlignment.
+ masm.alignJitStackBasedOnNArgs(0);
+
+ // Push |this| for getter (target object).
+ {
+ ValueOperand val = regs.takeAnyValue();
+ masm.tagValue(JSVAL_TYPE_OBJECT, objReg, val);
+ masm.Push(val);
+ regs.add(val);
+ }
+
+ regs.add(objReg);
+
+ Register callee = regs.takeAny();
+ masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()), callee);
+
+ // Push argc, callee, and descriptor.
+ {
+ Register callScratch = regs.takeAny();
+ EmitBaselineCreateStubFrameDescriptor(masm, callScratch, JitFrameLayout::Size());
+ masm.Push(Imm32(0)); // ActualArgc is 0
+ masm.Push(callee);
+ masm.Push(callScratch);
+ regs.add(callScratch);
+ }
+
+ Register code = regs.takeAnyExcluding(ArgumentsRectifierReg);
+ masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
+ masm.loadBaselineOrIonRaw(code, code, nullptr);
+
+ Register scratch = regs.takeAny();
+
+ // Handle arguments underflow.
+ Label noUnderflow;
+ masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
+ masm.branch32(Assembler::Equal, scratch, Imm32(0), &noUnderflow);
+ {
+ // Call the arguments rectifier.
+ MOZ_ASSERT(ArgumentsRectifierReg != code);
+
+ JitCode* argumentsRectifier =
+ cx->runtime()->jitRuntime()->getArgumentsRectifier();
+
+ masm.movePtr(ImmGCPtr(argumentsRectifier), code);
+ masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
+ masm.movePtr(ImmWord(0), ArgumentsRectifierReg);
+ }
+
+ masm.bind(&noUnderflow);
+ masm.callJit(code);
+
+ leaveStubFrame(masm, true);
+
+ return true;
+}
+
+template <class T>
+bool
+ICGetElemNativeCompiler<T>::emitCheckKey(MacroAssembler& masm, Label& failure)
+{
+ MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
+ return false;
+}
+
+template <>
+bool
+ICGetElemNativeCompiler<JS::Symbol*>::emitCheckKey(MacroAssembler& masm, Label& failure)
+{
+ MOZ_ASSERT(!needsAtomize_);
+ masm.branchTestSymbol(Assembler::NotEqual, R1, &failure);
+ Address symbolAddr(ICStubReg, ICGetElemNativeStubImpl<JS::Symbol*>::offsetOfKey());
+ Register symExtract = masm.extractObject(R1, ExtractTemp1);
+ masm.branchPtr(Assembler::NotEqual, symbolAddr, symExtract, &failure);
+ return true;
+}
+
+template <>
+bool
+ICGetElemNativeCompiler<PropertyName*>::emitCheckKey(MacroAssembler& masm, Label& failure)
+{
+ masm.branchTestString(Assembler::NotEqual, R1, &failure);
+ // Check key identity. Don't automatically fail if this fails, since the incoming
+ // key maybe a non-interned string. Switch to a slowpath vm-call based check.
+ Address nameAddr(ICStubReg, ICGetElemNativeStubImpl<PropertyName*>::offsetOfKey());
+ Register strExtract = masm.extractString(R1, ExtractTemp1);
+
+ // If needsAtomize_ is true, and the string is not already an atom, then atomize the
+ // string before proceeding.
+ if (needsAtomize_) {
+ Label skipAtomize;
+
+ // If string is already an atom, skip the atomize.
+ masm.branchTest32(Assembler::NonZero,
+ Address(strExtract, JSString::offsetOfFlags()),
+ Imm32(JSString::ATOM_BIT),
+ &skipAtomize);
+
+ // Stow R0.
+ EmitStowICValues(masm, 1);
+
+ enterStubFrame(masm, R0.scratchReg());
+
+ // Atomize the string into a new value.
+ masm.push(strExtract);
+ if (!callVM(DoAtomizeStringInfo, masm))
+ return false;
+
+ // Atomized string is now in JSReturnOperand (R0).
+ // Leave stub frame, move atomized string into R1.
+ MOZ_ASSERT(R0 == JSReturnOperand);
+ leaveStubFrame(masm);
+ masm.moveValue(JSReturnOperand, R1);
+
+ // Unstow R0
+ EmitUnstowICValues(masm, 1);
+
+ // Extract string from R1 again.
+ DebugOnly<Register> strExtract2 = masm.extractString(R1, ExtractTemp1);
+ MOZ_ASSERT(Register(strExtract2) == strExtract);
+
+ masm.bind(&skipAtomize);
+ }
+
+ // Key has been atomized if necessary. Do identity check on string pointer.
+ masm.branchPtr(Assembler::NotEqual, nameAddr, strExtract, &failure);
+ return true;
+}
+
+template <class T>
+bool
+ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ Label failurePopR1;
+ bool popR1 = false;
+
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Unbox object.
+ Register objReg = masm.extractObject(R0, ExtractTemp0);
+
+ // Check object shape/group.
+ GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratchReg,
+ ICGetElemNativeStub::offsetOfReceiverGuard(), &failure);
+
+ // Since this stub sometimes enters a stub frame, we manually set this to true (lie).
+#ifdef DEBUG
+ entersStubFrame_ = true;
+#endif
+
+ if (!emitCheckKey(masm, failure))
+ return false;
+
+ Register holderReg;
+ if (obj_ == holder_) {
+ holderReg = objReg;
+
+ if (obj_->is<UnboxedPlainObject>() && acctype_ != ICGetElemNativeStub::UnboxedProperty) {
+ // The property will be loaded off the unboxed expando.
+ masm.push(R1.scratchReg());
+ popR1 = true;
+ holderReg = R1.scratchReg();
+ masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
+ }
+ } else {
+ // Shape guard holder.
+ if (regs.empty()) {
+ masm.push(R1.scratchReg());
+ popR1 = true;
+ holderReg = R1.scratchReg();
+ } else {
+ holderReg = regs.takeAny();
+ }
+
+ if (kind == ICStub::GetElem_NativePrototypeCallNativeName ||
+ kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
+ kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
+ kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol)
+ {
+ masm.loadPtr(Address(ICStubReg,
+ ICGetElemNativePrototypeCallStub<T>::offsetOfHolder()),
+ holderReg);
+ masm.loadPtr(Address(ICStubReg,
+ ICGetElemNativePrototypeCallStub<T>::offsetOfHolderShape()),
+ scratchReg);
+ } else {
+ masm.loadPtr(Address(ICStubReg,
+ ICGetElem_NativePrototypeSlot<T>::offsetOfHolder()),
+ holderReg);
+ masm.loadPtr(Address(ICStubReg,
+ ICGetElem_NativePrototypeSlot<T>::offsetOfHolderShape()),
+ scratchReg);
+ }
+ masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratchReg,
+ popR1 ? &failurePopR1 : &failure);
+ }
+
+ if (acctype_ == ICGetElemNativeStub::DynamicSlot ||
+ acctype_ == ICGetElemNativeStub::FixedSlot)
+ {
+ masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()),
+ scratchReg);
+
+ // Load from object.
+ if (acctype_ == ICGetElemNativeStub::DynamicSlot)
+ masm.addPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
+ else
+ masm.addPtr(holderReg, scratchReg);
+
+ Address valAddr(scratchReg, 0);
+ masm.loadValue(valAddr, R0);
+ if (popR1)
+ masm.addToStackPtr(ImmWord(sizeof(size_t)));
+
+ } else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) {
+ masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()),
+ scratchReg);
+ masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_,
+ TypedOrValueRegister(R0));
+ if (popR1)
+ masm.addToStackPtr(ImmWord(sizeof(size_t)));
+ } else {
+ MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter ||
+ acctype_ == ICGetElemNativeStub::ScriptedGetter);
+ MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNativeName ||
+ kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
+ kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
+ kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol);
+
+ if (acctype_ == ICGetElemNativeStub::NativeGetter) {
+ // If calling a native getter, there is no chance of failure now.
+
+ // GetElem key (R1) is no longer needed.
+ if (popR1)
+ masm.addToStackPtr(ImmWord(sizeof(size_t)));
+
+ if (!emitCallNative(masm, objReg))
+ return false;
+
+ } else {
+ MOZ_ASSERT(acctype_ == ICGetElemNativeStub::ScriptedGetter);
+
+ // Load function in scratchReg and ensure that it has a jit script.
+ masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()),
+ scratchReg);
+ masm.branchIfFunctionHasNoScript(scratchReg, popR1 ? &failurePopR1 : &failure);
+ masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
+ masm.loadBaselineOrIonRaw(scratchReg, scratchReg, popR1 ? &failurePopR1 : &failure);
+
+ // At this point, we are guaranteed to successfully complete.
+ if (popR1)
+ masm.addToStackPtr(Imm32(sizeof(size_t)));
+
+ if (!emitCallScripted(masm, objReg))
+ return false;
+ }
+ }
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ // Failure case - jump to next stub
+ if (popR1) {
+ masm.bind(&failurePopR1);
+ masm.pop(R1.scratchReg());
+ }
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+
+ return true;
+}
+
+//
+// GetElem_String
+//
+
+bool
+ICGetElem_String::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ masm.branchTestString(Assembler::NotEqual, R0, &failure);
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Unbox string in R0.
+ Register str = masm.extractString(R0, ExtractTemp0);
+
+ // Check for non-linear strings.
+ masm.branchIfRope(str, &failure);
+
+ // Unbox key.
+ Register key = masm.extractInt32(R1, ExtractTemp1);
+
+ // Bounds check.
+ masm.branch32(Assembler::BelowOrEqual, Address(str, JSString::offsetOfLength()),
+ key, &failure);
+
+ // Get char code.
+ masm.loadStringChar(str, key, scratchReg);
+
+ // Check if char code >= UNIT_STATIC_LIMIT.
+ masm.branch32(Assembler::AboveOrEqual, scratchReg, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
+ &failure);
+
+ // Load static string.
+ masm.movePtr(ImmPtr(&cx->staticStrings().unitStaticTable), str);
+ masm.loadPtr(BaseIndex(str, scratchReg, ScalePointer), str);
+
+ // Return.
+ masm.tagValue(JSVAL_TYPE_STRING, str, R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// GetElem_Dense
+//
+
+bool
+ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Unbox R0 and shape guard.
+ Register obj = masm.extractObject(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICGetElem_Dense::offsetOfShape()), scratchReg);
+ masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
+
+ // Load obj->elements.
+ masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
+
+ // Unbox key.
+ Register key = masm.extractInt32(R1, ExtractTemp1);
+
+ // Bounds check.
+ Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure);
+
+ // Hole check and load value.
+ BaseObjectElementIndex element(scratchReg, key);
+ masm.branchTestMagic(Assembler::Equal, element, &failure);
+
+ // Load value from element location.
+ masm.loadValue(element, R0);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// GetElem_UnboxedArray
+//
+
+bool
+ICGetElem_UnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Unbox R0 and group guard.
+ Register obj = masm.extractObject(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICGetElem_UnboxedArray::offsetOfGroup()), scratchReg);
+ masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure);
+
+ // Unbox key.
+ Register key = masm.extractInt32(R1, ExtractTemp1);
+
+ // Bounds check.
+ masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()),
+ scratchReg);
+ masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
+ masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
+
+ // Load obj->elements.
+ masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
+
+ // Load value.
+ size_t width = UnboxedTypeSize(elementType_);
+ BaseIndex addr(scratchReg, key, ScaleFromElemWidth(width));
+ masm.loadUnboxedProperty(addr, elementType_, R0);
+
+ // Only monitor the result if its type might change.
+ if (elementType_ == JSVAL_TYPE_OBJECT)
+ EmitEnterTypeMonitorIC(masm);
+ else
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// GetElem_TypedArray
+//
+
+static void
+LoadTypedThingLength(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result)
+{
+ switch (layout) {
+ case Layout_TypedArray:
+ masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), result);
+ break;
+ case Layout_OutlineTypedObject:
+ case Layout_InlineTypedObject:
+ masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), result);
+ masm.loadPtr(Address(result, ObjectGroup::offsetOfAddendum()), result);
+ masm.unboxInt32(Address(result, ArrayTypeDescr::offsetOfLength()), result);
+ break;
+ default:
+ MOZ_CRASH();
+ }
+}
+
+bool
+ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+
+ if (layout_ != Layout_TypedArray)
+ CheckForTypedObjectWithDetachedStorage(cx, masm, &failure);
+
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Unbox R0 and shape guard.
+ Register obj = masm.extractObject(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICGetElem_TypedArray::offsetOfShape()), scratchReg);
+ masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
+
+ // Ensure the index is an integer.
+ if (cx->runtime()->jitSupportsFloatingPoint) {
+ Label isInt32;
+ masm.branchTestInt32(Assembler::Equal, R1, &isInt32);
+ {
+ // If the index is a double, try to convert it to int32. It's okay
+ // to convert -0 to 0: the shape check ensures the object is a typed
+ // array so the difference is not observable.
+ masm.branchTestDouble(Assembler::NotEqual, R1, &failure);
+ masm.unboxDouble(R1, FloatReg0);
+ masm.convertDoubleToInt32(FloatReg0, scratchReg, &failure, /* negZeroCheck = */false);
+ masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R1);
+ }
+ masm.bind(&isInt32);
+ } else {
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+ }
+
+ // Unbox key.
+ Register key = masm.extractInt32(R1, ExtractTemp1);
+
+ // Bounds check.
+ LoadTypedThingLength(masm, layout_, obj, scratchReg);
+ masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
+
+ // Load the elements vector.
+ LoadTypedThingData(masm, layout_, obj, scratchReg);
+
+ // Load the value.
+ BaseIndex source(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_)));
+ masm.loadFromTypedArray(type_, source, R0, false, scratchReg, &failure);
+
+ // Todo: Allow loading doubles from uint32 arrays, but this requires monitoring.
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// GetElem_Arguments
+//
+bool
+ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ if (which_ == ICGetElem_Arguments::Magic) {
+ // Ensure that this is a magic arguments value.
+ masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
+
+ // Ensure that frame has not loaded different arguments object since.
+ masm.branchTest32(Assembler::NonZero,
+ Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
+ Imm32(BaselineFrame::HAS_ARGS_OBJ),
+ &failure);
+
+ // Ensure that index is an integer.
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+ Register idx = masm.extractInt32(R1, ExtractTemp1);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratch = regs.takeAny();
+
+ // Load num actual arguments
+ Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
+ masm.loadPtr(actualArgs, scratch);
+
+ // Ensure idx < argc
+ masm.branch32(Assembler::AboveOrEqual, idx, scratch, &failure);
+
+ // Load argval
+ masm.movePtr(BaselineFrameReg, scratch);
+ masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), scratch);
+ BaseValueIndex element(scratch, idx);
+ masm.loadValue(element, R0);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+ }
+
+ MOZ_ASSERT(which_ == ICGetElem_Arguments::Mapped ||
+ which_ == ICGetElem_Arguments::Unmapped);
+
+ const Class* clasp = (which_ == ICGetElem_Arguments::Mapped)
+ ? &MappedArgumentsObject::class_
+ : &UnmappedArgumentsObject::class_;
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Guard on input being an arguments object.
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+ Register objReg = masm.extractObject(R0, ExtractTemp0);
+ masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure);
+
+ // Guard on index being int32
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+ Register idxReg = masm.extractInt32(R1, ExtractTemp1);
+
+ // Get initial ArgsObj length value.
+ masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg);
+
+ // Test if length or any element have been overridden.
+ masm.branchTest32(Assembler::NonZero,
+ scratchReg,
+ Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT |
+ ArgumentsObject::ELEMENT_OVERRIDDEN_BIT),
+ &failure);
+
+ // Length has not been overridden, ensure that R1 is an integer and is <= length.
+ masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg);
+ masm.branch32(Assembler::AboveOrEqual, idxReg, scratchReg, &failure);
+
+ // Length check succeeded, now check the correct bit. We clobber potential type regs
+ // now. Inputs will have to be reconstructed if we fail after this point, but that's
+ // unlikely.
+ Label failureReconstructInputs;
+ regs = availableGeneralRegs(0);
+ regs.takeUnchecked(objReg);
+ regs.takeUnchecked(idxReg);
+ regs.take(scratchReg);
+ Register argData = regs.takeAny();
+
+ // Load ArgumentsData
+ masm.loadPrivate(Address(objReg, ArgumentsObject::getDataSlotOffset()), argData);
+
+ // Fail if we have a RareArgumentsData (elements were deleted).
+ masm.branchPtr(Assembler::NotEqual,
+ Address(argData, offsetof(ArgumentsData, rareData)),
+ ImmWord(0),
+ &failureReconstructInputs);
+
+ // Load the value. Use scratchReg to form a ValueOperand to load into.
+ masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), argData);
+ regs.add(scratchReg);
+ ValueOperand tempVal = regs.takeAnyValue();
+ masm.loadValue(BaseValueIndex(argData, idxReg), tempVal);
+
+ // Makesure that this is not a FORWARD_TO_CALL_SLOT magic value.
+ masm.branchTestMagic(Assembler::Equal, tempVal, &failureReconstructInputs);
+
+ // Copy value from temp to R0.
+ masm.moveValue(tempVal, R0);
+
+ // Type-check result
+ EmitEnterTypeMonitorIC(masm);
+
+ // Failed, but inputs are deconstructed into object and int, and need to be
+ // reconstructed into values.
+ masm.bind(&failureReconstructInputs);
+ masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0);
+ masm.tagValue(JSVAL_TYPE_INT32, idxReg, R1);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// SetElem_Fallback
+//
+
+static bool
+SetElemAddHasSameShapes(ICSetElem_DenseOrUnboxedArrayAdd* stub, JSObject* obj)
+{
+ static const size_t MAX_DEPTH = ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH;
+ ICSetElem_DenseOrUnboxedArrayAddImpl<MAX_DEPTH>* nstub = stub->toImplUnchecked<MAX_DEPTH>();
+
+ if (obj->maybeShape() != nstub->shape(0))
+ return false;
+
+ JSObject* proto = obj->staticPrototype();
+ for (size_t i = 0; i < stub->protoChainDepth(); i++) {
+ if (!proto->isNative())
+ return false;
+ if (proto->as<NativeObject>().lastProperty() != nstub->shape(i + 1))
+ return false;
+ proto = obj->staticPrototype();
+ if (!proto) {
+ if (i != stub->protoChainDepth() - 1)
+ return false;
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool
+DenseOrUnboxedArraySetElemStubExists(JSContext* cx, ICStub::Kind kind,
+ ICSetElem_Fallback* stub, HandleObject obj)
+{
+ MOZ_ASSERT(kind == ICStub::SetElem_DenseOrUnboxedArray ||
+ kind == ICStub::SetElem_DenseOrUnboxedArrayAdd);
+
+ for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
+ if (kind == ICStub::SetElem_DenseOrUnboxedArray && iter->isSetElem_DenseOrUnboxedArray()) {
+ ICSetElem_DenseOrUnboxedArray* nstub = iter->toSetElem_DenseOrUnboxedArray();
+ if (obj->maybeShape() == nstub->shape() && obj->getGroup(cx) == nstub->group())
+ return true;
+ }
+
+ if (kind == ICStub::SetElem_DenseOrUnboxedArrayAdd && iter->isSetElem_DenseOrUnboxedArrayAdd()) {
+ ICSetElem_DenseOrUnboxedArrayAdd* nstub = iter->toSetElem_DenseOrUnboxedArrayAdd();
+ if (obj->getGroup(cx) == nstub->group() && SetElemAddHasSameShapes(nstub, obj))
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+TypedArraySetElemStubExists(ICSetElem_Fallback* stub, HandleObject obj, bool expectOOB)
+{
+ for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
+ if (!iter->isSetElem_TypedArray())
+ continue;
+ ICSetElem_TypedArray* taStub = iter->toSetElem_TypedArray();
+ if (obj->maybeShape() == taStub->shape() && taStub->expectOutOfBounds() == expectOOB)
+ return true;
+ }
+ return false;
+}
+
+static bool
+RemoveExistingTypedArraySetElemStub(JSContext* cx, ICSetElem_Fallback* stub, HandleObject obj)
+{
+ for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
+ if (!iter->isSetElem_TypedArray())
+ continue;
+
+ if (obj->maybeShape() != iter->toSetElem_TypedArray()->shape())
+ continue;
+
+ // TypedArraySetElem stubs are only removed using this procedure if
+ // being replaced with one that expects out of bounds index.
+ MOZ_ASSERT(!iter->toSetElem_TypedArray()->expectOutOfBounds());
+ iter.unlink(cx);
+ return true;
+ }
+ return false;
+}
+
+static bool
+CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
+ Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
+ bool* isAddingCaseOut, size_t* protoDepthOut)
+{
+ uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
+ uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj);
+
+ *isAddingCaseOut = false;
+ *protoDepthOut = 0;
+
+ // Some initial sanity checks.
+ if (initLength < oldInitLength || capacity < oldCapacity)
+ return false;
+
+ // Unboxed arrays need to be able to emit floating point code.
+ if (obj->is<UnboxedArrayObject>() && !obj->runtimeFromMainThread()->jitSupportsFloatingPoint)
+ return false;
+
+ Shape* shape = obj->maybeShape();
+
+ // Cannot optimize if the shape changed.
+ if (oldShape != shape)
+ return false;
+
+ // Cannot optimize if the capacity changed.
+ if (oldCapacity != capacity)
+ return false;
+
+ // Cannot optimize if the index doesn't fit within the new initialized length.
+ if (index >= initLength)
+ return false;
+
+ // Cannot optimize if the value at position after the set is a hole.
+ if (obj->isNative() && !obj->as<NativeObject>().containsDenseElement(index))
+ return false;
+
+ // At this point, if we know that the initLength did not change, then
+ // an optimized set is possible.
+ if (oldInitLength == initLength)
+ return true;
+
+ // If it did change, ensure that it changed specifically by incrementing by 1
+ // to accomodate this particular indexed set.
+ if (oldInitLength + 1 != initLength)
+ return false;
+ if (index != oldInitLength)
+ return false;
+
+ // The checks are not complete. The object may have a setter definition,
+ // either directly, or via a prototype, or via the target object for a prototype
+ // which is a proxy, that handles a particular integer write.
+ // Scan the prototype and shape chain to make sure that this is not the case.
+ if (obj->isIndexed())
+ return false;
+ JSObject* curObj = obj->staticPrototype();
+ while (curObj) {
+ ++*protoDepthOut;
+ if (!curObj->isNative() || curObj->isIndexed())
+ return false;
+ curObj = curObj->staticPrototype();
+ }
+
+ if (*protoDepthOut > ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH)
+ return false;
+
+ *isAddingCaseOut = true;
+ return true;
+}
+
+static bool
+DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_, Value* stack,
+ HandleValue objv, HandleValue index, HandleValue rhs)
+{
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICSetElem_Fallback*> stub(frame, stub_);
+
+ RootedScript script(cx, frame->script());
+ RootedScript outerScript(cx, script);
+ jsbytecode* pc = stub->icEntry()->pc(script);
+ JSOp op = JSOp(*pc);
+ FallbackICSpew(cx, stub, "SetElem(%s)", CodeName[JSOp(*pc)]);
+
+ MOZ_ASSERT(op == JSOP_SETELEM ||
+ op == JSOP_STRICTSETELEM ||
+ op == JSOP_INITELEM ||
+ op == JSOP_INITHIDDENELEM ||
+ op == JSOP_INITELEM_ARRAY ||
+ op == JSOP_INITELEM_INC);
+
+ RootedObject obj(cx, ToObjectFromStack(cx, objv));
+ if (!obj)
+ return false;
+
+ RootedShape oldShape(cx, obj->maybeShape());
+
+ // Check the old capacity
+ uint32_t oldCapacity = 0;
+ uint32_t oldInitLength = 0;
+ if (index.isInt32() && index.toInt32() >= 0) {
+ oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
+ oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
+ }
+
+ if (op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM) {
+ if (!InitElemOperation(cx, pc, obj, index, rhs))
+ return false;
+ } else if (op == JSOP_INITELEM_ARRAY) {
+ MOZ_ASSERT(uint32_t(index.toInt32()) <= INT32_MAX,
+ "the bytecode emitter must fail to compile code that would "
+ "produce JSOP_INITELEM_ARRAY with an index exceeding "
+ "int32_t range");
+ MOZ_ASSERT(uint32_t(index.toInt32()) == GET_UINT32(pc));
+ if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
+ return false;
+ } else if (op == JSOP_INITELEM_INC) {
+ if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
+ return false;
+ } else {
+ if (!SetObjectElement(cx, obj, index, rhs, objv, JSOp(*pc) == JSOP_STRICTSETELEM, script, pc))
+ return false;
+ }
+
+ // Don't try to attach stubs that wish to be hidden. We don't know how to
+ // have different enumerability in the stubs for the moment.
+ if (op == JSOP_INITHIDDENELEM)
+ return true;
+
+ // Overwrite the object on the stack (pushed for the decompiler) with the rhs.
+ MOZ_ASSERT(stack[2] == objv);
+ stack[2] = rhs;
+
+ // Check if debug mode toggling made the stub invalid.
+ if (stub.invalid())
+ return true;
+
+ if (stub->numOptimizedStubs() >= ICSetElem_Fallback::MAX_OPTIMIZED_STUBS) {
+ // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
+ // But for now we just bail.
+ return true;
+ }
+
+ // Try to generate new stubs.
+ if (IsNativeOrUnboxedDenseElementAccess(obj, index) && !rhs.isMagic(JS_ELEMENTS_HOLE)) {
+ bool addingCase;
+ size_t protoDepth;
+
+ if (CanOptimizeDenseOrUnboxedArraySetElem(obj, index.toInt32(),
+ oldShape, oldCapacity, oldInitLength,
+ &addingCase, &protoDepth))
+ {
+ RootedShape shape(cx, obj->maybeShape());
+ RootedObjectGroup group(cx, obj->getGroup(cx));
+ if (!group)
+ return false;
+
+ if (addingCase &&
+ !DenseOrUnboxedArraySetElemStubExists(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd,
+ stub, obj))
+ {
+ JitSpew(JitSpew_BaselineIC,
+ " Generating SetElem_DenseOrUnboxedArrayAdd stub "
+ "(shape=%p, group=%p, protoDepth=%" PRIuSIZE ")",
+ shape.get(), group.get(), protoDepth);
+ ICSetElemDenseOrUnboxedArrayAddCompiler compiler(cx, obj, protoDepth);
+ ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(outerScript));
+ if (!newStub)
+ return false;
+ if (compiler.needsUpdateStubs() &&
+ !newStub->addUpdateStubForValue(cx, outerScript, obj, JSID_VOIDHANDLE, rhs))
+ {
+ return false;
+ }
+
+ stub->addNewStub(newStub);
+ } else if (!addingCase &&
+ !DenseOrUnboxedArraySetElemStubExists(cx,
+ ICStub::SetElem_DenseOrUnboxedArray,
+ stub, obj))
+ {
+ JitSpew(JitSpew_BaselineIC,
+ " Generating SetElem_DenseOrUnboxedArray stub (shape=%p, group=%p)",
+ shape.get(), group.get());
+ ICSetElem_DenseOrUnboxedArray::Compiler compiler(cx, shape, group);
+ ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(outerScript));
+ if (!newStub)
+ return false;
+ if (compiler.needsUpdateStubs() &&
+ !newStub->addUpdateStubForValue(cx, outerScript, obj, JSID_VOIDHANDLE, rhs))
+ {
+ return false;
+ }
+
+ stub->addNewStub(newStub);
+ }
+ }
+
+ return true;
+ }
+
+ if ((obj->is<TypedArrayObject>() || IsPrimitiveArrayTypedObject(obj)) &&
+ index.isNumber() &&
+ rhs.isNumber())
+ {
+ if (!cx->runtime()->jitSupportsFloatingPoint &&
+ (TypedThingRequiresFloatingPoint(obj) || index.isDouble()))
+ {
+ return true;
+ }
+
+ bool expectOutOfBounds;
+ double idx = index.toNumber();
+ if (obj->is<TypedArrayObject>()) {
+ expectOutOfBounds = (idx < 0 || idx >= double(obj->as<TypedArrayObject>().length()));
+ } else {
+ // Typed objects throw on out of bounds accesses. Don't attach
+ // a stub in this case.
+ if (idx < 0 || idx >= double(obj->as<TypedObject>().length()))
+ return true;
+ expectOutOfBounds = false;
+
+ // Don't attach stubs if the underlying storage for typed objects
+ // in the compartment could be detached, as the stub will always
+ // bail out.
+ if (cx->compartment()->detachedTypedObjects)
+ return true;
+ }
+
+ if (!TypedArraySetElemStubExists(stub, obj, expectOutOfBounds)) {
+ // Remove any existing TypedArraySetElemStub that doesn't handle out-of-bounds
+ if (expectOutOfBounds)
+ RemoveExistingTypedArraySetElemStub(cx, stub, obj);
+
+ Shape* shape = obj->maybeShape();
+ Scalar::Type type = TypedThingElementType(obj);
+
+ JitSpew(JitSpew_BaselineIC,
+ " Generating SetElem_TypedArray stub (shape=%p, type=%u, oob=%s)",
+ shape, type, expectOutOfBounds ? "yes" : "no");
+ ICSetElem_TypedArray::Compiler compiler(cx, shape, type, expectOutOfBounds);
+ ICStub* typedArrayStub = compiler.getStub(compiler.getStubSpace(outerScript));
+ if (!typedArrayStub)
+ return false;
+
+ stub->addNewStub(typedArrayStub);
+ return true;
+ }
+ }
+
+ return true;
+}
+
+typedef bool (*DoSetElemFallbackFn)(JSContext*, BaselineFrame*, ICSetElem_Fallback*, Value*,
+ HandleValue, HandleValue, HandleValue);
+static const VMFunction DoSetElemFallbackInfo =
+ FunctionInfo<DoSetElemFallbackFn>(DoSetElemFallback, "DoSetElemFallback", TailCall,
+ PopValues(2));
+
+bool
+ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+ MOZ_ASSERT(R0 == JSReturnOperand);
+
+ EmitRestoreTailCallReg(masm);
+
+ // State: R0: object, R1: index, stack: rhs.
+ // For the decompiler, the stack has to be: object, index, rhs,
+ // so we push the index, then overwrite the rhs Value with R0
+ // and push the rhs value.
+ masm.pushValue(R1);
+ masm.loadValue(Address(masm.getStackPointer(), sizeof(Value)), R1);
+ masm.storeValue(R0, Address(masm.getStackPointer(), sizeof(Value)));
+ masm.pushValue(R1);
+
+ // Push arguments.
+ masm.pushValue(R1); // RHS
+
+ // Push index. On x86 and ARM two push instructions are emitted so use a
+ // separate register to store the old stack pointer.
+ masm.moveStackPtrTo(R1.scratchReg());
+ masm.pushValue(Address(R1.scratchReg(), 2 * sizeof(Value)));
+ masm.pushValue(R0); // Object.
+
+ // Push pointer to stack values, so that the stub can overwrite the object
+ // (pushed for the decompiler) with the rhs.
+ masm.computeEffectiveAddress(Address(masm.getStackPointer(), 3 * sizeof(Value)), R0.scratchReg());
+ masm.push(R0.scratchReg());
+
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoSetElemFallbackInfo, masm);
+}
+
+void
+BaselineScript::noteArrayWriteHole(uint32_t pcOffset)
+{
+ ICEntry& entry = icEntryFromPCOffset(pcOffset);
+ ICFallbackStub* stub = entry.fallbackStub();
+
+ if (stub->isSetElem_Fallback())
+ stub->toSetElem_Fallback()->noteArrayWriteHole();
+}
+
+//
+// SetElem_DenseOrUnboxedArray
+//
+
+template <typename T>
+void
+EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType type)
+{
+ if (type == JSVAL_TYPE_OBJECT)
+ EmitPreBarrier(masm, address, MIRType::Object);
+ else if (type == JSVAL_TYPE_STRING)
+ EmitPreBarrier(masm, address, MIRType::String);
+ else
+ MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type));
+}
+
+bool
+ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ // R0 = object
+ // R1 = key
+ // Stack = { ... rhs-value, <return-addr>? }
+ Label failure, failurePopR0;
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Unbox R0 and guard on its group and, if this is a native access, its shape.
+ Register obj = masm.extractObject(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICSetElem_DenseOrUnboxedArray::offsetOfGroup()),
+ scratchReg);
+ masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure);
+ if (unboxedType_ == JSVAL_TYPE_MAGIC) {
+ masm.loadPtr(Address(ICStubReg, ICSetElem_DenseOrUnboxedArray::offsetOfShape()),
+ scratchReg);
+ masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
+ }
+
+ if (needsUpdateStubs()) {
+ // Stow both R0 and R1 (object and key)
+ // But R0 and R1 still hold their values.
+ EmitStowICValues(masm, 2);
+
+ // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR }
+ // Load rhs-value into R0
+ masm.loadValue(Address(masm.getStackPointer(), 2 * sizeof(Value) + ICStackValueOffset), R0);
+
+ // Call the type-update stub.
+ if (!callTypeUpdateIC(masm, sizeof(Value)))
+ return false;
+
+ // Unstow R0 and R1 (object and key)
+ EmitUnstowICValues(masm, 2);
+
+ // Restore object.
+ obj = masm.extractObject(R0, ExtractTemp0);
+
+ // Trigger post barriers here on the value being written. Fields which
+ // objects can be written to also need update stubs.
+ masm.Push(R1);
+ masm.loadValue(Address(masm.getStackPointer(), sizeof(Value) + ICStackValueOffset), R1);
+
+ LiveGeneralRegisterSet saveRegs;
+ saveRegs.add(R0);
+ saveRegs.addUnchecked(obj);
+ saveRegs.add(ICStubReg);
+ emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs);
+
+ masm.Pop(R1);
+ }
+
+ // Unbox key.
+ Register key = masm.extractInt32(R1, ExtractTemp1);
+
+ if (unboxedType_ == JSVAL_TYPE_MAGIC) {
+ // Set element on a native object.
+
+ // Load obj->elements in scratchReg.
+ masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
+
+ // Bounds check.
+ Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure);
+
+ // Hole check.
+ BaseIndex element(scratchReg, key, TimesEight);
+ masm.branchTestMagic(Assembler::Equal, element, &failure);
+
+ // Perform a single test to see if we either need to convert double
+ // elements, clone the copy on write elements in the object or fail
+ // due to a frozen element.
+ Label noSpecialHandling;
+ Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags());
+ masm.branchTest32(Assembler::Zero, elementsFlags,
+ Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS |
+ ObjectElements::COPY_ON_WRITE |
+ ObjectElements::FROZEN),
+ &noSpecialHandling);
+
+ // Fail if we need to clone copy on write elements or to throw due
+ // to a frozen element.
+ masm.branchTest32(Assembler::NonZero, elementsFlags,
+ Imm32(ObjectElements::COPY_ON_WRITE |
+ ObjectElements::FROZEN),
+ &failure);
+
+ // Failure is not possible now. Free up registers.
+ regs.add(R0);
+ regs.add(R1);
+ regs.takeUnchecked(obj);
+ regs.takeUnchecked(key);
+
+ Address valueAddr(masm.getStackPointer(), ICStackValueOffset);
+
+ // We need to convert int32 values being stored into doubles. In this case
+ // the heap typeset is guaranteed to contain both int32 and double, so it's
+ // okay to store a double. Note that double arrays are only created by
+ // IonMonkey, so if we have no floating-point support Ion is disabled and
+ // there should be no double arrays.
+ if (cx->runtime()->jitSupportsFloatingPoint)
+ masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &noSpecialHandling);
+ else
+ masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support.");
+
+ masm.bind(&noSpecialHandling);
+
+ ValueOperand tmpVal = regs.takeAnyValue();
+ masm.loadValue(valueAddr, tmpVal);
+ EmitPreBarrier(masm, element, MIRType::Value);
+ masm.storeValue(tmpVal, element);
+ } else {
+ // Set element on an unboxed array.
+
+ // Bounds check.
+ Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
+ masm.load32(initLength, scratchReg);
+ masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
+ masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
+
+ // Load obj->elements.
+ masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
+
+ // Compute the address being written to.
+ BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
+
+ EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_);
+
+ Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value));
+ masm.Push(R0);
+ masm.loadValue(valueAddr, R0);
+ masm.storeUnboxedProperty(address, unboxedType_,
+ ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
+ masm.Pop(R0);
+ }
+
+ EmitReturnFromIC(masm);
+
+ if (failurePopR0.used()) {
+ // Failure case: restore the value of R0
+ masm.bind(&failurePopR0);
+ masm.popValue(R0);
+ }
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// SetElem_DenseOrUnboxedArrayAdd
+//
+
+ICUpdatedStub*
+ICSetElemDenseOrUnboxedArrayAddCompiler::getStub(ICStubSpace* space)
+{
+ Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
+ if (!shapes.append(obj_->maybeShape()))
+ return nullptr;
+
+ if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
+ return nullptr;
+
+ JS_STATIC_ASSERT(ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH == 4);
+
+ ICUpdatedStub* stub = nullptr;
+ switch (protoChainDepth_) {
+ case 0: stub = getStubSpecific<0>(space, shapes); break;
+ case 1: stub = getStubSpecific<1>(space, shapes); break;
+ case 2: stub = getStubSpecific<2>(space, shapes); break;
+ case 3: stub = getStubSpecific<3>(space, shapes); break;
+ case 4: stub = getStubSpecific<4>(space, shapes); break;
+ default: MOZ_CRASH("ProtoChainDepth too high.");
+ }
+ if (!stub || !stub->initUpdatingChain(cx, space))
+ return nullptr;
+ return stub;
+}
+
+bool
+ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ // R0 = object
+ // R1 = key
+ // Stack = { ... rhs-value, <return-addr>? }
+ Label failure, failurePopR0, failureUnstow;
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Unbox R0 and guard on its group and, if this is a native access, its shape.
+ Register obj = masm.extractObject(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICSetElem_DenseOrUnboxedArrayAdd::offsetOfGroup()),
+ scratchReg);
+ masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure);
+ if (unboxedType_ == JSVAL_TYPE_MAGIC) {
+ masm.loadPtr(Address(ICStubReg, ICSetElem_DenseOrUnboxedArrayAddImpl<0>::offsetOfShape(0)),
+ scratchReg);
+ masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
+ }
+
+ // Stow both R0 and R1 (object and key)
+ // But R0 and R1 still hold their values.
+ EmitStowICValues(masm, 2);
+
+ uint32_t framePushedAfterStow = masm.framePushed();
+
+ // We may need to free up some registers.
+ regs = availableGeneralRegs(0);
+ regs.take(R0);
+ regs.take(scratchReg);
+
+ // Shape guard objects on the proto chain.
+ Register protoReg = regs.takeAny();
+ for (size_t i = 0; i < protoChainDepth_; i++) {
+ masm.loadObjProto(i == 0 ? obj : protoReg, protoReg);
+ masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failureUnstow);
+ masm.loadPtr(Address(ICStubReg, ICSetElem_DenseOrUnboxedArrayAddImpl<0>::offsetOfShape(i + 1)),
+ scratchReg);
+ masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratchReg, &failureUnstow);
+ }
+ regs.add(protoReg);
+ regs.add(scratchReg);
+
+ if (needsUpdateStubs()) {
+ // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR }
+ // Load rhs-value in to R0
+ masm.loadValue(Address(masm.getStackPointer(), 2 * sizeof(Value) + ICStackValueOffset), R0);
+
+ // Call the type-update stub.
+ if (!callTypeUpdateIC(masm, sizeof(Value)))
+ return false;
+ }
+
+ // Unstow R0 and R1 (object and key)
+ EmitUnstowICValues(masm, 2);
+
+ // Restore object.
+ obj = masm.extractObject(R0, ExtractTemp0);
+
+ if (needsUpdateStubs()) {
+ // Trigger post barriers here on the value being written. Fields which
+ // objects can be written to also need update stubs.
+ masm.Push(R1);
+ masm.loadValue(Address(masm.getStackPointer(), sizeof(Value) + ICStackValueOffset), R1);
+
+ LiveGeneralRegisterSet saveRegs;
+ saveRegs.add(R0);
+ saveRegs.addUnchecked(obj);
+ saveRegs.add(ICStubReg);
+ emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs);
+
+ masm.Pop(R1);
+ }
+
+ // Reset register set.
+ regs = availableGeneralRegs(2);
+ scratchReg = regs.takeAny();
+
+ // Unbox key.
+ Register key = masm.extractInt32(R1, ExtractTemp1);
+
+ if (unboxedType_ == JSVAL_TYPE_MAGIC) {
+ // Adding element to a native object.
+
+ // Load obj->elements in scratchReg.
+ masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
+
+ // Bounds check (key == initLength)
+ Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::NotEqual, initLength, key, &failure);
+
+ // Capacity check.
+ Address capacity(scratchReg, ObjectElements::offsetOfCapacity());
+ masm.branch32(Assembler::BelowOrEqual, capacity, key, &failure);
+
+ // Check for copy on write elements.
+ Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags());
+ masm.branchTest32(Assembler::NonZero, elementsFlags,
+ Imm32(ObjectElements::COPY_ON_WRITE |
+ ObjectElements::FROZEN),
+ &failure);
+
+ // Failure is not possible now. Free up registers.
+ regs.add(R0);
+ regs.add(R1);
+ regs.takeUnchecked(obj);
+ regs.takeUnchecked(key);
+
+ // Increment initLength before write.
+ masm.add32(Imm32(1), initLength);
+
+ // If length is now <= key, increment length before write.
+ Label skipIncrementLength;
+ Address length(scratchReg, ObjectElements::offsetOfLength());
+ masm.branch32(Assembler::Above, length, key, &skipIncrementLength);
+ masm.add32(Imm32(1), length);
+ masm.bind(&skipIncrementLength);
+
+ // Convert int32 values to double if convertDoubleElements is set. In this
+ // case the heap typeset is guaranteed to contain both int32 and double, so
+ // it's okay to store a double.
+ Label dontConvertDoubles;
+ masm.branchTest32(Assembler::Zero, elementsFlags,
+ Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
+ &dontConvertDoubles);
+
+ Address valueAddr(masm.getStackPointer(), ICStackValueOffset);
+
+ // Note that double arrays are only created by IonMonkey, so if we have no
+ // floating-point support Ion is disabled and there should be no double arrays.
+ if (cx->runtime()->jitSupportsFloatingPoint)
+ masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &dontConvertDoubles);
+ else
+ masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support.");
+ masm.bind(&dontConvertDoubles);
+
+ // Write the value. No need for pre-barrier since we're not overwriting an old value.
+ ValueOperand tmpVal = regs.takeAnyValue();
+ BaseIndex element(scratchReg, key, TimesEight);
+ masm.loadValue(valueAddr, tmpVal);
+ masm.storeValue(tmpVal, element);
+ } else {
+ // Adding element to an unboxed array.
+
+ // Bounds check (key == initLength)
+ Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
+ masm.load32(initLengthAddr, scratchReg);
+ masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
+ masm.branch32(Assembler::NotEqual, scratchReg, key, &failure);
+
+ // Capacity check.
+ masm.checkUnboxedArrayCapacity(obj, RegisterOrInt32Constant(key), scratchReg, &failure);
+
+ // Load obj->elements.
+ masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
+
+ // Write the value first, since this can fail. No need for pre-barrier
+ // since we're not overwriting an old value.
+ masm.Push(R0);
+ Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value));
+ masm.loadValue(valueAddr, R0);
+ BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
+ masm.storeUnboxedProperty(address, unboxedType_,
+ ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
+ masm.Pop(R0);
+
+ // Increment initialized length.
+ masm.add32(Imm32(1), initLengthAddr);
+
+ // If length is now <= key, increment length.
+ Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
+ Label skipIncrementLength;
+ masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength);
+ masm.add32(Imm32(1), lengthAddr);
+ masm.bind(&skipIncrementLength);
+ }
+
+ EmitReturnFromIC(masm);
+
+ if (failurePopR0.used()) {
+ // Failure case: restore the value of R0
+ masm.bind(&failurePopR0);
+ masm.popValue(R0);
+ masm.jump(&failure);
+ }
+
+ // Failure case - fail but first unstow R0 and R1
+ masm.bind(&failureUnstow);
+ masm.setFramePushed(framePushedAfterStow);
+ EmitUnstowICValues(masm, 2);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// SetElem_TypedArray
+//
+
+// Write an arbitrary value to a typed array or typed object address at dest.
+// If the value could not be converted to the appropriate format, jump to
+// failure or failureModifiedScratch.
+template <typename T>
+static void
+StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type, Address value, T dest,
+ Register scratch, Label* failure, Label* failureModifiedScratch)
+{
+ Label done;
+
+ if (type == Scalar::Float32 || type == Scalar::Float64) {
+ masm.ensureDouble(value, FloatReg0, failure);
+ if (type == Scalar::Float32) {
+ masm.convertDoubleToFloat32(FloatReg0, ScratchFloat32Reg);
+ masm.storeToTypedFloatArray(type, ScratchFloat32Reg, dest);
+ } else {
+ masm.storeToTypedFloatArray(type, FloatReg0, dest);
+ }
+ } else if (type == Scalar::Uint8Clamped) {
+ Label notInt32;
+ masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
+ masm.unboxInt32(value, scratch);
+ masm.clampIntToUint8(scratch);
+
+ Label clamped;
+ masm.bind(&clamped);
+ masm.storeToTypedIntArray(type, scratch, dest);
+ masm.jump(&done);
+
+ // If the value is a double, clamp to uint8 and jump back.
+ // Else, jump to failure.
+ masm.bind(&notInt32);
+ if (cx->runtime()->jitSupportsFloatingPoint) {
+ masm.branchTestDouble(Assembler::NotEqual, value, failure);
+ masm.unboxDouble(value, FloatReg0);
+ masm.clampDoubleToUint8(FloatReg0, scratch);
+ masm.jump(&clamped);
+ } else {
+ masm.jump(failure);
+ }
+ } else {
+ Label notInt32;
+ masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
+ masm.unboxInt32(value, scratch);
+
+ Label isInt32;
+ masm.bind(&isInt32);
+ masm.storeToTypedIntArray(type, scratch, dest);
+ masm.jump(&done);
+
+ // If the value is a double, truncate and jump back.
+ // Else, jump to failure.
+ masm.bind(&notInt32);
+ if (cx->runtime()->jitSupportsFloatingPoint) {
+ masm.branchTestDouble(Assembler::NotEqual, value, failure);
+ masm.unboxDouble(value, FloatReg0);
+ masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratch, failureModifiedScratch);
+ masm.jump(&isInt32);
+ } else {
+ masm.jump(failure);
+ }
+ }
+
+ masm.bind(&done);
+}
+
+bool
+ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+
+ if (layout_ != Layout_TypedArray)
+ CheckForTypedObjectWithDetachedStorage(cx, masm, &failure);
+
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratchReg = regs.takeAny();
+
+ // Unbox R0 and shape guard.
+ Register obj = masm.extractObject(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICSetElem_TypedArray::offsetOfShape()), scratchReg);
+ masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
+
+ // Ensure the index is an integer.
+ if (cx->runtime()->jitSupportsFloatingPoint) {
+ Label isInt32;
+ masm.branchTestInt32(Assembler::Equal, R1, &isInt32);
+ {
+ // If the index is a double, try to convert it to int32. It's okay
+ // to convert -0 to 0: the shape check ensures the object is a typed
+ // array so the difference is not observable.
+ masm.branchTestDouble(Assembler::NotEqual, R1, &failure);
+ masm.unboxDouble(R1, FloatReg0);
+ masm.convertDoubleToInt32(FloatReg0, scratchReg, &failure, /* negZeroCheck = */false);
+ masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R1);
+ }
+ masm.bind(&isInt32);
+ } else {
+ masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+ }
+
+ // Unbox key.
+ Register key = masm.extractInt32(R1, ExtractTemp1);
+
+ // Bounds check.
+ Label oobWrite;
+ LoadTypedThingLength(masm, layout_, obj, scratchReg);
+ masm.branch32(Assembler::BelowOrEqual, scratchReg, key,
+ expectOutOfBounds_ ? &oobWrite : &failure);
+
+ // Load the elements vector.
+ LoadTypedThingData(masm, layout_, obj, scratchReg);
+
+ BaseIndex dest(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_)));
+ Address value(masm.getStackPointer(), ICStackValueOffset);
+
+ // We need a second scratch register. It's okay to clobber the type tag of
+ // R0 or R1, as long as it's restored before jumping to the next stub.
+ regs = availableGeneralRegs(0);
+ regs.takeUnchecked(obj);
+ regs.takeUnchecked(key);
+ regs.take(scratchReg);
+ Register secondScratch = regs.takeAny();
+
+ Label failureModifiedSecondScratch;
+ StoreToTypedArray(cx, masm, type_, value, dest,
+ secondScratch, &failure, &failureModifiedSecondScratch);
+ EmitReturnFromIC(masm);
+
+ if (failureModifiedSecondScratch.used()) {
+ // Writing to secondScratch may have clobbered R0 or R1, restore them
+ // first.
+ masm.bind(&failureModifiedSecondScratch);
+ masm.tagValue(JSVAL_TYPE_OBJECT, obj, R0);
+ masm.tagValue(JSVAL_TYPE_INT32, key, R1);
+ }
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+
+ if (expectOutOfBounds_) {
+ MOZ_ASSERT(layout_ == Layout_TypedArray);
+ masm.bind(&oobWrite);
+ EmitReturnFromIC(masm);
+ }
+ return true;
+}
+
+//
+// In_Fallback
+//
+
+static bool
+TryAttachDenseInStub(JSContext* cx, HandleScript outerScript, ICIn_Fallback* stub,
+ HandleValue key, HandleObject obj, bool* attached)
+{
+ MOZ_ASSERT(!*attached);
+
+ if (!IsNativeDenseElementAccess(obj, key))
+ return true;
+
+ JitSpew(JitSpew_BaselineIC, " Generating In(Native[Int32] dense) stub");
+ ICIn_Dense::Compiler compiler(cx, obj->as<NativeObject>().lastProperty());
+ ICStub* denseStub = compiler.getStub(compiler.getStubSpace(outerScript));
+ if (!denseStub)
+ return false;
+
+ *attached = true;
+ stub->addNewStub(denseStub);
+ return true;
+}
+
+static bool
+TryAttachNativeInStub(JSContext* cx, HandleScript outerScript, ICIn_Fallback* stub,
+ HandleValue key, HandleObject obj, bool* attached)
+{
+ MOZ_ASSERT(!*attached);
+
+ RootedId id(cx);
+ if (!IsOptimizableElementPropertyName(cx, key, &id))
+ return true;
+
+ RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
+ RootedShape shape(cx);
+ RootedObject holder(cx);
+ if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
+ return false;
+
+ if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
+ ICStub::Kind kind = (obj == holder) ? ICStub::In_Native
+ : ICStub::In_NativePrototype;
+ JitSpew(JitSpew_BaselineIC, " Generating In(Native %s) stub",
+ (obj == holder) ? "direct" : "prototype");
+ ICInNativeCompiler compiler(cx, kind, obj, holder, name);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(outerScript));
+ if (!newStub)
+ return false;
+
+ *attached = true;
+ stub->addNewStub(newStub);
+ return true;
+ }
+
+ return true;
+}
+
+static bool
+TryAttachNativeInDoesNotExistStub(JSContext* cx, HandleScript outerScript,
+ ICIn_Fallback* stub, HandleValue key,
+ HandleObject obj, bool* attached)
+{
+ MOZ_ASSERT(!*attached);
+
+ RootedId id(cx);
+ if (!IsOptimizableElementPropertyName(cx, key, &id))
+ return true;
+
+ // Check if does-not-exist can be confirmed on property.
+ RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
+ RootedObject lastProto(cx);
+ size_t protoChainDepth = SIZE_MAX;
+ if (!CheckHasNoSuchProperty(cx, obj.get(), name.get(), lastProto.address(), &protoChainDepth))
+ return true;
+ MOZ_ASSERT(protoChainDepth < SIZE_MAX);
+
+ if (protoChainDepth > ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH)
+ return true;
+
+ // Confirmed no-such-property. Add stub.
+ JitSpew(JitSpew_BaselineIC, " Generating In_NativeDoesNotExist stub");
+ ICInNativeDoesNotExistCompiler compiler(cx, obj, name, protoChainDepth);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(outerScript));
+ if (!newStub)
+ return false;
+
+ *attached = true;
+ stub->addNewStub(newStub);
+ return true;
+}
+
+static bool
+DoInFallback(JSContext* cx, BaselineFrame* frame, ICIn_Fallback* stub_,
+ HandleValue key, HandleValue objValue, MutableHandleValue res)
+{
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICIn_Fallback*> stub(frame, stub_);
+
+ FallbackICSpew(cx, stub, "In");
+
+ if (!objValue.isObject()) {
+ ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, objValue, nullptr);
+ return false;
+ }
+
+ RootedObject obj(cx, &objValue.toObject());
+
+ bool cond = false;
+ if (!OperatorIn(cx, key, obj, &cond))
+ return false;
+ res.setBoolean(cond);
+
+ if (stub.invalid())
+ return true;
+
+ if (stub->numOptimizedStubs() >= ICIn_Fallback::MAX_OPTIMIZED_STUBS)
+ return true;
+
+ if (obj->isNative()) {
+ RootedScript script(cx, frame->script());
+ bool attached = false;
+ if (cond) {
+ if (!TryAttachDenseInStub(cx, script, stub, key, obj, &attached))
+ return false;
+ if (attached)
+ return true;
+ if (!TryAttachNativeInStub(cx, script, stub, key, obj, &attached))
+ return false;
+ if (attached)
+ return true;
+ } else {
+ if (!TryAttachNativeInDoesNotExistStub(cx, script, stub, key, obj, &attached))
+ return false;
+ if (attached)
+ return true;
+ }
+ }
+
+ return true;
+}
+
+typedef bool (*DoInFallbackFn)(JSContext*, BaselineFrame*, ICIn_Fallback*, HandleValue,
+ HandleValue, MutableHandleValue);
+static const VMFunction DoInFallbackInfo =
+ FunctionInfo<DoInFallbackFn>(DoInFallback, "DoInFallback", TailCall, PopValues(2));
+
+bool
+ICIn_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ EmitRestoreTailCallReg(masm);
+
+ // Sync for the decompiler.
+ masm.pushValue(R0);
+ masm.pushValue(R1);
+
+ // Push arguments.
+ masm.pushValue(R1);
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoInFallbackInfo, masm);
+}
+
+bool
+ICInNativeCompiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure, failurePopR0Scratch;
+
+ masm.branchTestString(Assembler::NotEqual, R0, &failure);
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratch = regs.takeAny();
+
+ // Check key identity.
+ Register strExtract = masm.extractString(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICInNativeStub::offsetOfName()), scratch);
+ masm.branchPtr(Assembler::NotEqual, strExtract, scratch, &failure);
+
+ // Unbox and shape guard object.
+ Register objReg = masm.extractObject(R1, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICInNativeStub::offsetOfShape()), scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
+
+ if (kind == ICStub::In_NativePrototype) {
+ // Shape guard holder. Use R0 scrachReg since on x86 there're not enough registers.
+ Register holderReg = R0.scratchReg();
+ masm.push(R0.scratchReg());
+ masm.loadPtr(Address(ICStubReg, ICIn_NativePrototype::offsetOfHolder()),
+ holderReg);
+ masm.loadPtr(Address(ICStubReg, ICIn_NativePrototype::offsetOfHolderShape()),
+ scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failurePopR0Scratch);
+ masm.addToStackPtr(Imm32(sizeof(size_t)));
+ }
+
+ masm.moveValue(BooleanValue(true), R0);
+
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failurePopR0Scratch);
+ masm.pop(R0.scratchReg());
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+ICStub*
+ICInNativeDoesNotExistCompiler::getStub(ICStubSpace* space)
+{
+ Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
+ if (!shapes.append(obj_->as<NativeObject>().lastProperty()))
+ return nullptr;
+
+ if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
+ return nullptr;
+
+ JS_STATIC_ASSERT(ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
+
+ ICStub* stub = nullptr;
+ switch (protoChainDepth_) {
+ case 0: stub = getStubSpecific<0>(space, shapes); break;
+ case 1: stub = getStubSpecific<1>(space, shapes); break;
+ case 2: stub = getStubSpecific<2>(space, shapes); break;
+ case 3: stub = getStubSpecific<3>(space, shapes); break;
+ case 4: stub = getStubSpecific<4>(space, shapes); break;
+ case 5: stub = getStubSpecific<5>(space, shapes); break;
+ case 6: stub = getStubSpecific<6>(space, shapes); break;
+ case 7: stub = getStubSpecific<7>(space, shapes); break;
+ case 8: stub = getStubSpecific<8>(space, shapes); break;
+ default: MOZ_CRASH("ProtoChainDepth too high.");
+ }
+ if (!stub)
+ return nullptr;
+ return stub;
+}
+
+bool
+ICInNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure, failurePopR0Scratch;
+
+ masm.branchTestString(Assembler::NotEqual, R0, &failure);
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratch = regs.takeAny();
+
+#ifdef DEBUG
+ // Ensure that protoChainDepth_ matches the protoChainDepth stored on the stub.
+ {
+ Label ok;
+ masm.load16ZeroExtend(Address(ICStubReg, ICStub::offsetOfExtra()), scratch);
+ masm.branch32(Assembler::Equal, scratch, Imm32(protoChainDepth_), &ok);
+ masm.assumeUnreachable("Non-matching proto chain depth on stub.");
+ masm.bind(&ok);
+ }
+#endif // DEBUG
+
+ // Check key identity.
+ Register strExtract = masm.extractString(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICIn_NativeDoesNotExist::offsetOfName()), scratch);
+ masm.branchPtr(Assembler::NotEqual, strExtract, scratch, &failure);
+
+ // Unbox and guard against old shape.
+ Register objReg = masm.extractObject(R1, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICIn_NativeDoesNotExist::offsetOfShape(0)),
+ scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
+
+ // Check the proto chain.
+ Register protoReg = R0.scratchReg();
+ masm.push(R0.scratchReg());
+ for (size_t i = 0; i < protoChainDepth_; ++i) {
+ masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg);
+ masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failurePopR0Scratch);
+ size_t shapeOffset = ICIn_NativeDoesNotExistImpl<0>::offsetOfShape(i + 1);
+ masm.loadPtr(Address(ICStubReg, shapeOffset), scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failurePopR0Scratch);
+ }
+ masm.addToStackPtr(Imm32(sizeof(size_t)));
+
+ // Shape and type checks succeeded, ok to proceed.
+ masm.moveValue(BooleanValue(false), R0);
+
+ EmitReturnFromIC(masm);
+
+ masm.bind(&failurePopR0Scratch);
+ masm.pop(R0.scratchReg());
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICIn_Dense::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+
+ masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratch = regs.takeAny();
+
+ // Unbox and shape guard object.
+ Register obj = masm.extractObject(R1, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICIn_Dense::offsetOfShape()), scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, obj, scratch, &failure);
+
+ // Load obj->elements.
+ masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+
+ // Unbox key and bounds check.
+ Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
+ Register key = masm.extractInt32(R0, ExtractTemp0);
+ masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure);
+
+ // Hole check.
+ JS_STATIC_ASSERT(sizeof(Value) == 8);
+ BaseIndex element(scratch, key, TimesEight);
+ masm.branchTestMagic(Assembler::Equal, element, &failure);
+
+ masm.moveValue(BooleanValue(true), R0);
+
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+// Try to update existing SetProp setter call stubs for the given holder in
+// place with a new shape and setter.
+static bool
+UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
+ ICStub::Kind kind,
+ NativeObject* holder,
+ JSObject* receiver,
+ JSFunction* setter)
+{
+ MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
+ kind == ICStub::SetProp_CallNative);
+ MOZ_ASSERT(holder);
+ MOZ_ASSERT(receiver);
+
+ bool isOwnSetter = (holder == receiver);
+ bool foundMatchingStub = false;
+ ReceiverGuard receiverGuard(receiver);
+ for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
+ if (iter->kind() == kind) {
+ ICSetPropCallSetter* setPropStub = static_cast<ICSetPropCallSetter*>(*iter);
+ if (setPropStub->holder() == holder && setPropStub->isOwnSetter() == isOwnSetter) {
+ // If this is an own setter, update the receiver guard as well,
+ // since that's the shape we'll be guarding on. Furthermore,
+ // isOwnSetter() relies on holderShape_ and receiverGuard_ being
+ // the same shape.
+ if (isOwnSetter)
+ setPropStub->receiverGuard().update(receiverGuard);
+
+ MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
+ !setPropStub->receiverGuard().matches(receiverGuard),
+ "Why didn't we end up using this stub?");
+
+ // We want to update the holder shape to match the new one no
+ // matter what, even if the receiver shape is different.
+ setPropStub->holderShape() = holder->lastProperty();
+
+ // Make sure to update the setter, since a shape change might
+ // have changed which setter we want to use.
+ setPropStub->setter() = setter;
+ if (setPropStub->receiverGuard().matches(receiverGuard))
+ foundMatchingStub = true;
+ }
+ }
+ }
+
+ return foundMatchingStub;
+}
+
+// Attach an optimized stub for a GETGNAME/CALLGNAME slot-read op.
+static bool
+TryAttachGlobalNameValueStub(JSContext* cx, HandleScript script, jsbytecode* pc,
+ ICGetName_Fallback* stub,
+ Handle<LexicalEnvironmentObject*> globalLexical,
+ HandlePropertyName name, bool* attached)
+{
+ MOZ_ASSERT(globalLexical->isGlobal());
+ MOZ_ASSERT(!*attached);
+
+ RootedId id(cx, NameToId(name));
+
+ // The property must be found, and it must be found as a normal data property.
+ RootedShape shape(cx, globalLexical->lookup(cx, id));
+ RootedNativeObject current(cx, globalLexical);
+ while (true) {
+ shape = current->lookup(cx, id);
+ if (shape)
+ break;
+ if (current == globalLexical) {
+ current = &globalLexical->global();
+ } else {
+ JSObject* proto = current->staticPrototype();
+ if (!proto || !proto->is<NativeObject>())
+ return true;
+ current = &proto->as<NativeObject>();
+ }
+ }
+
+ // Instantiate this global property, for use during Ion compilation.
+ if (IsIonEnabled(cx))
+ EnsureTrackPropertyTypes(cx, current, id);
+
+ if (shape->hasDefaultGetter() && shape->hasSlot()) {
+
+ // TODO: if there's a previous stub discard it, or just update its Shape + slot?
+
+ ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
+ ICStub* newStub;
+ if (current == globalLexical) {
+ MOZ_ASSERT(shape->slot() >= current->numFixedSlots());
+ uint32_t slot = shape->slot() - current->numFixedSlots();
+
+ JitSpew(JitSpew_BaselineIC, " Generating GetName(GlobalName lexical) stub");
+ ICGetName_GlobalLexical::Compiler compiler(cx, monitorStub, slot);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ } else {
+ bool isFixedSlot;
+ uint32_t offset;
+ GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
+
+ // Check the prototype chain from the global to the current
+ // prototype. Ignore the global lexical scope as it doesn' figure
+ // into the prototype chain. We guard on the global lexical
+ // scope's shape independently.
+ if (!IsCacheableGetPropReadSlot(&globalLexical->global(), current, shape))
+ return true;
+
+ JitSpew(JitSpew_BaselineIC, " Generating GetName(GlobalName non-lexical) stub");
+ ICGetPropNativeCompiler compiler(cx, ICStub::GetName_Global,
+ ICStubCompiler::Engine::Baseline, monitorStub,
+ globalLexical, current, name, isFixedSlot, offset,
+ /* inputDefinitelyObject = */ true);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ }
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ }
+ return true;
+}
+
+// Attach an optimized stub for a GETGNAME/CALLGNAME getter op.
+static bool
+TryAttachGlobalNameAccessorStub(JSContext* cx, HandleScript script, jsbytecode* pc,
+ ICGetName_Fallback* stub,
+ Handle<LexicalEnvironmentObject*> globalLexical,
+ HandlePropertyName name, bool* attached,
+ bool* isTemporarilyUnoptimizable)
+{
+ MOZ_ASSERT(globalLexical->isGlobal());
+ RootedId id(cx, NameToId(name));
+
+ // There must not be a shadowing binding on the global lexical scope.
+ if (globalLexical->lookup(cx, id))
+ return true;
+
+ RootedGlobalObject global(cx, &globalLexical->global());
+
+ // The property must be found, and it must be found as a normal data property.
+ RootedShape shape(cx);
+ RootedNativeObject current(cx, global);
+ while (true) {
+ shape = current->lookup(cx, id);
+ if (shape)
+ break;
+ JSObject* proto = current->staticPrototype();
+ if (!proto || !proto->is<NativeObject>())
+ return true;
+ current = &proto->as<NativeObject>();
+ }
+
+ // Instantiate this global property, for use during Ion compilation.
+ if (IsIonEnabled(cx))
+ EnsureTrackPropertyTypes(cx, current, id);
+
+ // Try to add a getter stub. We don't handle scripted getters yet; if this
+ // changes we need to make sure IonBuilder::getPropTryCommonGetter (which
+ // requires a Baseline stub) handles non-outerized this objects correctly.
+ bool isScripted;
+ if (IsCacheableGetPropCall(cx, global, current, shape, &isScripted, isTemporarilyUnoptimizable) &&
+ !isScripted)
+ {
+ ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
+ RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
+
+ // The CallNativeGlobal stub needs to generate 3 shape checks:
+ //
+ // 1. The global lexical scope shape check.
+ // 2. The global object shape check.
+ // 3. The holder shape check.
+ //
+ // 1 is done as the receiver check, as for GETNAME the global lexical scope is in the
+ // receiver position. 2 is done as a manual check that other GetProp stubs don't do. 3 is
+ // done as the holder check per normal.
+ //
+ // In the case the holder is the global object, check 2 is redundant but is not yet
+ // optimized away.
+ JitSpew(JitSpew_BaselineIC, " Generating GetName(GlobalName/NativeGetter) stub");
+ if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNativeGlobal, current,
+ globalLexical, getter))
+ {
+ *attached = true;
+ return true;
+ }
+ ICGetPropCallNativeCompiler compiler(cx, ICStub::GetProp_CallNativeGlobal,
+ ICStubCompiler::Engine::Baseline,
+ monitorStub, globalLexical, current,
+ getter, script->pcToOffset(pc),
+ /* outerClass = */ nullptr,
+ /* inputDefinitelyObject = */ true);
+
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ }
+ return true;
+}
+
+static bool
+TryAttachEnvNameStub(JSContext* cx, HandleScript script, ICGetName_Fallback* stub,
+ HandleObject initialEnvChain, HandlePropertyName name, bool* attached)
+{
+ MOZ_ASSERT(!*attached);
+
+ Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
+ RootedId id(cx, NameToId(name));
+ RootedObject envChain(cx, initialEnvChain);
+
+ Shape* shape = nullptr;
+ while (envChain) {
+ if (!shapes.append(envChain->maybeShape()))
+ return false;
+
+ if (envChain->is<GlobalObject>()) {
+ shape = envChain->as<GlobalObject>().lookup(cx, id);
+ if (shape)
+ break;
+ return true;
+ }
+
+ if (!envChain->is<EnvironmentObject>() || envChain->is<WithEnvironmentObject>())
+ return true;
+
+ // Check for an 'own' property on the env. There is no need to
+ // check the prototype as non-with scopes do not inherit properties
+ // from any prototype.
+ shape = envChain->as<NativeObject>().lookup(cx, id);
+ if (shape)
+ break;
+
+ envChain = envChain->enclosingEnvironment();
+ }
+
+ // We don't handle getters here. When this changes, we need to make sure
+ // IonBuilder::getPropTryCommonGetter (which requires a Baseline stub to
+ // work) handles non-outerized this objects correctly.
+
+ if (!IsCacheableGetPropReadSlot(envChain, envChain, shape))
+ return true;
+
+ bool isFixedSlot;
+ uint32_t offset;
+ GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
+
+ ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
+ ICStub* newStub;
+
+ switch (shapes.length()) {
+ case 1: {
+ ICGetName_Env<0>::Compiler compiler(cx, monitorStub, Move(shapes.get()), isFixedSlot,
+ offset);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ break;
+ }
+ case 2: {
+ ICGetName_Env<1>::Compiler compiler(cx, monitorStub, Move(shapes.get()), isFixedSlot,
+ offset);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ break;
+ }
+ case 3: {
+ ICGetName_Env<2>::Compiler compiler(cx, monitorStub, Move(shapes.get()), isFixedSlot,
+ offset);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ break;
+ }
+ case 4: {
+ ICGetName_Env<3>::Compiler compiler(cx, monitorStub, Move(shapes.get()), isFixedSlot,
+ offset);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ break;
+ }
+ case 5: {
+ ICGetName_Env<4>::Compiler compiler(cx, monitorStub, Move(shapes.get()), isFixedSlot,
+ offset);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ break;
+ }
+ case 6: {
+ ICGetName_Env<5>::Compiler compiler(cx, monitorStub, Move(shapes.get()), isFixedSlot,
+ offset);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ break;
+ }
+ case 7: {
+ ICGetName_Env<6>::Compiler compiler(cx, monitorStub, Move(shapes.get()), isFixedSlot,
+ offset);
+ newStub = compiler.getStub(compiler.getStubSpace(script));
+ break;
+ }
+ default:
+ return true;
+ }
+
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+}
+
+static bool
+DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_,
+ HandleObject envChain, MutableHandleValue res)
+{
+ SharedStubInfo info(cx, frame, stub_->icEntry());
+
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICGetName_Fallback*> stub(frame, stub_);
+
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = stub->icEntry()->pc(script);
+ mozilla::DebugOnly<JSOp> op = JSOp(*pc);
+ FallbackICSpew(cx, stub, "GetName(%s)", CodeName[JSOp(*pc)]);
+
+ MOZ_ASSERT(op == JSOP_GETNAME || op == JSOP_GETGNAME);
+
+ RootedPropertyName name(cx, script->getName(pc));
+ bool attached = false;
+ bool isTemporarilyUnoptimizable = false;
+
+ // Attach new stub.
+ if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) {
+ // TODO: Discard all stubs in this IC and replace with generic stub.
+ attached = true;
+ }
+
+ if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) {
+ if (!TryAttachGlobalNameAccessorStub(cx, script, pc, stub,
+ envChain.as<LexicalEnvironmentObject>(),
+ name, &attached, &isTemporarilyUnoptimizable))
+ {
+ return false;
+ }
+ }
+
+ static_assert(JSOP_GETGNAME_LENGTH == JSOP_GETNAME_LENGTH,
+ "Otherwise our check for JSOP_TYPEOF isn't ok");
+ if (JSOp(pc[JSOP_GETGNAME_LENGTH]) == JSOP_TYPEOF) {
+ if (!GetEnvironmentNameForTypeOf(cx, envChain, name, res))
+ return false;
+ } else {
+ if (!GetEnvironmentName(cx, envChain, name, res))
+ return false;
+ }
+
+ TypeScript::Monitor(cx, script, pc, res);
+
+ // Check if debug mode toggling made the stub invalid.
+ if (stub.invalid())
+ return true;
+
+ // Add a type monitor stub for the resulting value.
+ if (!stub->addMonitorStubForValue(cx, &info, res))
+ return false;
+ if (attached)
+ return true;
+
+ if (IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) {
+ Handle<LexicalEnvironmentObject*> globalLexical = envChain.as<LexicalEnvironmentObject>();
+ if (!TryAttachGlobalNameValueStub(cx, script, pc, stub, globalLexical, name, &attached))
+ return false;
+ } else {
+ if (!TryAttachEnvNameStub(cx, script, stub, envChain, name, &attached))
+ return false;
+ }
+
+ if (!attached && !isTemporarilyUnoptimizable)
+ stub->noteUnoptimizableAccess();
+ return true;
+}
+
+typedef bool (*DoGetNameFallbackFn)(JSContext*, BaselineFrame*, ICGetName_Fallback*,
+ HandleObject, MutableHandleValue);
+static const VMFunction DoGetNameFallbackInfo =
+ FunctionInfo<DoGetNameFallbackFn>(DoGetNameFallback, "DoGetNameFallback", TailCall);
+
+bool
+ICGetName_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+ MOZ_ASSERT(R0 == JSReturnOperand);
+
+ EmitRestoreTailCallReg(masm);
+
+ masm.push(R0.scratchReg());
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoGetNameFallbackInfo, masm);
+}
+
+bool
+ICGetName_GlobalLexical::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ Register obj = R0.scratchReg();
+ Register scratch = R1.scratchReg();
+
+ // There's no need to guard on the shape. Lexical bindings are
+ // non-configurable, and this stub cannot be shared across globals.
+
+ // Load dynamic slot.
+ masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj);
+ masm.load32(Address(ICStubReg, ICGetName_GlobalLexical::offsetOfSlot()), scratch);
+ masm.loadValue(BaseIndex(obj, scratch, TimesEight), R0);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+template <size_t NumHops>
+bool
+ICGetName_Env<NumHops>::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
+ Register obj = R0.scratchReg();
+ Register walker = regs.takeAny();
+ Register scratch = regs.takeAny();
+
+ // Use a local to silence Clang tautological-compare warning if NumHops is 0.
+ size_t numHops = NumHops;
+
+ for (size_t index = 0; index < NumHops + 1; index++) {
+ Register scope = index ? walker : obj;
+
+ // Shape guard.
+ masm.loadPtr(Address(ICStubReg, ICGetName_Env::offsetOfShape(index)), scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, scope, scratch, &failure);
+
+ if (index < numHops) {
+ masm.extractObject(Address(scope, EnvironmentObject::offsetOfEnclosingEnvironment()),
+ walker);
+ }
+ }
+
+ Register scope = NumHops ? walker : obj;
+
+ if (!isFixedSlot_) {
+ masm.loadPtr(Address(scope, NativeObject::offsetOfSlots()), walker);
+ scope = walker;
+ }
+
+ masm.load32(Address(ICStubReg, ICGetName_Env::offsetOfOffset()), scratch);
+
+ // GETNAME needs to check for uninitialized lexicals.
+ BaseIndex slot(scope, scratch, TimesOne);
+ masm.branchTestMagic(Assembler::Equal, slot, &failure);
+ masm.loadValue(slot, R0);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// BindName_Fallback
+//
+
+static bool
+DoBindNameFallback(JSContext* cx, BaselineFrame* frame, ICBindName_Fallback* stub,
+ HandleObject envChain, MutableHandleValue res)
+{
+ jsbytecode* pc = stub->icEntry()->pc(frame->script());
+ mozilla::DebugOnly<JSOp> op = JSOp(*pc);
+ FallbackICSpew(cx, stub, "BindName(%s)", CodeName[JSOp(*pc)]);
+
+ MOZ_ASSERT(op == JSOP_BINDNAME || op == JSOP_BINDGNAME);
+
+ RootedPropertyName name(cx, frame->script()->getName(pc));
+
+ RootedObject scope(cx);
+ if (!LookupNameUnqualified(cx, name, envChain, &scope))
+ return false;
+
+ res.setObject(*scope);
+ return true;
+}
+
+typedef bool (*DoBindNameFallbackFn)(JSContext*, BaselineFrame*, ICBindName_Fallback*,
+ HandleObject, MutableHandleValue);
+static const VMFunction DoBindNameFallbackInfo =
+ FunctionInfo<DoBindNameFallbackFn>(DoBindNameFallback, "DoBindNameFallback", TailCall);
+
+bool
+ICBindName_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+ MOZ_ASSERT(R0 == JSReturnOperand);
+
+ EmitRestoreTailCallReg(masm);
+
+ masm.push(R0.scratchReg());
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoBindNameFallbackInfo, masm);
+}
+
+//
+// GetIntrinsic_Fallback
+//
+
+static bool
+DoGetIntrinsicFallback(JSContext* cx, BaselineFrame* frame, ICGetIntrinsic_Fallback* stub_,
+ MutableHandleValue res)
+{
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICGetIntrinsic_Fallback*> stub(frame, stub_);
+
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = stub->icEntry()->pc(script);
+ mozilla::DebugOnly<JSOp> op = JSOp(*pc);
+ FallbackICSpew(cx, stub, "GetIntrinsic(%s)", CodeName[JSOp(*pc)]);
+
+ MOZ_ASSERT(op == JSOP_GETINTRINSIC);
+
+ if (!GetIntrinsicOperation(cx, pc, res))
+ return false;
+
+ // An intrinsic operation will always produce the same result, so only
+ // needs to be monitored once. Attach a stub to load the resulting constant
+ // directly.
+
+ TypeScript::Monitor(cx, script, pc, res);
+
+ // Check if debug mode toggling made the stub invalid.
+ if (stub.invalid())
+ return true;
+
+ JitSpew(JitSpew_BaselineIC, " Generating GetIntrinsic optimized stub");
+ ICGetIntrinsic_Constant::Compiler compiler(cx, res);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ return true;
+}
+
+typedef bool (*DoGetIntrinsicFallbackFn)(JSContext*, BaselineFrame*, ICGetIntrinsic_Fallback*,
+ MutableHandleValue);
+static const VMFunction DoGetIntrinsicFallbackInfo =
+ FunctionInfo<DoGetIntrinsicFallbackFn>(DoGetIntrinsicFallback, "DoGetIntrinsicFallback",
+ TailCall);
+
+bool
+ICGetIntrinsic_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ EmitRestoreTailCallReg(masm);
+
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoGetIntrinsicFallbackInfo, masm);
+}
+
+bool
+ICGetIntrinsic_Constant::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ masm.loadValue(Address(ICStubReg, ICGetIntrinsic_Constant::offsetOfValue()), R0);
+
+ EmitReturnFromIC(masm);
+ return true;
+}
+
+//
+// SetProp_Fallback
+//
+
+// Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on a
+// value property.
+static bool
+TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, ICSetProp_Fallback* stub,
+ HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
+ HandlePropertyName name, HandleId id, HandleValue rhs, bool* attached)
+{
+ MOZ_ASSERT(!*attached);
+
+ if (obj->watched())
+ return true;
+
+ RootedShape shape(cx);
+ RootedObject holder(cx);
+ if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
+ return false;
+ if (obj != holder)
+ return true;
+
+ if (!obj->isNative()) {
+ if (obj->is<UnboxedPlainObject>()) {
+ UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
+ if (expando) {
+ shape = expando->lookup(cx, name);
+ if (!shape)
+ return true;
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ size_t chainDepth;
+ if (IsCacheableSetPropAddSlot(cx, obj, oldShape, id, shape, &chainDepth)) {
+ // Don't attach if proto chain depth is too high.
+ if (chainDepth > ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH)
+ return true;
+
+ // Don't attach if we are adding a property to an object which the new
+ // script properties analysis hasn't been performed for yet, as there
+ // may be a shape change required here afterwards. Pretend we attached
+ // a stub, though, so the access is not marked as unoptimizable.
+ if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
+ *attached = true;
+ return true;
+ }
+
+ bool isFixedSlot;
+ uint32_t offset;
+ GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
+
+ JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.ADD) stub");
+ ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldGroup,
+ chainDepth, isFixedSlot, offset);
+ ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+ if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ if (IsCacheableSetPropWriteSlot(obj, oldShape, shape)) {
+ // For some property writes, such as the initial overwrite of global
+ // properties, TI will not mark the property as having been
+ // overwritten. Don't attach a stub in this case, so that we don't
+ // execute another write to the property without TI seeing that write.
+ EnsureTrackPropertyTypes(cx, obj, id);
+ if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
+ *attached = true;
+ return true;
+ }
+
+ bool isFixedSlot;
+ uint32_t offset;
+ GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
+
+ JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.PROP) stub");
+ MOZ_ASSERT(LastPropertyForSetProp(obj) == oldShape,
+ "Should this really be a SetPropWriteSlot?");
+ ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
+ ICSetProp_Native* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+ if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
+ return false;
+
+ if (IsPreliminaryObject(obj))
+ newStub->notePreliminaryObject();
+ else
+ StripPreliminaryObjectStubs(cx, stub);
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ return true;
+}
+
+// Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on
+// an accessor property.
+static bool
+TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
+ ICSetProp_Fallback* stub,
+ HandleObject obj, const RootedReceiverGuard& receiverGuard,
+ HandlePropertyName name,
+ HandleId id, HandleValue rhs, bool* attached,
+ bool* isTemporarilyUnoptimizable)
+{
+ MOZ_ASSERT(!*attached);
+ MOZ_ASSERT(!*isTemporarilyUnoptimizable);
+
+ if (obj->watched())
+ return true;
+
+ RootedShape shape(cx);
+ RootedObject holder(cx);
+ if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
+ return false;
+
+ bool isScripted = false;
+ bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape,
+ &isScripted, isTemporarilyUnoptimizable);
+
+ // Try handling scripted setters.
+ if (cacheableCall && isScripted) {
+ RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
+ MOZ_ASSERT(callee->hasScript());
+
+ if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallScripted,
+ &holder->as<NativeObject>(), obj, callee)) {
+ *attached = true;
+ return true;
+ }
+
+ JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObj/ScriptedSetter %s:%" PRIuSIZE ") stub",
+ callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
+
+ ICSetProp_CallScripted::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ // Try handling JSNative setters.
+ if (cacheableCall && !isScripted) {
+ RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
+ MOZ_ASSERT(callee->isNative());
+
+ if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallNative,
+ &holder->as<NativeObject>(), obj, callee)) {
+ *attached = true;
+ return true;
+ }
+
+ JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObj/NativeSetter %p) stub",
+ callee->native());
+
+ ICSetProp_CallNative::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ return true;
+}
+
+static bool
+TryAttachUnboxedSetPropStub(JSContext* cx, HandleScript script,
+ ICSetProp_Fallback* stub, HandleId id,
+ HandleObject obj, HandleValue rhs, bool* attached)
+{
+ MOZ_ASSERT(!*attached);
+
+ if (!cx->runtime()->jitSupportsFloatingPoint)
+ return true;
+
+ if (!obj->is<UnboxedPlainObject>())
+ return true;
+
+ const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
+ if (!property)
+ return true;
+
+ ICSetProp_Unboxed::Compiler compiler(cx, obj->group(),
+ property->offset + UnboxedPlainObject::offsetOfData(),
+ property->type);
+ ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+ if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
+ return false;
+
+ stub->addNewStub(newStub);
+
+ StripPreliminaryObjectStubs(cx, stub);
+
+ *attached = true;
+ return true;
+}
+
+static bool
+TryAttachTypedObjectSetPropStub(JSContext* cx, HandleScript script,
+ ICSetProp_Fallback* stub, HandleId id,
+ HandleObject obj, HandleValue rhs, bool* attached)
+{
+ MOZ_ASSERT(!*attached);
+
+ if (!cx->runtime()->jitSupportsFloatingPoint)
+ return true;
+
+ if (!obj->is<TypedObject>())
+ return true;
+
+ if (!obj->as<TypedObject>().typeDescr().is<StructTypeDescr>())
+ return true;
+ Rooted<StructTypeDescr*> structDescr(cx);
+ structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>();
+
+ size_t fieldIndex;
+ if (!structDescr->fieldIndex(id, &fieldIndex))
+ return true;
+
+ Rooted<TypeDescr*> fieldDescr(cx, &structDescr->fieldDescr(fieldIndex));
+ if (!fieldDescr->is<SimpleTypeDescr>())
+ return true;
+
+ uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
+
+ ICSetProp_TypedObject::Compiler compiler(cx, obj->maybeShape(), obj->group(), fieldOffset,
+ &fieldDescr->as<SimpleTypeDescr>());
+ ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+ if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
+ return false;
+
+ stub->addNewStub(newStub);
+
+ *attached = true;
+ return true;
+}
+
+static bool
+DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_,
+ HandleValue lhs, HandleValue rhs, MutableHandleValue res)
+{
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICSetProp_Fallback*> stub(frame, stub_);
+
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = stub->icEntry()->pc(script);
+ JSOp op = JSOp(*pc);
+ FallbackICSpew(cx, stub, "SetProp(%s)", CodeName[op]);
+
+ MOZ_ASSERT(op == JSOP_SETPROP ||
+ op == JSOP_STRICTSETPROP ||
+ op == JSOP_SETNAME ||
+ op == JSOP_STRICTSETNAME ||
+ op == JSOP_SETGNAME ||
+ op == JSOP_STRICTSETGNAME ||
+ op == JSOP_INITPROP ||
+ op == JSOP_INITLOCKEDPROP ||
+ op == JSOP_INITHIDDENPROP ||
+ op == JSOP_SETALIASEDVAR ||
+ op == JSOP_INITALIASEDLEXICAL ||
+ op == JSOP_INITGLEXICAL);
+
+ RootedPropertyName name(cx);
+ if (op == JSOP_SETALIASEDVAR || op == JSOP_INITALIASEDLEXICAL)
+ name = EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc);
+ else
+ name = script->getName(pc);
+ RootedId id(cx, NameToId(name));
+
+ RootedObject obj(cx, ToObjectFromStack(cx, lhs));
+ if (!obj)
+ return false;
+ RootedShape oldShape(cx, obj->maybeShape());
+ RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
+ if (!oldGroup)
+ return false;
+ RootedReceiverGuard oldGuard(cx, ReceiverGuard(obj));
+
+ if (obj->is<UnboxedPlainObject>()) {
+ MOZ_ASSERT(!oldShape);
+ if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
+ oldShape = expando->lastProperty();
+ }
+
+ bool attached = false;
+ // There are some reasons we can fail to attach a stub that are temporary.
+ // We want to avoid calling noteUnoptimizableAccess() if the reason we
+ // failed to attach a stub is one of those temporary reasons, since we might
+ // end up attaching a stub for the exact same access later.
+ bool isTemporarilyUnoptimizable = false;
+ if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
+ lhs.isObject() &&
+ !TryAttachSetAccessorPropStub(cx, script, pc, stub, obj, oldGuard, name, id,
+ rhs, &attached, &isTemporarilyUnoptimizable))
+ {
+ return false;
+ }
+
+ if (op == JSOP_INITPROP ||
+ op == JSOP_INITLOCKEDPROP ||
+ op == JSOP_INITHIDDENPROP)
+ {
+ if (!InitPropertyOperation(cx, op, obj, id, rhs))
+ return false;
+ } else if (op == JSOP_SETNAME ||
+ op == JSOP_STRICTSETNAME ||
+ op == JSOP_SETGNAME ||
+ op == JSOP_STRICTSETGNAME)
+ {
+ if (!SetNameOperation(cx, script, pc, obj, rhs))
+ return false;
+ } else if (op == JSOP_SETALIASEDVAR || op == JSOP_INITALIASEDLEXICAL) {
+ obj->as<EnvironmentObject>().setAliasedBinding(cx, EnvironmentCoordinate(pc), name, rhs);
+ } else if (op == JSOP_INITGLEXICAL) {
+ RootedValue v(cx, rhs);
+ LexicalEnvironmentObject* lexicalEnv;
+ if (script->hasNonSyntacticScope())
+ lexicalEnv = &NearestEnclosingExtensibleLexicalEnvironment(frame->environmentChain());
+ else
+ lexicalEnv = &cx->global()->lexicalEnvironment();
+ InitGlobalLexicalOperation(cx, lexicalEnv, script, pc, v);
+ } else {
+ MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP);
+
+ ObjectOpResult result;
+ if (!SetProperty(cx, obj, id, rhs, lhs, result) ||
+ !result.checkStrictErrorOrWarning(cx, obj, id, op == JSOP_STRICTSETPROP))
+ {
+ return false;
+ }
+ }
+
+ // Leave the RHS on the stack.
+ res.set(rhs);
+
+ // Check if debug mode toggling made the stub invalid.
+ if (stub.invalid())
+ return true;
+
+ if (stub->numOptimizedStubs() >= ICSetProp_Fallback::MAX_OPTIMIZED_STUBS) {
+ // TODO: Discard all stubs in this IC and replace with generic setprop stub.
+ return true;
+ }
+
+ if (!attached &&
+ lhs.isObject() &&
+ !TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldShape, oldGroup,
+ name, id, rhs, &attached))
+ {
+ return false;
+ }
+ if (attached)
+ return true;
+
+ if (!attached &&
+ lhs.isObject() &&
+ !TryAttachUnboxedSetPropStub(cx, script, stub, id, obj, rhs, &attached))
+ {
+ return false;
+ }
+ if (attached)
+ return true;
+
+ if (!attached &&
+ lhs.isObject() &&
+ !TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
+ {
+ return false;
+ }
+ if (attached)
+ return true;
+
+ MOZ_ASSERT(!attached);
+ if (!isTemporarilyUnoptimizable)
+ stub->noteUnoptimizableAccess();
+
+ return true;
+}
+
+typedef bool (*DoSetPropFallbackFn)(JSContext*, BaselineFrame*, ICSetProp_Fallback*,
+ HandleValue, HandleValue, MutableHandleValue);
+static const VMFunction DoSetPropFallbackInfo =
+ FunctionInfo<DoSetPropFallbackFn>(DoSetPropFallback, "DoSetPropFallback", TailCall,
+ PopValues(2));
+
+bool
+ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+ MOZ_ASSERT(R0 == JSReturnOperand);
+
+ EmitRestoreTailCallReg(masm);
+
+ // Ensure stack is fully synced for the expression decompiler.
+ masm.pushValue(R0);
+ masm.pushValue(R1);
+
+ // Push arguments.
+ masm.pushValue(R1);
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ if (!tailCallVM(DoSetPropFallbackInfo, masm))
+ return false;
+
+ // Even though the fallback frame doesn't enter a stub frame, the CallScripted
+ // frame that we are emulating does. Again, we lie.
+#ifdef DEBUG
+ EmitRepushTailCallReg(masm);
+ EmitStowICValues(masm, 1);
+ enterStubFrame(masm, R1.scratchReg());
+#else
+ inStubFrame_ = true;
+#endif
+
+ // What follows is bailout-only code for inlined script getters.
+ // The return address pointed to by the baseline stack points here.
+ returnOffset_ = masm.currentOffset();
+
+ leaveStubFrame(masm, true);
+
+ // Retrieve the stashed initial argument from the caller's frame before returning
+ EmitUnstowICValues(masm, 1);
+ EmitReturnFromIC(masm);
+
+ return true;
+}
+
+void
+ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<JitCode*> code)
+{
+ cx->compartment()->jitCompartment()->initBaselineSetPropReturnAddr(code->raw() + returnOffset_);
+}
+
+static void
+GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj,
+ Register object, Register scratch,
+ size_t offsetOfGroup, size_t offsetOfShape, Label* failure)
+{
+ // Guard against object group.
+ masm.loadPtr(Address(ICStubReg, offsetOfGroup), scratch);
+ masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch,
+ failure);
+
+ // Guard against shape or expando shape.
+ masm.loadPtr(Address(ICStubReg, offsetOfShape), scratch);
+ if (obj->is<UnboxedPlainObject>()) {
+ Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
+ masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
+ Label done;
+ masm.push(object);
+ masm.loadPtr(expandoAddress, object);
+ masm.branchTestObjShape(Assembler::Equal, object, scratch, &done);
+ masm.pop(object);
+ masm.jump(failure);
+ masm.bind(&done);
+ masm.pop(object);
+ } else {
+ masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
+ }
+}
+
+bool
+ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+
+ // Guard input is an object.
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+ Register objReg = masm.extractObject(R0, ExtractTemp0);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratch = regs.takeAny();
+
+ GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch,
+ ICSetProp_Native::offsetOfGroup(),
+ ICSetProp_Native::offsetOfShape(),
+ &failure);
+
+ // Stow both R0 and R1 (object and value).
+ EmitStowICValues(masm, 2);
+
+ // Type update stub expects the value to check in R0.
+ masm.moveValue(R1, R0);
+
+ // Call the type-update stub.
+ if (!callTypeUpdateIC(masm, sizeof(Value)))
+ return false;
+
+ // Unstow R0 and R1 (object and key)
+ EmitUnstowICValues(masm, 2);
+
+ regs.add(R0);
+ regs.takeUnchecked(objReg);
+
+ Register holderReg;
+ if (obj_->is<UnboxedPlainObject>()) {
+ // We are loading off the expando object, so use that for the holder.
+ holderReg = regs.takeAny();
+ masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
+ if (!isFixedSlot_)
+ masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
+ } else if (isFixedSlot_) {
+ holderReg = objReg;
+ } else {
+ holderReg = regs.takeAny();
+ masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
+ }
+
+ // Perform the store.
+ masm.load32(Address(ICStubReg, ICSetProp_Native::offsetOfOffset()), scratch);
+ EmitPreBarrier(masm, BaseIndex(holderReg, scratch, TimesOne), MIRType::Value);
+ masm.storeValue(R1, BaseIndex(holderReg, scratch, TimesOne));
+ if (holderReg != objReg)
+ regs.add(holderReg);
+ if (cx->runtime()->gc.nursery.exists()) {
+ Register scr = regs.takeAny();
+ LiveGeneralRegisterSet saveRegs;
+ saveRegs.add(R1);
+ emitPostWriteBarrierSlot(masm, objReg, R1, scr, saveRegs);
+ regs.add(scr);
+ }
+
+ // The RHS has to be in R0.
+ masm.moveValue(R1, R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+ICUpdatedStub*
+ICSetPropNativeAddCompiler::getStub(ICStubSpace* space)
+{
+ Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
+ if (!shapes.append(oldShape_))
+ return nullptr;
+
+ if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
+ return nullptr;
+
+ JS_STATIC_ASSERT(ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH == 4);
+
+ ICUpdatedStub* stub = nullptr;
+ switch(protoChainDepth_) {
+ case 0: stub = getStubSpecific<0>(space, shapes); break;
+ case 1: stub = getStubSpecific<1>(space, shapes); break;
+ case 2: stub = getStubSpecific<2>(space, shapes); break;
+ case 3: stub = getStubSpecific<3>(space, shapes); break;
+ case 4: stub = getStubSpecific<4>(space, shapes); break;
+ default: MOZ_CRASH("ProtoChainDepth too high.");
+ }
+ if (!stub || !stub->initUpdatingChain(cx, space))
+ return nullptr;
+ return stub;
+}
+
+bool
+ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ Label failureUnstow;
+
+ // Guard input is an object.
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+ Register objReg = masm.extractObject(R0, ExtractTemp0);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratch = regs.takeAny();
+
+ GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch,
+ ICSetProp_NativeAdd::offsetOfGroup(),
+ ICSetProp_NativeAddImpl<0>::offsetOfShape(0),
+ &failure);
+
+ // Stow both R0 and R1 (object and value).
+ EmitStowICValues(masm, 2);
+
+ regs = availableGeneralRegs(1);
+ scratch = regs.takeAny();
+ Register protoReg = regs.takeAny();
+ // Check the proto chain.
+ for (size_t i = 0; i < protoChainDepth_; i++) {
+ masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg);
+ masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failureUnstow);
+ masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAddImpl<0>::offsetOfShape(i + 1)),
+ scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failureUnstow);
+ }
+
+ // Shape and type checks succeeded, ok to proceed.
+
+ // Load RHS into R0 for TypeUpdate check.
+ // Stack is currently: [..., ObjValue, RHSValue, MaybeReturnAddr? ]
+ masm.loadValue(Address(masm.getStackPointer(), ICStackValueOffset), R0);
+
+ // Call the type-update stub.
+ if (!callTypeUpdateIC(masm, sizeof(Value)))
+ return false;
+
+ // Unstow R0 and R1 (object and key)
+ EmitUnstowICValues(masm, 2);
+ regs = availableGeneralRegs(2);
+ scratch = regs.takeAny();
+
+ if (obj_->is<PlainObject>()) {
+ // Try to change the object's group.
+ Label noGroupChange;
+
+ // Check if the cache has a new group to change to.
+ masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
+ masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange);
+
+ // Check if the old group still has a newScript.
+ masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch);
+ masm.branchPtr(Assembler::Equal,
+ Address(scratch, ObjectGroup::offsetOfAddendum()),
+ ImmWord(0),
+ &noGroupChange);
+
+ // Reload the new group from the cache.
+ masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
+
+ // Change the object's group.
+ Address groupAddr(objReg, JSObject::offsetOfGroup());
+ EmitPreBarrier(masm, groupAddr, MIRType::ObjectGroup);
+ masm.storePtr(scratch, groupAddr);
+
+ masm.bind(&noGroupChange);
+ }
+
+ Register holderReg;
+ regs.add(R0);
+ regs.takeUnchecked(objReg);
+
+ if (obj_->is<UnboxedPlainObject>()) {
+ holderReg = regs.takeAny();
+ masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
+
+ // Write the expando object's new shape.
+ Address shapeAddr(holderReg, ShapedObject::offsetOfShape());
+ EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
+ masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
+ masm.storePtr(scratch, shapeAddr);
+
+ if (!isFixedSlot_)
+ masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
+ } else {
+ // Write the object's new shape.
+ Address shapeAddr(objReg, ShapedObject::offsetOfShape());
+ EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
+ masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
+ masm.storePtr(scratch, shapeAddr);
+
+ if (isFixedSlot_) {
+ holderReg = objReg;
+ } else {
+ holderReg = regs.takeAny();
+ masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
+ }
+ }
+
+ // Perform the store. No write barrier required since this is a new
+ // initialization.
+ masm.load32(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfOffset()), scratch);
+ masm.storeValue(R1, BaseIndex(holderReg, scratch, TimesOne));
+
+ if (holderReg != objReg)
+ regs.add(holderReg);
+
+ if (cx->runtime()->gc.nursery.exists()) {
+ Register scr = regs.takeAny();
+ LiveGeneralRegisterSet saveRegs;
+ saveRegs.add(R1);
+ emitPostWriteBarrierSlot(masm, objReg, R1, scr, saveRegs);
+ }
+
+ // The RHS has to be in R0.
+ masm.moveValue(R1, R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failureUnstow);
+ EmitUnstowICValues(masm, 2);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+
+ // Guard input is an object.
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratch = regs.takeAny();
+
+ // Unbox and group guard.
+ Register object = masm.extractObject(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICSetProp_Unboxed::offsetOfGroup()), scratch);
+ masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch,
+ &failure);
+
+ if (needsUpdateStubs()) {
+ // Stow both R0 and R1 (object and value).
+ EmitStowICValues(masm, 2);
+
+ // Move RHS into R0 for TypeUpdate check.
+ masm.moveValue(R1, R0);
+
+ // Call the type update stub.
+ if (!callTypeUpdateIC(masm, sizeof(Value)))
+ return false;
+
+ // Unstow R0 and R1 (object and key)
+ EmitUnstowICValues(masm, 2);
+
+ // The TypeUpdate IC may have smashed object. Rederive it.
+ masm.unboxObject(R0, object);
+
+ // Trigger post barriers here on the values being written. Fields which
+ // objects can be written to also need update stubs.
+ LiveGeneralRegisterSet saveRegs;
+ saveRegs.add(R0);
+ saveRegs.add(R1);
+ saveRegs.addUnchecked(object);
+ saveRegs.add(ICStubReg);
+ emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs);
+ }
+
+ // Compute the address being written to.
+ masm.load32(Address(ICStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch);
+ BaseIndex address(object, scratch, TimesOne);
+
+ EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_);
+ masm.storeUnboxedProperty(address, fieldType_,
+ ConstantOrRegister(TypedOrValueRegister(R1)), &failure);
+
+ // The RHS has to be in R0.
+ masm.moveValue(R1, R0);
+
+ EmitReturnFromIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+
+ CheckForTypedObjectWithDetachedStorage(cx, masm, &failure);
+
+ // Guard input is an object.
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+ Register scratch = regs.takeAny();
+
+ // Unbox and shape guard.
+ Register object = masm.extractObject(R0, ExtractTemp0);
+ masm.loadPtr(Address(ICStubReg, ICSetProp_TypedObject::offsetOfShape()), scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, object, scratch, &failure);
+
+ // Guard that the object group matches.
+ masm.loadPtr(Address(ICStubReg, ICSetProp_TypedObject::offsetOfGroup()), scratch);
+ masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch,
+ &failure);
+
+ if (needsUpdateStubs()) {
+ // Stow both R0 and R1 (object and value).
+ EmitStowICValues(masm, 2);
+
+ // Move RHS into R0 for TypeUpdate check.
+ masm.moveValue(R1, R0);
+
+ // Call the type update stub.
+ if (!callTypeUpdateIC(masm, sizeof(Value)))
+ return false;
+
+ // Unstow R0 and R1 (object and key)
+ EmitUnstowICValues(masm, 2);
+
+ // We may have clobbered object in the TypeUpdate IC. Rederive it.
+ masm.unboxObject(R0, object);
+
+ // Trigger post barriers here on the values being written. Descriptors
+ // which can write objects also need update stubs.
+ LiveGeneralRegisterSet saveRegs;
+ saveRegs.add(R0);
+ saveRegs.add(R1);
+ saveRegs.addUnchecked(object);
+ saveRegs.add(ICStubReg);
+ emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs);
+ }
+
+ // Save the rhs on the stack so we can get a second scratch register.
+ Label failurePopRHS;
+ masm.pushValue(R1);
+ regs = availableGeneralRegs(1);
+ regs.takeUnchecked(object);
+ regs.take(scratch);
+ Register secondScratch = regs.takeAny();
+
+ // Get the object's data pointer.
+ LoadTypedThingData(masm, layout_, object, scratch);
+
+ // Compute the address being written to.
+ masm.load32(Address(ICStubReg, ICSetProp_TypedObject::offsetOfFieldOffset()), secondScratch);
+ masm.addPtr(secondScratch, scratch);
+
+ Address dest(scratch, 0);
+ Address value(masm.getStackPointer(), 0);
+
+ if (fieldDescr_->is<ScalarTypeDescr>()) {
+ Scalar::Type type = fieldDescr_->as<ScalarTypeDescr>().type();
+ StoreToTypedArray(cx, masm, type, value, dest,
+ secondScratch, &failurePopRHS, &failurePopRHS);
+ masm.popValue(R1);
+ } else {
+ ReferenceTypeDescr::Type type = fieldDescr_->as<ReferenceTypeDescr>().type();
+
+ masm.popValue(R1);
+
+ switch (type) {
+ case ReferenceTypeDescr::TYPE_ANY:
+ EmitPreBarrier(masm, dest, MIRType::Value);
+ masm.storeValue(R1, dest);
+ break;
+
+ case ReferenceTypeDescr::TYPE_OBJECT: {
+ EmitPreBarrier(masm, dest, MIRType::Object);
+ Label notObject;
+ masm.branchTestObject(Assembler::NotEqual, R1, &notObject);
+ Register rhsObject = masm.extractObject(R1, ExtractTemp0);
+ masm.storePtr(rhsObject, dest);
+ EmitReturnFromIC(masm);
+ masm.bind(&notObject);
+ masm.branchTestNull(Assembler::NotEqual, R1, &failure);
+ masm.storePtr(ImmWord(0), dest);
+ break;
+ }
+
+ case ReferenceTypeDescr::TYPE_STRING: {
+ EmitPreBarrier(masm, dest, MIRType::String);
+ masm.branchTestString(Assembler::NotEqual, R1, &failure);
+ Register rhsString = masm.extractString(R1, ExtractTemp0);
+ masm.storePtr(rhsString, dest);
+ break;
+ }
+
+ default:
+ MOZ_CRASH();
+ }
+ }
+
+ // The RHS has to be in R0.
+ masm.moveValue(R1, R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&failurePopRHS);
+ masm.popValue(R1);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ Label failureUnstow;
+ Label failureLeaveStubFrame;
+
+ // Guard input is an object.
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ // Stow R0 and R1 to free up registers.
+ EmitStowICValues(masm, 2);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
+ Register scratch = regs.takeAnyExcluding(ICTailCallReg);
+
+ // Unbox and shape guard.
+ uint32_t framePushed = masm.framePushed();
+ Register objReg = masm.extractObject(R0, ExtractTemp0);
+ GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
+ ICSetProp_CallScripted::offsetOfReceiverGuard(), &failureUnstow);
+
+ if (receiver_ != holder_) {
+ Register holderReg = regs.takeAny();
+ masm.loadPtr(Address(ICStubReg, ICSetProp_CallScripted::offsetOfHolder()), holderReg);
+ masm.loadPtr(Address(ICStubReg, ICSetProp_CallScripted::offsetOfHolderShape()), scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
+ regs.add(holderReg);
+ }
+
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, scratch);
+
+ // Load callee function and code. To ensure that |code| doesn't end up being
+ // ArgumentsRectifierReg, if it's available we assign it to |callee| instead.
+ Register callee;
+ if (regs.has(ArgumentsRectifierReg)) {
+ callee = ArgumentsRectifierReg;
+ regs.take(callee);
+ } else {
+ callee = regs.takeAny();
+ }
+ Register code = regs.takeAny();
+ masm.loadPtr(Address(ICStubReg, ICSetProp_CallScripted::offsetOfSetter()), callee);
+ masm.branchIfFunctionHasNoScript(callee, &failureLeaveStubFrame);
+ masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
+ masm.loadBaselineOrIonRaw(code, code, &failureLeaveStubFrame);
+
+ // Align the stack such that the JitFrameLayout is aligned on
+ // JitStackAlignment.
+ masm.alignJitStackBasedOnNArgs(1);
+
+ // Setter is called with the new value as the only argument, and |obj| as thisv.
+ // Note that we use Push, not push, so that callJit will align the stack
+ // properly on ARM.
+
+ // To Push R1, read it off of the stowed values on stack.
+ // Stack: [ ..., R0, R1, ..STUBFRAME-HEADER.., padding? ]
+ masm.PushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
+ masm.Push(R0);
+ EmitBaselineCreateStubFrameDescriptor(masm, scratch, JitFrameLayout::Size());
+ masm.Push(Imm32(1)); // ActualArgc is 1
+ masm.Push(callee);
+ masm.Push(scratch);
+
+ // Handle arguments underflow.
+ Label noUnderflow;
+ masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
+ masm.branch32(Assembler::BelowOrEqual, scratch, Imm32(1), &noUnderflow);
+ {
+ // Call the arguments rectifier.
+ MOZ_ASSERT(ArgumentsRectifierReg != code);
+
+ JitCode* argumentsRectifier =
+ cx->runtime()->jitRuntime()->getArgumentsRectifier();
+
+ masm.movePtr(ImmGCPtr(argumentsRectifier), code);
+ masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
+ masm.movePtr(ImmWord(1), ArgumentsRectifierReg);
+ }
+
+ masm.bind(&noUnderflow);
+ masm.callJit(code);
+
+ uint32_t framePushedAfterCall = masm.framePushed();
+
+ leaveStubFrame(masm, true);
+ // Do not care about return value from function. The original RHS should be returned
+ // as the result of this operation.
+ EmitUnstowICValues(masm, 2);
+ masm.moveValue(R1, R0);
+ EmitReturnFromIC(masm);
+
+ // Leave stub frame and go to next stub.
+ masm.bind(&failureLeaveStubFrame);
+ masm.setFramePushed(framePushedAfterCall);
+ inStubFrame_ = true;
+ leaveStubFrame(masm, false);
+
+ // Unstow R0 and R1
+ masm.bind(&failureUnstow);
+ masm.setFramePushed(framePushed);
+ EmitUnstowICValues(masm, 2);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+static bool
+DoCallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj, HandleValue val)
+{
+ MOZ_ASSERT(callee->isNative());
+ JSNative natfun = callee->native();
+
+ JS::AutoValueArray<3> vp(cx);
+ vp[0].setObject(*callee.get());
+ vp[1].setObject(*obj.get());
+ vp[2].set(val);
+
+ return natfun(cx, 1, vp.begin());
+}
+
+typedef bool (*DoCallNativeSetterFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
+static const VMFunction DoCallNativeSetterInfo =
+ FunctionInfo<DoCallNativeSetterFn>(DoCallNativeSetter, "DoNativeCallSetter");
+
+bool
+ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ Label failureUnstow;
+
+ // Guard input is an object.
+ masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+ // Stow R0 and R1 to free up registers.
+ EmitStowICValues(masm, 2);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
+ Register scratch = regs.takeAnyExcluding(ICTailCallReg);
+
+ // Unbox and shape guard.
+ uint32_t framePushed = masm.framePushed();
+ Register objReg = masm.extractObject(R0, ExtractTemp0);
+ GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
+ ICSetProp_CallNative::offsetOfReceiverGuard(), &failureUnstow);
+
+ if (receiver_ != holder_) {
+ Register holderReg = regs.takeAny();
+ masm.loadPtr(Address(ICStubReg, ICSetProp_CallNative::offsetOfHolder()), holderReg);
+ masm.loadPtr(Address(ICStubReg, ICSetProp_CallNative::offsetOfHolderShape()), scratch);
+ masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
+ regs.add(holderReg);
+ }
+
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, scratch);
+
+ // Load callee function and code. To ensure that |code| doesn't end up being
+ // ArgumentsRectifierReg, if it's available we assign it to |callee| instead.
+ Register callee = regs.takeAny();
+ masm.loadPtr(Address(ICStubReg, ICSetProp_CallNative::offsetOfSetter()), callee);
+
+ // To Push R1, read it off of the stowed values on stack.
+ // Stack: [ ..., R0, R1, ..STUBFRAME-HEADER.. ]
+ masm.moveStackPtrTo(scratch);
+ masm.pushValue(Address(scratch, STUB_FRAME_SIZE));
+ masm.push(objReg);
+ masm.push(callee);
+
+ // Don't need to preserve R0 anymore.
+ regs.add(R0);
+
+ if (!callVM(DoCallNativeSetterInfo, masm))
+ return false;
+ leaveStubFrame(masm);
+
+ // Do not care about return value from function. The original RHS should be returned
+ // as the result of this operation.
+ EmitUnstowICValues(masm, 2);
+ masm.moveValue(R1, R0);
+ EmitReturnFromIC(masm);
+
+ // Unstow R0 and R1
+ masm.bind(&failureUnstow);
+ masm.setFramePushed(framePushed);
+ EmitUnstowICValues(masm, 2);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// Call_Fallback
+//
+
+static bool
+TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
+ HandleValue thisv, uint32_t argc, Value* argv, bool* attached)
+{
+ if (argc != 2)
+ return true;
+
+ if (!thisv.isObject() || !thisv.toObject().is<JSFunction>())
+ return true;
+ RootedFunction target(cx, &thisv.toObject().as<JSFunction>());
+
+ bool isScripted = target->hasJITCode();
+
+ // right now, only handle situation where second argument is |arguments|
+ if (argv[1].isMagic(JS_OPTIMIZED_ARGUMENTS) && !script->needsArgsObj()) {
+ if (isScripted && !stub->hasStub(ICStub::Call_ScriptedApplyArguments)) {
+ JitSpew(JitSpew_BaselineIC, " Generating Call_ScriptedApplyArguments stub");
+
+ ICCall_ScriptedApplyArguments::Compiler compiler(
+ cx, stub->fallbackMonitorStub()->firstMonitorStub(), script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+
+ // TODO: handle FUNAPPLY for native targets.
+ }
+
+ if (argv[1].isObject() && argv[1].toObject().is<ArrayObject>()) {
+ if (isScripted && !stub->hasStub(ICStub::Call_ScriptedApplyArray)) {
+ JitSpew(JitSpew_BaselineIC, " Generating Call_ScriptedApplyArray stub");
+
+ ICCall_ScriptedApplyArray::Compiler compiler(
+ cx, stub->fallbackMonitorStub()->firstMonitorStub(), script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+ }
+ }
+ return true;
+}
+
+static bool
+TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
+ HandleValue thisv, bool* attached)
+{
+ // Try to attach a stub for Function.prototype.call with scripted |this|.
+
+ *attached = false;
+ if (!thisv.isObject() || !thisv.toObject().is<JSFunction>())
+ return true;
+ RootedFunction target(cx, &thisv.toObject().as<JSFunction>());
+
+ // Attach a stub if the script can be Baseline-compiled. We do this also
+ // if the script is not yet compiled to avoid attaching a CallNative stub
+ // that handles everything, even after the callee becomes hot.
+ if (target->hasScript() && target->nonLazyScript()->canBaselineCompile() &&
+ !stub->hasStub(ICStub::Call_ScriptedFunCall))
+ {
+ JitSpew(JitSpew_BaselineIC, " Generating Call_ScriptedFunCall stub");
+
+ ICCall_ScriptedFunCall::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ *attached = true;
+ stub->addNewStub(newStub);
+ return true;
+ }
+
+ return true;
+}
+
+// Check if target is a native SIMD operation which returns a SIMD type.
+// If so, set res to a template object matching the SIMD type produced and return true.
+static bool
+GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject res)
+{
+ const JSJitInfo* jitInfo = target->jitInfo();
+ if (!jitInfo || jitInfo->type() != JSJitInfo::InlinableNative)
+ return false;
+
+ // Check if this is a native inlinable SIMD operation.
+ SimdType ctrlType;
+ switch (jitInfo->inlinableNative) {
+ case InlinableNative::SimdInt8x16: ctrlType = SimdType::Int8x16; break;
+ case InlinableNative::SimdUint8x16: ctrlType = SimdType::Uint8x16; break;
+ case InlinableNative::SimdInt16x8: ctrlType = SimdType::Int16x8; break;
+ case InlinableNative::SimdUint16x8: ctrlType = SimdType::Uint16x8; break;
+ case InlinableNative::SimdInt32x4: ctrlType = SimdType::Int32x4; break;
+ case InlinableNative::SimdUint32x4: ctrlType = SimdType::Uint32x4; break;
+ case InlinableNative::SimdFloat32x4: ctrlType = SimdType::Float32x4; break;
+ case InlinableNative::SimdBool8x16: ctrlType = SimdType::Bool8x16; break;
+ case InlinableNative::SimdBool16x8: ctrlType = SimdType::Bool16x8; break;
+ case InlinableNative::SimdBool32x4: ctrlType = SimdType::Bool32x4; break;
+ // This is not an inlinable SIMD operation.
+ default: return false;
+ }
+
+ // The controlling type is not necessarily the return type.
+ // Check the actual operation.
+ SimdOperation simdOp = SimdOperation(jitInfo->nativeOp);
+ SimdType retType;
+
+ switch(simdOp) {
+ case SimdOperation::Fn_allTrue:
+ case SimdOperation::Fn_anyTrue:
+ case SimdOperation::Fn_extractLane:
+ // These operations return a scalar. No template object needed.
+ return false;
+
+ case SimdOperation::Fn_lessThan:
+ case SimdOperation::Fn_lessThanOrEqual:
+ case SimdOperation::Fn_equal:
+ case SimdOperation::Fn_notEqual:
+ case SimdOperation::Fn_greaterThan:
+ case SimdOperation::Fn_greaterThanOrEqual:
+ // These operations return a boolean vector with the same shape as the
+ // controlling type.
+ retType = GetBooleanSimdType(ctrlType);
+ break;
+
+ default:
+ // All other operations return the controlling type.
+ retType = ctrlType;
+ break;
+ }
+
+ // Create a template object based on retType.
+ RootedGlobalObject global(cx, cx->global());
+ Rooted<SimdTypeDescr*> descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, retType));
+ res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr));
+ return true;
+}
+
+static void
+EnsureArrayGroupAnalyzed(JSContext* cx, JSObject* obj)
+{
+ if (PreliminaryObjectArrayWithTemplate* objects = obj->group()->maybePreliminaryObjects())
+ objects->maybeAnalyze(cx, obj->group(), /* forceAnalyze = */ true);
+}
+
+static bool
+GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& args,
+ MutableHandleObject res, bool* skipAttach)
+{
+ Native native = target->native();
+
+ // Check for natives to which template objects can be attached. This is
+ // done to provide templates to Ion for inlining these natives later on.
+
+ if (native == ArrayConstructor || native == array_construct) {
+ // Note: the template array won't be used if its length is inaccurately
+ // computed here. (We allocate here because compilation may occur on a
+ // separate thread where allocation is impossible.)
+ size_t count = 0;
+ if (args.length() != 1)
+ count = args.length();
+ else if (args.length() == 1 && args[0].isInt32() && args[0].toInt32() >= 0)
+ count = args[0].toInt32();
+
+ if (count <= ArrayObject::EagerAllocationMaxLength) {
+ ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
+ if (!group)
+ return false;
+ if (group->maybePreliminaryObjects()) {
+ *skipAttach = true;
+ return true;
+ }
+
+ // With this and other array templates, analyze the group so that
+ // we don't end up with a template whose structure might change later.
+ res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject));
+ if (!res)
+ return false;
+ EnsureArrayGroupAnalyzed(cx, res);
+ return true;
+ }
+ }
+
+ if (args.length() == 1) {
+ size_t len = 0;
+
+ if (args[0].isInt32() && args[0].toInt32() >= 0)
+ len = args[0].toInt32();
+
+ if (!TypedArrayObject::GetTemplateObjectForNative(cx, native, len, res))
+ return false;
+ if (res)
+ return true;
+ }
+
+ if (native == js::array_slice) {
+ if (args.thisv().isObject()) {
+ JSObject* obj = &args.thisv().toObject();
+ if (!obj->isSingleton()) {
+ if (obj->group()->maybePreliminaryObjects()) {
+ *skipAttach = true;
+ return true;
+ }
+ res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
+ TenuredObject));
+ if (!res)
+ return false;
+ EnsureArrayGroupAnalyzed(cx, res);
+ return true;
+ }
+ }
+ }
+
+ if (native == js::intrinsic_StringSplitString && args.length() == 2 && args[0].isString() &&
+ args[1].isString())
+ {
+ ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
+ if (!group)
+ return false;
+ if (group->maybePreliminaryObjects()) {
+ *skipAttach = true;
+ return true;
+ }
+
+ res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject));
+ if (!res)
+ return false;
+ EnsureArrayGroupAnalyzed(cx, res);
+ return true;
+ }
+
+ if (native == StringConstructor) {
+ RootedString emptyString(cx, cx->runtime()->emptyString);
+ res.set(StringObject::create(cx, emptyString, /* proto = */ nullptr, TenuredObject));
+ return !!res;
+ }
+
+ if (native == obj_create && args.length() == 1 && args[0].isObjectOrNull()) {
+ RootedObject proto(cx, args[0].toObjectOrNull());
+ res.set(ObjectCreateImpl(cx, proto, TenuredObject));
+ return !!res;
+ }
+
+ if (JitSupportsSimd() && GetTemplateObjectForSimd(cx, target, res))
+ return !!res;
+
+ return true;
+}
+
+static bool
+GetTemplateObjectForClassHook(JSContext* cx, JSNative hook, CallArgs& args,
+ MutableHandleObject templateObject)
+{
+ if (hook == TypedObject::construct) {
+ Rooted<TypeDescr*> descr(cx, &args.callee().as<TypeDescr>());
+ templateObject.set(TypedObject::createZeroed(cx, descr, 1, gc::TenuredHeap));
+ return !!templateObject;
+ }
+
+ if (hook == SimdTypeDescr::call && JitSupportsSimd()) {
+ Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>());
+ templateObject.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr));
+ return !!templateObject;
+ }
+
+ return true;
+}
+
+static bool
+IsOptimizableCallStringSplit(const Value& callee, int argc, Value* args)
+{
+ if (argc != 2 || !args[0].isString() || !args[1].isString())
+ return false;
+
+ if (!args[0].toString()->isAtom() || !args[1].toString()->isAtom())
+ return false;
+
+ if (!callee.isObject() || !callee.toObject().is<JSFunction>())
+ return false;
+
+ JSFunction& calleeFun = callee.toObject().as<JSFunction>();
+ if (!calleeFun.isNative() || calleeFun.native() != js::intrinsic_StringSplitString)
+ return false;
+
+ return true;
+}
+
+static bool
+TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
+ JSOp op, uint32_t argc, Value* vp, bool constructing, bool isSpread,
+ bool createSingleton, bool* handled)
+{
+ bool isSuper = op == JSOP_SUPERCALL || op == JSOP_SPREADSUPERCALL;
+
+ if (createSingleton || op == JSOP_EVAL || op == JSOP_STRICTEVAL)
+ return true;
+
+ if (stub->numOptimizedStubs() >= ICCall_Fallback::MAX_OPTIMIZED_STUBS) {
+ // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
+ // But for now we just bail.
+ return true;
+ }
+
+ RootedValue callee(cx, vp[0]);
+ RootedValue thisv(cx, vp[1]);
+
+ // Don't attach an optimized call stub if we could potentially attach an
+ // optimized StringSplit stub.
+ if (stub->numOptimizedStubs() == 0 && IsOptimizableCallStringSplit(callee, argc, vp + 2))
+ return true;
+
+ MOZ_ASSERT_IF(stub->hasStub(ICStub::Call_StringSplit), stub->numOptimizedStubs() == 1);
+
+ stub->unlinkStubsWithKind(cx, ICStub::Call_StringSplit);
+
+ if (!callee.isObject())
+ return true;
+
+ RootedObject obj(cx, &callee.toObject());
+ if (!obj->is<JSFunction>()) {
+ // Try to attach a stub for a call/construct hook on the object.
+ // Ignore proxies, which are special cased by callHook/constructHook.
+ if (obj->is<ProxyObject>())
+ return true;
+ if (JSNative hook = constructing ? obj->constructHook() : obj->callHook()) {
+ if (op != JSOP_FUNAPPLY && !isSpread && !createSingleton) {
+ RootedObject templateObject(cx);
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (!GetTemplateObjectForClassHook(cx, hook, args, &templateObject))
+ return false;
+
+ JitSpew(JitSpew_BaselineIC, " Generating Call_ClassHook stub");
+ ICCall_ClassHook::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ obj->getClass(), hook, templateObject,
+ script->pcToOffset(pc), constructing);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *handled = true;
+ return true;
+ }
+ }
+ return true;
+ }
+
+ RootedFunction fun(cx, &obj->as<JSFunction>());
+
+ if (fun->hasScript()) {
+ // Never attach optimized scripted call stubs for JSOP_FUNAPPLY.
+ // MagicArguments may escape the frame through them.
+ if (op == JSOP_FUNAPPLY)
+ return true;
+
+ // If callee is not an interpreted constructor, we have to throw.
+ if (constructing && !fun->isConstructor())
+ return true;
+
+ // Likewise, if the callee is a class constructor, we have to throw.
+ if (!constructing && fun->isClassConstructor())
+ return true;
+
+ if (!fun->hasJITCode()) {
+ // Don't treat this as an unoptimizable case, as we'll add a stub
+ // when the callee becomes hot.
+ *handled = true;
+ return true;
+ }
+
+ // Check if this stub chain has already generalized scripted calls.
+ if (stub->scriptedStubsAreGeneralized()) {
+ JitSpew(JitSpew_BaselineIC, " Chain already has generalized scripted call stub!");
+ return true;
+ }
+
+ if (stub->scriptedStubCount() >= ICCall_Fallback::MAX_SCRIPTED_STUBS) {
+ // Create a Call_AnyScripted stub.
+ JitSpew(JitSpew_BaselineIC, " Generating Call_AnyScripted stub (cons=%s, spread=%s)",
+ constructing ? "yes" : "no", isSpread ? "yes" : "no");
+ ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ constructing, isSpread, script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ // Before adding new stub, unlink all previous Call_Scripted.
+ stub->unlinkStubsWithKind(cx, ICStub::Call_Scripted);
+
+ // Add new generalized stub.
+ stub->addNewStub(newStub);
+ *handled = true;
+ return true;
+ }
+
+ // Keep track of the function's |prototype| property in type
+ // information, for use during Ion compilation.
+ if (IsIonEnabled(cx))
+ EnsureTrackPropertyTypes(cx, fun, NameToId(cx->names().prototype));
+
+ // Remember the template object associated with any script being called
+ // as a constructor, for later use during Ion compilation. This is unsound
+ // for super(), as a single callsite can have multiple possible prototype object
+ // created (via different newTargets)
+ RootedObject templateObject(cx);
+ if (constructing && !isSuper) {
+ // If we are calling a constructor for which the new script
+ // properties analysis has not been performed yet, don't attach a
+ // stub. After the analysis is performed, CreateThisForFunction may
+ // start returning objects with a different type, and the Ion
+ // compiler will get confused.
+
+ // Only attach a stub if the function already has a prototype and
+ // we can look it up without causing side effects.
+ RootedObject newTarget(cx, &vp[2 + argc].toObject());
+ RootedValue protov(cx);
+ if (!GetPropertyPure(cx, newTarget, NameToId(cx->names().prototype), protov.address())) {
+ JitSpew(JitSpew_BaselineIC, " Can't purely lookup function prototype");
+ return true;
+ }
+
+ if (protov.isObject()) {
+ TaggedProto proto(&protov.toObject());
+ ObjectGroup* group = ObjectGroup::defaultNewGroup(cx, nullptr, proto, newTarget);
+ if (!group)
+ return false;
+
+ if (group->newScript() && !group->newScript()->analyzed()) {
+ JitSpew(JitSpew_BaselineIC, " Function newScript has not been analyzed");
+
+ // This is temporary until the analysis is perfomed, so
+ // don't treat this as unoptimizable.
+ *handled = true;
+ return true;
+ }
+ }
+
+ JSObject* thisObject = CreateThisForFunction(cx, fun, newTarget, TenuredObject);
+ if (!thisObject)
+ return false;
+
+ if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
+ templateObject = thisObject;
+ }
+
+ JitSpew(JitSpew_BaselineIC,
+ " Generating Call_Scripted stub (fun=%p, %s:%" PRIuSIZE ", cons=%s, spread=%s)",
+ fun.get(), fun->nonLazyScript()->filename(), fun->nonLazyScript()->lineno(),
+ constructing ? "yes" : "no", isSpread ? "yes" : "no");
+ ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ fun, templateObject,
+ constructing, isSpread, script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *handled = true;
+ return true;
+ }
+
+ if (fun->isNative() && (!constructing || (constructing && fun->isConstructor()))) {
+ // Generalized native call stubs are not here yet!
+ MOZ_ASSERT(!stub->nativeStubsAreGeneralized());
+
+ // Check for JSOP_FUNAPPLY
+ if (op == JSOP_FUNAPPLY) {
+ if (fun->native() == fun_apply)
+ return TryAttachFunApplyStub(cx, stub, script, pc, thisv, argc, vp + 2, handled);
+
+ // Don't try to attach a "regular" optimized call stubs for FUNAPPLY ops,
+ // since MagicArguments may escape through them.
+ return true;
+ }
+
+ if (op == JSOP_FUNCALL && fun->native() == fun_call) {
+ if (!TryAttachFunCallStub(cx, stub, script, pc, thisv, handled))
+ return false;
+ if (*handled)
+ return true;
+ }
+
+ if (stub->nativeStubCount() >= ICCall_Fallback::MAX_NATIVE_STUBS) {
+ JitSpew(JitSpew_BaselineIC,
+ " Too many Call_Native stubs. TODO: add Call_AnyNative!");
+ return true;
+ }
+
+ if (fun->native() == intrinsic_IsSuspendedStarGenerator) {
+ // This intrinsic only appears in self-hosted code.
+ MOZ_ASSERT(op != JSOP_NEW);
+ MOZ_ASSERT(argc == 1);
+ JitSpew(JitSpew_BaselineIC, " Generating Call_IsSuspendedStarGenerator stub");
+
+ ICCall_IsSuspendedStarGenerator::Compiler compiler(cx);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *handled = true;
+ return true;
+ }
+
+ RootedObject templateObject(cx);
+ if (MOZ_LIKELY(!isSpread && !isSuper)) {
+ bool skipAttach = false;
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (!GetTemplateObjectForNative(cx, fun, args, &templateObject, &skipAttach))
+ return false;
+ if (skipAttach) {
+ *handled = true;
+ return true;
+ }
+ MOZ_ASSERT_IF(templateObject, !templateObject->group()->maybePreliminaryObjects());
+ }
+
+ JitSpew(JitSpew_BaselineIC, " Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",
+ fun.get(), constructing ? "yes" : "no", isSpread ? "yes" : "no");
+ ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ fun, templateObject, constructing, isSpread,
+ script->pcToOffset(pc));
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *handled = true;
+ return true;
+ }
+
+ return true;
+}
+
+static bool
+CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result)
+{
+ uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
+ JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject);
+ if (!nobj)
+ return false;
+ EnsureArrayGroupAnalyzed(cx, nobj);
+ CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
+
+ result.setObject(*nobj);
+ return true;
+}
+
+static bool
+TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
+ uint32_t argc, HandleValue callee, Value* vp, jsbytecode* pc,
+ HandleValue res, bool* attached)
+{
+ if (stub->numOptimizedStubs() != 0)
+ return true;
+
+ Value* args = vp + 2;
+
+ // String.prototype.split will not yield a constructable.
+ if (JSOp(*pc) == JSOP_NEW)
+ return true;
+
+ if (!IsOptimizableCallStringSplit(callee, argc, args))
+ return true;
+
+ MOZ_ASSERT(callee.isObject());
+ MOZ_ASSERT(callee.toObject().is<JSFunction>());
+
+ RootedString str(cx, args[0].toString());
+ RootedString sep(cx, args[1].toString());
+ RootedObject obj(cx, &res.toObject());
+ RootedValue arr(cx);
+
+ // Copy the array before storing in stub.
+ if (!CopyArray(cx, obj, &arr))
+ return false;
+
+ // Atomize all elements of the array.
+ RootedObject arrObj(cx, &arr.toObject());
+ uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj);
+ for (uint32_t i = 0; i < initLength; i++) {
+ JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString());
+ if (!str)
+ return false;
+
+ if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) {
+ // The value could not be stored to an unboxed dense element.
+ return true;
+ }
+ }
+
+ ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+ script->pcToOffset(pc), str, sep,
+ arr);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+}
+
+static bool
+DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint32_t argc,
+ Value* vp, MutableHandleValue res)
+{
+ SharedStubInfo info(cx, frame, stub_->icEntry());
+
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_);
+
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = stub->icEntry()->pc(script);
+ JSOp op = JSOp(*pc);
+ FallbackICSpew(cx, stub, "Call(%s)", CodeName[op]);
+
+ MOZ_ASSERT(argc == GET_ARGC(pc));
+ bool constructing = (op == JSOP_NEW);
+
+ // Ensure vp array is rooted - we may GC in here.
+ size_t numValues = argc + 2 + constructing;
+ AutoArrayRooter vpRoot(cx, numValues, vp);
+
+ CallArgs callArgs = CallArgsFromSp(argc + constructing, vp + numValues, constructing);
+ RootedValue callee(cx, vp[0]);
+
+ // Handle funapply with JSOP_ARGUMENTS
+ if (op == JSOP_FUNAPPLY && argc == 2 && callArgs[1].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
+ if (!GuardFunApplyArgumentsOptimization(cx, frame, callArgs))
+ return false;
+ }
+
+ bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc);
+
+ // Try attaching a call stub.
+ bool handled = false;
+ if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false,
+ createSingleton, &handled))
+ {
+ return false;
+ }
+
+ if (op == JSOP_NEW) {
+ if (!ConstructFromStack(cx, callArgs))
+ return false;
+ res.set(callArgs.rval());
+ } else if ((op == JSOP_EVAL || op == JSOP_STRICTEVAL) &&
+ frame->environmentChain()->global().valueIsEval(callee))
+ {
+ if (!DirectEval(cx, callArgs.get(0), res))
+ return false;
+ } else {
+ MOZ_ASSERT(op == JSOP_CALL ||
+ op == JSOP_CALLITER ||
+ op == JSOP_FUNCALL ||
+ op == JSOP_FUNAPPLY ||
+ op == JSOP_EVAL ||
+ op == JSOP_STRICTEVAL);
+ if (op == JSOP_CALLITER && callee.isPrimitive()) {
+ MOZ_ASSERT(argc == 0, "thisv must be on top of the stack");
+ ReportValueError(cx, JSMSG_NOT_ITERABLE, -1, callArgs.thisv(), nullptr);
+ return false;
+ }
+
+ if (!CallFromStack(cx, callArgs))
+ return false;
+
+ res.set(callArgs.rval());
+ }
+
+ TypeScript::Monitor(cx, script, pc, res);
+
+ // Check if debug mode toggling made the stub invalid.
+ if (stub.invalid())
+ return true;
+
+ // Attach a new TypeMonitor stub for this value.
+ ICTypeMonitor_Fallback* typeMonFbStub = stub->fallbackMonitorStub();
+ if (!typeMonFbStub->addMonitorStubForValue(cx, &info, res))
+ {
+ return false;
+ }
+
+ // Add a type monitor stub for the resulting value.
+ if (!stub->addMonitorStubForValue(cx, &info, res))
+ return false;
+
+ // If 'callee' is a potential Call_StringSplit, try to attach an
+ // optimized StringSplit stub. Note that vp[0] now holds the return value
+ // instead of the callee, so we pass the callee as well.
+ if (!TryAttachStringSplit(cx, stub, script, argc, callee, vp, pc, res, &handled))
+ return false;
+
+ if (!handled)
+ stub->noteUnoptimizableCall();
+ return true;
+}
+
+static bool
+DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, Value* vp,
+ MutableHandleValue res)
+{
+ SharedStubInfo info(cx, frame, stub_->icEntry());
+
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_);
+
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = stub->icEntry()->pc(script);
+ JSOp op = JSOp(*pc);
+ bool constructing = (op == JSOP_SPREADNEW);
+ FallbackICSpew(cx, stub, "SpreadCall(%s)", CodeName[op]);
+
+ // Ensure vp array is rooted - we may GC in here.
+ AutoArrayRooter vpRoot(cx, 3 + constructing, vp);
+
+ RootedValue callee(cx, vp[0]);
+ RootedValue thisv(cx, vp[1]);
+ RootedValue arr(cx, vp[2]);
+ RootedValue newTarget(cx, constructing ? vp[3] : NullValue());
+
+ // Try attaching a call stub.
+ bool handled = false;
+ if (op != JSOP_SPREADEVAL && op != JSOP_STRICTSPREADEVAL &&
+ !TryAttachCallStub(cx, stub, script, pc, op, 1, vp, constructing, true, false,
+ &handled))
+ {
+ return false;
+ }
+
+ if (!SpreadCallOperation(cx, script, pc, thisv, callee, arr, newTarget, res))
+ return false;
+
+ // Check if debug mode toggling made the stub invalid.
+ if (stub.invalid())
+ return true;
+
+ // Attach a new TypeMonitor stub for this value.
+ ICTypeMonitor_Fallback* typeMonFbStub = stub->fallbackMonitorStub();
+ if (!typeMonFbStub->addMonitorStubForValue(cx, &info, res))
+ {
+ return false;
+ }
+
+ // Add a type monitor stub for the resulting value.
+ if (!stub->addMonitorStubForValue(cx, &info, res))
+ return false;
+
+ if (!handled)
+ stub->noteUnoptimizableCall();
+ return true;
+}
+
+void
+ICCallStubCompiler::pushCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
+ Register argcReg, bool isJitCall, bool isConstructing)
+{
+ MOZ_ASSERT(!regs.has(argcReg));
+
+ // Account for new.target
+ Register count = regs.takeAny();
+
+ masm.move32(argcReg, count);
+
+ // If we are setting up for a jitcall, we have to align the stack taking
+ // into account the args and newTarget. We could also count callee and |this|,
+ // but it's a waste of stack space. Because we want to keep argcReg unchanged,
+ // just account for newTarget initially, and add the other 2 after assuring
+ // allignment.
+ if (isJitCall) {
+ if (isConstructing)
+ masm.add32(Imm32(1), count);
+ } else {
+ masm.add32(Imm32(2 + isConstructing), count);
+ }
+
+ // argPtr initially points to the last argument.
+ Register argPtr = regs.takeAny();
+ masm.moveStackPtrTo(argPtr);
+
+ // Skip 4 pointers pushed on top of the arguments: the frame descriptor,
+ // return address, old frame pointer and stub reg.
+ masm.addPtr(Imm32(STUB_FRAME_SIZE), argPtr);
+
+ // Align the stack such that the JitFrameLayout is aligned on the
+ // JitStackAlignment.
+ if (isJitCall) {
+ masm.alignJitStackBasedOnNArgs(count);
+
+ // Account for callee and |this|, skipped earlier
+ masm.add32(Imm32(2), count);
+ }
+
+ // Push all values, starting at the last one.
+ Label loop, done;
+ masm.bind(&loop);
+ masm.branchTest32(Assembler::Zero, count, count, &done);
+ {
+ masm.pushValue(Address(argPtr, 0));
+ masm.addPtr(Imm32(sizeof(Value)), argPtr);
+
+ masm.sub32(Imm32(1), count);
+ masm.jump(&loop);
+ }
+ masm.bind(&done);
+}
+
+void
+ICCallStubCompiler::guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure,
+ bool isConstructing)
+{
+ masm.unboxObject(Address(masm.getStackPointer(),
+ isConstructing * sizeof(Value) + ICStackValueOffset), argcReg);
+ masm.loadPtr(Address(argcReg, NativeObject::offsetOfElements()), argcReg);
+ masm.load32(Address(argcReg, ObjectElements::offsetOfLength()), argcReg);
+
+ // Limit actual argc to something reasonable (huge number of arguments can
+ // blow the stack limit).
+ static_assert(ICCall_Scripted::MAX_ARGS_SPREAD_LENGTH <= ARGS_LENGTH_MAX,
+ "maximum arguments length for optimized stub should be <= ARGS_LENGTH_MAX");
+ masm.branch32(Assembler::Above, argcReg, Imm32(ICCall_Scripted::MAX_ARGS_SPREAD_LENGTH),
+ failure);
+}
+
+void
+ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler& masm,
+ AllocatableGeneralRegisterSet regs,
+ Register argcReg, bool isJitCall,
+ bool isConstructing)
+{
+ // Pull the array off the stack before aligning.
+ Register startReg = regs.takeAny();
+ masm.unboxObject(Address(masm.getStackPointer(),
+ (isConstructing * sizeof(Value)) + STUB_FRAME_SIZE), startReg);
+ masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
+
+ // Align the stack such that the JitFrameLayout is aligned on the
+ // JitStackAlignment.
+ if (isJitCall) {
+ Register alignReg = argcReg;
+ if (isConstructing) {
+ alignReg = regs.takeAny();
+ masm.movePtr(argcReg, alignReg);
+ masm.addPtr(Imm32(1), alignReg);
+ }
+ masm.alignJitStackBasedOnNArgs(alignReg);
+ if (isConstructing) {
+ MOZ_ASSERT(alignReg != argcReg);
+ regs.add(alignReg);
+ }
+ }
+
+ // Push newTarget, if necessary
+ if (isConstructing)
+ masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
+
+ // Push arguments: set up endReg to point to &array[argc]
+ Register endReg = regs.takeAny();
+ masm.movePtr(argcReg, endReg);
+ static_assert(sizeof(Value) == 8, "Value must be 8 bytes");
+ masm.lshiftPtr(Imm32(3), endReg);
+ masm.addPtr(startReg, endReg);
+
+ // Copying pre-decrements endReg by 8 until startReg is reached
+ Label copyDone;
+ Label copyStart;
+ masm.bind(&copyStart);
+ masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
+ masm.subPtr(Imm32(sizeof(Value)), endReg);
+ masm.pushValue(Address(endReg, 0));
+ masm.jump(&copyStart);
+ masm.bind(&copyDone);
+
+ regs.add(startReg);
+ regs.add(endReg);
+
+ // Push the callee and |this|.
+ masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + (1 + isConstructing) * sizeof(Value)));
+ masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + (2 + isConstructing) * sizeof(Value)));
+}
+
+Register
+ICCallStubCompiler::guardFunApply(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
+ Register argcReg, bool checkNative, FunApplyThing applyThing,
+ Label* failure)
+{
+ // Ensure argc == 2
+ masm.branch32(Assembler::NotEqual, argcReg, Imm32(2), failure);
+
+ // Stack looks like:
+ // [..., CalleeV, ThisV, Arg0V, Arg1V <MaybeReturnReg>]
+
+ Address secondArgSlot(masm.getStackPointer(), ICStackValueOffset);
+ if (applyThing == FunApply_MagicArgs) {
+ // Ensure that the second arg is magic arguments.
+ masm.branchTestMagic(Assembler::NotEqual, secondArgSlot, failure);
+
+ // Ensure that this frame doesn't have an arguments object.
+ masm.branchTest32(Assembler::NonZero,
+ Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
+ Imm32(BaselineFrame::HAS_ARGS_OBJ),
+ failure);
+
+ // Limit the length to something reasonable.
+ masm.branch32(Assembler::Above,
+ Address(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs()),
+ Imm32(ICCall_ScriptedApplyArray::MAX_ARGS_ARRAY_LENGTH),
+ failure);
+ } else {
+ MOZ_ASSERT(applyThing == FunApply_Array);
+
+ AllocatableGeneralRegisterSet regsx = regs;
+
+ // Ensure that the second arg is an array.
+ ValueOperand secondArgVal = regsx.takeAnyValue();
+ masm.loadValue(secondArgSlot, secondArgVal);
+
+ masm.branchTestObject(Assembler::NotEqual, secondArgVal, failure);
+ Register secondArgObj = masm.extractObject(secondArgVal, ExtractTemp1);
+
+ regsx.add(secondArgVal);
+ regsx.takeUnchecked(secondArgObj);
+
+ masm.branchTestObjClass(Assembler::NotEqual, secondArgObj, regsx.getAny(),
+ &ArrayObject::class_, failure);
+
+ // Get the array elements and ensure that initializedLength == length
+ masm.loadPtr(Address(secondArgObj, NativeObject::offsetOfElements()), secondArgObj);
+
+ Register lenReg = regsx.takeAny();
+ masm.load32(Address(secondArgObj, ObjectElements::offsetOfLength()), lenReg);
+
+ masm.branch32(Assembler::NotEqual,
+ Address(secondArgObj, ObjectElements::offsetOfInitializedLength()),
+ lenReg, failure);
+
+ // Limit the length to something reasonable (huge number of arguments can
+ // blow the stack limit).
+ masm.branch32(Assembler::Above, lenReg,
+ Imm32(ICCall_ScriptedApplyArray::MAX_ARGS_ARRAY_LENGTH),
+ failure);
+
+ // Ensure no holes. Loop through values in array and make sure none are magic.
+ // Start address is secondArgObj, end address is secondArgObj + (lenReg * sizeof(Value))
+ JS_STATIC_ASSERT(sizeof(Value) == 8);
+ masm.lshiftPtr(Imm32(3), lenReg);
+ masm.addPtr(secondArgObj, lenReg);
+
+ Register start = secondArgObj;
+ Register end = lenReg;
+ Label loop;
+ Label endLoop;
+ masm.bind(&loop);
+ masm.branchPtr(Assembler::AboveOrEqual, start, end, &endLoop);
+ masm.branchTestMagic(Assembler::Equal, Address(start, 0), failure);
+ masm.addPtr(Imm32(sizeof(Value)), start);
+ masm.jump(&loop);
+ masm.bind(&endLoop);
+ }
+
+ // Stack now confirmed to be like:
+ // [..., CalleeV, ThisV, Arg0V, MagicValue(Arguments), <MaybeReturnAddr>]
+
+ // Load the callee, ensure that it's fun_apply
+ ValueOperand val = regs.takeAnyValue();
+ Address calleeSlot(masm.getStackPointer(), ICStackValueOffset + (3 * sizeof(Value)));
+ masm.loadValue(calleeSlot, val);
+
+ masm.branchTestObject(Assembler::NotEqual, val, failure);
+ Register callee = masm.extractObject(val, ExtractTemp1);
+
+ masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
+ failure);
+ masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+
+ masm.branchPtr(Assembler::NotEqual, callee, ImmPtr(fun_apply), failure);
+
+ // Load the |thisv|, ensure that it's a scripted function with a valid baseline or ion
+ // script, or a native function.
+ Address thisSlot(masm.getStackPointer(), ICStackValueOffset + (2 * sizeof(Value)));
+ masm.loadValue(thisSlot, val);
+
+ masm.branchTestObject(Assembler::NotEqual, val, failure);
+ Register target = masm.extractObject(val, ExtractTemp1);
+ regs.add(val);
+ regs.takeUnchecked(target);
+
+ masm.branchTestObjClass(Assembler::NotEqual, target, regs.getAny(), &JSFunction::class_,
+ failure);
+
+ if (checkNative) {
+ masm.branchIfInterpreted(target, failure);
+ } else {
+ masm.branchIfFunctionHasNoScript(target, failure);
+ Register temp = regs.takeAny();
+ masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), temp);
+ masm.loadBaselineOrIonRaw(temp, temp, failure);
+ regs.add(temp);
+ }
+ return target;
+}
+
+void
+ICCallStubCompiler::pushCallerArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs)
+{
+ // Initialize copyReg to point to start caller arguments vector.
+ // Initialize argcReg to poitn to the end of it.
+ Register startReg = regs.takeAny();
+ Register endReg = regs.takeAny();
+ masm.loadPtr(Address(BaselineFrameReg, 0), startReg);
+ masm.loadPtr(Address(startReg, BaselineFrame::offsetOfNumActualArgs()), endReg);
+ masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), startReg);
+ masm.alignJitStackBasedOnNArgs(endReg);
+ masm.lshiftPtr(Imm32(ValueShift), endReg);
+ masm.addPtr(startReg, endReg);
+
+ // Copying pre-decrements endReg by 8 until startReg is reached
+ Label copyDone;
+ Label copyStart;
+ masm.bind(&copyStart);
+ masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
+ masm.subPtr(Imm32(sizeof(Value)), endReg);
+ masm.pushValue(Address(endReg, 0));
+ masm.jump(&copyStart);
+ masm.bind(&copyDone);
+}
+
+void
+ICCallStubCompiler::pushArrayArguments(MacroAssembler& masm, Address arrayVal,
+ AllocatableGeneralRegisterSet regs)
+{
+ // Load start and end address of values to copy.
+ // guardFunApply has already gauranteed that the array is packed and contains
+ // no holes.
+ Register startReg = regs.takeAny();
+ Register endReg = regs.takeAny();
+ masm.extractObject(arrayVal, startReg);
+ masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
+ masm.load32(Address(startReg, ObjectElements::offsetOfInitializedLength()), endReg);
+ masm.alignJitStackBasedOnNArgs(endReg);
+ masm.lshiftPtr(Imm32(ValueShift), endReg);
+ masm.addPtr(startReg, endReg);
+
+ // Copying pre-decrements endReg by 8 until startReg is reached
+ Label copyDone;
+ Label copyStart;
+ masm.bind(&copyStart);
+ masm.branchPtr(Assembler::Equal, endReg, startReg, &copyDone);
+ masm.subPtr(Imm32(sizeof(Value)), endReg);
+ masm.pushValue(Address(endReg, 0));
+ masm.jump(&copyStart);
+ masm.bind(&copyDone);
+}
+
+typedef bool (*DoCallFallbackFn)(JSContext*, BaselineFrame*, ICCall_Fallback*,
+ uint32_t, Value*, MutableHandleValue);
+static const VMFunction DoCallFallbackInfo =
+ FunctionInfo<DoCallFallbackFn>(DoCallFallback, "DoCallFallback");
+
+typedef bool (*DoSpreadCallFallbackFn)(JSContext*, BaselineFrame*, ICCall_Fallback*,
+ Value*, MutableHandleValue);
+static const VMFunction DoSpreadCallFallbackInfo =
+ FunctionInfo<DoSpreadCallFallbackFn>(DoSpreadCallFallback, "DoSpreadCallFallback");
+
+bool
+ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ MOZ_ASSERT(R0 == JSReturnOperand);
+
+ // Values are on the stack left-to-right. Calling convention wants them
+ // right-to-left so duplicate them on the stack in reverse order.
+ // |this| and callee are pushed last.
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+
+ if (MOZ_UNLIKELY(isSpread_)) {
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, R1.scratchReg());
+
+ // Use BaselineFrameReg instead of BaselineStackReg, because
+ // BaselineFrameReg and BaselineStackReg hold the same value just after
+ // calling enterStubFrame.
+
+ // newTarget
+ if (isConstructing_)
+ masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
+
+ // array
+ uint32_t valueOffset = isConstructing_;
+ masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
+
+ // this
+ masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
+
+ // callee
+ masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
+
+ masm.push(masm.getStackPointer());
+ masm.push(ICStubReg);
+
+ PushStubPayload(masm, R0.scratchReg());
+
+ if (!callVM(DoSpreadCallFallbackInfo, masm))
+ return false;
+
+ leaveStubFrame(masm);
+ EmitReturnFromIC(masm);
+
+ // SPREADCALL is not yet supported in Ion, so do not generate asmcode for
+ // bailout.
+ return true;
+ }
+
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, R1.scratchReg());
+
+ regs.take(R0.scratchReg()); // argc.
+
+ pushCallArguments(masm, regs, R0.scratchReg(), /* isJitCall = */ false, isConstructing_);
+
+ masm.push(masm.getStackPointer());
+ masm.push(R0.scratchReg());
+ masm.push(ICStubReg);
+
+ PushStubPayload(masm, R0.scratchReg());
+
+ if (!callVM(DoCallFallbackInfo, masm))
+ return false;
+
+ uint32_t framePushed = masm.framePushed();
+ leaveStubFrame(masm);
+ EmitReturnFromIC(masm);
+
+ // The following asmcode is only used when an Ion inlined frame bails out
+ // into into baseline jitcode. The return address pushed onto the
+ // reconstructed baseline stack points here.
+ returnOffset_ = masm.currentOffset();
+
+ // Here we are again in a stub frame. Marking as so.
+ inStubFrame_ = true;
+ masm.setFramePushed(framePushed);
+
+ // Load passed-in ThisV into R1 just in case it's needed. Need to do this before
+ // we leave the stub frame since that info will be lost.
+ // Current stack: [...., ThisV, ActualArgc, CalleeToken, Descriptor ]
+ masm.loadValue(Address(masm.getStackPointer(), 3 * sizeof(size_t)), R1);
+
+ leaveStubFrame(masm, true);
+
+ // If this is a |constructing| call, if the callee returns a non-object, we replace it with
+ // the |this| object passed in.
+ if (isConstructing_) {
+ MOZ_ASSERT(JSReturnOperand == R0);
+ Label skipThisReplace;
+
+ masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
+ masm.moveValue(R1, R0);
+#ifdef DEBUG
+ masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
+ masm.assumeUnreachable("Failed to return object in constructing call.");
+#endif
+ masm.bind(&skipThisReplace);
+ }
+
+ // At this point, ICStubReg points to the ICCall_Fallback stub, which is NOT
+ // a MonitoredStub, but rather a MonitoredFallbackStub. To use EmitEnterTypeMonitorIC,
+ // first load the ICTypeMonitor_Fallback stub into ICStubReg. Then, use
+ // EmitEnterTypeMonitorIC with a custom struct offset.
+ masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
+ ICStubReg);
+ EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
+
+ return true;
+}
+
+void
+ICCall_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<JitCode*> code)
+{
+ if (MOZ_UNLIKELY(isSpread_))
+ return;
+
+ cx->compartment()->jitCompartment()->initBaselineCallReturnAddr(code->raw() + returnOffset_,
+ isConstructing_);
+}
+
+typedef bool (*CreateThisFn)(JSContext* cx, HandleObject callee, HandleObject newTarget,
+ MutableHandleValue rval);
+static const VMFunction CreateThisInfoBaseline =
+ FunctionInfo<CreateThisFn>(CreateThis, "CreateThis");
+
+bool
+ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+ bool canUseTailCallReg = regs.has(ICTailCallReg);
+
+ Register argcReg = R0.scratchReg();
+ MOZ_ASSERT(argcReg != ArgumentsRectifierReg);
+
+ regs.take(argcReg);
+ regs.take(ArgumentsRectifierReg);
+ regs.takeUnchecked(ICTailCallReg);
+
+ if (isSpread_)
+ guardSpreadCall(masm, argcReg, &failure, isConstructing_);
+
+ // Load the callee in R1, accounting for newTarget, if necessary
+ // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, [newTarget] +ICStackValueOffset+ ]
+ if (isSpread_) {
+ unsigned skipToCallee = (2 + isConstructing_) * sizeof(Value);
+ masm.loadValue(Address(masm.getStackPointer(), skipToCallee + ICStackValueOffset), R1);
+ } else {
+ // Account for newTarget, if necessary
+ unsigned nonArgsSkip = (1 + isConstructing_) * sizeof(Value);
+ BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + nonArgsSkip);
+ masm.loadValue(calleeSlot, R1);
+ }
+ regs.take(R1);
+
+ // Ensure callee is an object.
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+
+ // Ensure callee is a function.
+ Register callee = masm.extractObject(R1, ExtractTemp0);
+
+ // If calling a specific script, check if the script matches. Otherwise, ensure that
+ // callee function is scripted. Leave calleeScript in |callee| reg.
+ if (callee_) {
+ MOZ_ASSERT(kind == ICStub::Call_Scripted);
+
+ // Check if the object matches this callee.
+ Address expectedCallee(ICStubReg, ICCall_Scripted::offsetOfCallee());
+ masm.branchPtr(Assembler::NotEqual, expectedCallee, callee, &failure);
+
+ // Guard against relazification.
+ masm.branchIfFunctionHasNoScript(callee, &failure);
+ } else {
+ // Ensure the object is a function.
+ masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
+ &failure);
+ if (isConstructing_) {
+ masm.branchIfNotInterpretedConstructor(callee, regs.getAny(), &failure);
+ } else {
+ masm.branchIfFunctionHasNoScript(callee, &failure);
+ masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, callee,
+ regs.getAny(), &failure);
+ }
+ }
+
+ // Load the JSScript.
+ masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+
+ // Load the start of the target JitCode.
+ Register code;
+ if (!isConstructing_) {
+ code = regs.takeAny();
+ masm.loadBaselineOrIonRaw(callee, code, &failure);
+ } else {
+ Address scriptCode(callee, JSScript::offsetOfBaselineOrIonRaw());
+ masm.branchPtr(Assembler::Equal, scriptCode, ImmPtr(nullptr), &failure);
+ }
+
+ // We no longer need R1.
+ regs.add(R1);
+
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, regs.getAny());
+ if (canUseTailCallReg)
+ regs.add(ICTailCallReg);
+
+ Label failureLeaveStubFrame;
+
+ if (isConstructing_) {
+ // Save argc before call.
+ masm.push(argcReg);
+
+ // Stack now looks like:
+ // [..., Callee, ThisV, Arg0V, ..., ArgNV, NewTarget, StubFrameHeader, ArgC ]
+ masm.loadValue(Address(masm.getStackPointer(), STUB_FRAME_SIZE + sizeof(size_t)), R1);
+ masm.push(masm.extractObject(R1, ExtractTemp0));
+
+ if (isSpread_) {
+ masm.loadValue(Address(masm.getStackPointer(),
+ 3 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t) +
+ sizeof(JSObject*)),
+ R1);
+ } else {
+ BaseValueIndex calleeSlot2(masm.getStackPointer(), argcReg,
+ 2 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t) +
+ sizeof(JSObject*));
+ masm.loadValue(calleeSlot2, R1);
+ }
+ masm.push(masm.extractObject(R1, ExtractTemp0));
+ if (!callVM(CreateThisInfoBaseline, masm))
+ return false;
+
+ // Return of CreateThis must be an object or uninitialized.
+#ifdef DEBUG
+ Label createdThisOK;
+ masm.branchTestObject(Assembler::Equal, JSReturnOperand, &createdThisOK);
+ masm.branchTestMagic(Assembler::Equal, JSReturnOperand, &createdThisOK);
+ masm.assumeUnreachable("The return of CreateThis must be an object or uninitialized.");
+ masm.bind(&createdThisOK);
+#endif
+
+ // Reset the register set from here on in.
+ MOZ_ASSERT(JSReturnOperand == R0);
+ regs = availableGeneralRegs(0);
+ regs.take(R0);
+ regs.take(ArgumentsRectifierReg);
+ argcReg = regs.takeAny();
+
+ // Restore saved argc so we can use it to calculate the address to save
+ // the resulting this object to.
+ masm.pop(argcReg);
+
+ // Save "this" value back into pushed arguments on stack. R0 can be clobbered after that.
+ // Stack now looks like:
+ // [..., Callee, ThisV, Arg0V, ..., ArgNV, [NewTarget], StubFrameHeader ]
+ if (isSpread_) {
+ masm.storeValue(R0, Address(masm.getStackPointer(),
+ (1 + isConstructing_) * sizeof(Value) + STUB_FRAME_SIZE));
+ } else {
+ BaseValueIndex thisSlot(masm.getStackPointer(), argcReg,
+ STUB_FRAME_SIZE + isConstructing_ * sizeof(Value));
+ masm.storeValue(R0, thisSlot);
+ }
+
+ // Restore the stub register from the baseline stub frame.
+ masm.loadPtr(Address(masm.getStackPointer(), STUB_FRAME_SAVED_STUB_OFFSET), ICStubReg);
+
+ // Reload callee script. Note that a GC triggered by CreateThis may
+ // have destroyed the callee BaselineScript and IonScript. CreateThis is
+ // safely repeatable though, so in this case we just leave the stub frame
+ // and jump to the next stub.
+
+ // Just need to load the script now.
+ if (isSpread_) {
+ unsigned skipForCallee = (2 + isConstructing_) * sizeof(Value);
+ masm.loadValue(Address(masm.getStackPointer(), skipForCallee + STUB_FRAME_SIZE), R0);
+ } else {
+ // Account for newTarget, if necessary
+ unsigned nonArgsSkip = (1 + isConstructing_) * sizeof(Value);
+ BaseValueIndex calleeSlot3(masm.getStackPointer(), argcReg, nonArgsSkip + STUB_FRAME_SIZE);
+ masm.loadValue(calleeSlot3, R0);
+ }
+ callee = masm.extractObject(R0, ExtractTemp0);
+ regs.add(R0);
+ regs.takeUnchecked(callee);
+ masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+
+ code = regs.takeAny();
+ masm.loadBaselineOrIonRaw(callee, code, &failureLeaveStubFrame);
+
+ // Release callee register, but don't add ExtractTemp0 back into the pool
+ // ExtractTemp0 is used later, and if it's allocated to some other register at that
+ // point, it will get clobbered when used.
+ if (callee != ExtractTemp0)
+ regs.add(callee);
+
+ if (canUseTailCallReg)
+ regs.addUnchecked(ICTailCallReg);
+ }
+ Register scratch = regs.takeAny();
+
+ // Values are on the stack left-to-right. Calling convention wants them
+ // right-to-left so duplicate them on the stack in reverse order.
+ // |this| and callee are pushed last.
+ if (isSpread_)
+ pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ true, isConstructing_);
+ else
+ pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true, isConstructing_);
+
+ // The callee is on top of the stack. Pop and unbox it.
+ ValueOperand val = regs.takeAnyValue();
+ masm.popValue(val);
+ callee = masm.extractObject(val, ExtractTemp0);
+
+ EmitBaselineCreateStubFrameDescriptor(masm, scratch, JitFrameLayout::Size());
+
+ // Note that we use Push, not push, so that callJit will align the stack
+ // properly on ARM.
+ masm.Push(argcReg);
+ masm.PushCalleeToken(callee, isConstructing_);
+ masm.Push(scratch);
+
+ // Handle arguments underflow.
+ Label noUnderflow;
+ masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), callee);
+ masm.branch32(Assembler::AboveOrEqual, argcReg, callee, &noUnderflow);
+ {
+ // Call the arguments rectifier.
+ MOZ_ASSERT(ArgumentsRectifierReg != code);
+ MOZ_ASSERT(ArgumentsRectifierReg != argcReg);
+
+ JitCode* argumentsRectifier =
+ cx->runtime()->jitRuntime()->getArgumentsRectifier();
+
+ masm.movePtr(ImmGCPtr(argumentsRectifier), code);
+ masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
+ masm.movePtr(argcReg, ArgumentsRectifierReg);
+ }
+
+ masm.bind(&noUnderflow);
+ masm.callJit(code);
+
+ // If this is a constructing call, and the callee returns a non-object, replace it with
+ // the |this| object passed in.
+ if (isConstructing_) {
+ Label skipThisReplace;
+ masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
+
+ // Current stack: [ Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
+ // However, we can't use this ThisVal, because it hasn't been traced. We need to use
+ // The ThisVal higher up the stack:
+ // Current stack: [ ThisVal, ARGVALS..., ...STUB FRAME...,
+ // Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
+
+ // Restore the BaselineFrameReg based on the frame descriptor.
+ //
+ // BaselineFrameReg = BaselineStackReg
+ // + sizeof(Descriptor) + sizeof(Callee) + sizeof(ActualArgc)
+ // + stubFrameSize(Descriptor)
+ // - sizeof(ICStubReg) - sizeof(BaselineFrameReg)
+ Address descriptorAddr(masm.getStackPointer(), 0);
+ masm.loadPtr(descriptorAddr, BaselineFrameReg);
+ masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), BaselineFrameReg);
+ masm.addPtr(Imm32((3 - 2) * sizeof(size_t)), BaselineFrameReg);
+ masm.addStackPtrTo(BaselineFrameReg);
+
+ // Load the number of arguments present before the stub frame.
+ Register argcReg = JSReturnOperand.scratchReg();
+ if (isSpread_) {
+ // Account for the Array object.
+ masm.move32(Imm32(1), argcReg);
+ } else {
+ Address argcAddr(masm.getStackPointer(), 2 * sizeof(size_t));
+ masm.loadPtr(argcAddr, argcReg);
+ }
+
+ // Current stack: [ ThisVal, ARGVALS..., ...STUB FRAME..., <-- BaselineFrameReg
+ // Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
+ //
+ // &ThisVal = BaselineFrameReg + argc * sizeof(Value) + STUB_FRAME_SIZE + sizeof(Value)
+ // This last sizeof(Value) accounts for the newTarget on the end of the arguments vector
+ // which is not reflected in actualArgc
+ BaseValueIndex thisSlotAddr(BaselineFrameReg, argcReg, STUB_FRAME_SIZE + sizeof(Value));
+ masm.loadValue(thisSlotAddr, JSReturnOperand);
+#ifdef DEBUG
+ masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
+ masm.assumeUnreachable("Return of constructing call should be an object.");
+#endif
+ masm.bind(&skipThisReplace);
+ }
+
+ leaveStubFrame(masm, true);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ // Leave stub frame and restore argc for the next stub.
+ masm.bind(&failureLeaveStubFrame);
+ inStubFrame_ = true;
+ leaveStubFrame(masm, false);
+ if (argcReg != R0.scratchReg())
+ masm.movePtr(argcReg, R0.scratchReg());
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue);
+static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray, "CopyArray");
+
+bool
+ICCall_StringSplit::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ // Stack Layout: [ ..., CalleeVal, ThisVal, strVal, sepVal, +ICStackValueOffset+ ]
+ static const size_t SEP_DEPTH = 0;
+ static const size_t STR_DEPTH = sizeof(Value);
+ static const size_t CALLEE_DEPTH = 3 * sizeof(Value);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+ Label failureRestoreArgc;
+#ifdef DEBUG
+ Label twoArg;
+ Register argcReg = R0.scratchReg();
+ masm.branch32(Assembler::Equal, argcReg, Imm32(2), &twoArg);
+ masm.assumeUnreachable("Expected argc == 2");
+ masm.bind(&twoArg);
+#endif
+ Register scratchReg = regs.takeAny();
+
+ // Guard that callee is native function js::intrinsic_StringSplitString.
+ {
+ Address calleeAddr(masm.getStackPointer(), ICStackValueOffset + CALLEE_DEPTH);
+ ValueOperand calleeVal = regs.takeAnyValue();
+
+ // Ensure that callee is an object.
+ masm.loadValue(calleeAddr, calleeVal);
+ masm.branchTestObject(Assembler::NotEqual, calleeVal, &failureRestoreArgc);
+
+ // Ensure that callee is a function.
+ Register calleeObj = masm.extractObject(calleeVal, ExtractTemp0);
+ masm.branchTestObjClass(Assembler::NotEqual, calleeObj, scratchReg,
+ &JSFunction::class_, &failureRestoreArgc);
+
+ // Ensure that callee's function impl is the native intrinsic_StringSplitString.
+ masm.loadPtr(Address(calleeObj, JSFunction::offsetOfNativeOrScript()), scratchReg);
+ masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(js::intrinsic_StringSplitString),
+ &failureRestoreArgc);
+
+ regs.add(calleeVal);
+ }
+
+ // Guard sep.
+ {
+ // Ensure that sep is a string.
+ Address sepAddr(masm.getStackPointer(), ICStackValueOffset + SEP_DEPTH);
+ ValueOperand sepVal = regs.takeAnyValue();
+
+ masm.loadValue(sepAddr, sepVal);
+ masm.branchTestString(Assembler::NotEqual, sepVal, &failureRestoreArgc);
+
+ Register sep = masm.extractString(sepVal, ExtractTemp0);
+ masm.branchPtr(Assembler::NotEqual, Address(ICStubReg, offsetOfExpectedSep()),
+ sep, &failureRestoreArgc);
+ regs.add(sepVal);
+ }
+
+ // Guard str.
+ {
+ // Ensure that str is a string.
+ Address strAddr(masm.getStackPointer(), ICStackValueOffset + STR_DEPTH);
+ ValueOperand strVal = regs.takeAnyValue();
+
+ masm.loadValue(strAddr, strVal);
+ masm.branchTestString(Assembler::NotEqual, strVal, &failureRestoreArgc);
+
+ Register str = masm.extractString(strVal, ExtractTemp0);
+ masm.branchPtr(Assembler::NotEqual, Address(ICStubReg, offsetOfExpectedStr()),
+ str, &failureRestoreArgc);
+ regs.add(strVal);
+ }
+
+ // Main stub body.
+ {
+ Register paramReg = regs.takeAny();
+
+ // Push arguments.
+ enterStubFrame(masm, scratchReg);
+ masm.loadPtr(Address(ICStubReg, offsetOfTemplateObject()), paramReg);
+ masm.push(paramReg);
+
+ if (!callVM(CopyArrayInfo, masm))
+ return false;
+ leaveStubFrame(masm);
+ regs.add(paramReg);
+ }
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ // Guard failure path.
+ masm.bind(&failureRestoreArgc);
+ masm.move32(Imm32(2), R0.scratchReg());
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICCall_IsSuspendedStarGenerator::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ // The IsSuspendedStarGenerator intrinsic is only called in self-hosted
+ // code, so it's safe to assume we have a single argument and the callee
+ // is our intrinsic.
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+
+ // Load the argument.
+ Address argAddr(masm.getStackPointer(), ICStackValueOffset);
+ ValueOperand argVal = regs.takeAnyValue();
+ masm.loadValue(argAddr, argVal);
+
+ // Check if it's an object.
+ Label returnFalse;
+ Register genObj = regs.takeAny();
+ masm.branchTestObject(Assembler::NotEqual, argVal, &returnFalse);
+ masm.unboxObject(argVal, genObj);
+
+ // Check if it's a StarGeneratorObject.
+ Register scratch = regs.takeAny();
+ masm.branchTestObjClass(Assembler::NotEqual, genObj, scratch, &StarGeneratorObject::class_,
+ &returnFalse);
+
+ // If the yield index slot holds an int32 value < YIELD_INDEX_CLOSING,
+ // the generator is suspended.
+ masm.loadValue(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), argVal);
+ masm.branchTestInt32(Assembler::NotEqual, argVal, &returnFalse);
+ masm.unboxInt32(argVal, scratch);
+ masm.branch32(Assembler::AboveOrEqual, scratch, Imm32(StarGeneratorObject::YIELD_INDEX_CLOSING),
+ &returnFalse);
+
+ masm.moveValue(BooleanValue(true), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&returnFalse);
+ masm.moveValue(BooleanValue(false), R0);
+ EmitReturnFromIC(masm);
+ return true;
+}
+
+bool
+ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+
+ Register argcReg = R0.scratchReg();
+ regs.take(argcReg);
+ regs.takeUnchecked(ICTailCallReg);
+
+ if (isSpread_)
+ guardSpreadCall(masm, argcReg, &failure, isConstructing_);
+
+ // Load the callee in R1.
+ if (isSpread_) {
+ masm.loadValue(Address(masm.getStackPointer(), ICStackValueOffset + 2 * sizeof(Value)), R1);
+ } else {
+ unsigned nonArgsSlots = (1 + isConstructing_) * sizeof(Value);
+ BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + nonArgsSlots);
+ masm.loadValue(calleeSlot, R1);
+ }
+ regs.take(R1);
+
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+
+ // Ensure callee matches this stub's callee.
+ Register callee = masm.extractObject(R1, ExtractTemp0);
+ Address expectedCallee(ICStubReg, ICCall_Native::offsetOfCallee());
+ masm.branchPtr(Assembler::NotEqual, expectedCallee, callee, &failure);
+
+ regs.add(R1);
+ regs.takeUnchecked(callee);
+
+ // Push a stub frame so that we can perform a non-tail call.
+ // Note that this leaves the return address in TailCallReg.
+ enterStubFrame(masm, regs.getAny());
+
+ // Values are on the stack left-to-right. Calling convention wants them
+ // right-to-left so duplicate them on the stack in reverse order.
+ // |this| and callee are pushed last.
+ if (isSpread_)
+ pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
+ else
+ pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
+
+
+ // Native functions have the signature:
+ //
+ // bool (*)(JSContext*, unsigned, Value* vp)
+ //
+ // Where vp[0] is space for callee/return value, vp[1] is |this|, and vp[2] onward
+ // are the function arguments.
+
+ // Initialize vp.
+ Register vpReg = regs.takeAny();
+ masm.moveStackPtrTo(vpReg);
+
+ // Construct a native exit frame.
+ masm.push(argcReg);
+
+ Register scratch = regs.takeAny();
+ EmitBaselineCreateStubFrameDescriptor(masm, scratch, ExitFrameLayout::Size());
+ masm.push(scratch);
+ masm.push(ICTailCallReg);
+ masm.enterFakeExitFrameForNative(isConstructing_);
+
+ // Execute call.
+ masm.setupUnalignedABICall(scratch);
+ masm.loadJSContext(scratch);
+ masm.passABIArg(scratch);
+ masm.passABIArg(argcReg);
+ masm.passABIArg(vpReg);
+
+#ifdef JS_SIMULATOR
+ // The simulator requires VM calls to be redirected to a special swi
+ // instruction to handle them, so we store the redirected pointer in the
+ // stub and use that instead of the original one.
+ masm.callWithABI(Address(ICStubReg, ICCall_Native::offsetOfNative()));
+#else
+ masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript()));
+#endif
+
+ // Test for failure.
+ masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
+
+ // Load the return value into R0.
+ masm.loadValue(Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), R0);
+
+ leaveStubFrame(masm);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+
+ Register argcReg = R0.scratchReg();
+ regs.take(argcReg);
+ regs.takeUnchecked(ICTailCallReg);
+
+ // Load the callee in R1.
+ unsigned nonArgSlots = (1 + isConstructing_) * sizeof(Value);
+ BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + nonArgSlots);
+ masm.loadValue(calleeSlot, R1);
+ regs.take(R1);
+
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+
+ // Ensure the callee's class matches the one in this stub.
+ Register callee = masm.extractObject(R1, ExtractTemp0);
+ Register scratch = regs.takeAny();
+ masm.loadObjClass(callee, scratch);
+ masm.branchPtr(Assembler::NotEqual,
+ Address(ICStubReg, ICCall_ClassHook::offsetOfClass()),
+ scratch, &failure);
+
+ regs.add(R1);
+ regs.takeUnchecked(callee);
+
+ // Push a stub frame so that we can perform a non-tail call.
+ // Note that this leaves the return address in TailCallReg.
+ enterStubFrame(masm, regs.getAny());
+
+ regs.add(scratch);
+ pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
+ regs.take(scratch);
+
+ masm.checkStackAlignment();
+
+ // Native functions have the signature:
+ //
+ // bool (*)(JSContext*, unsigned, Value* vp)
+ //
+ // Where vp[0] is space for callee/return value, vp[1] is |this|, and vp[2] onward
+ // are the function arguments.
+
+ // Initialize vp.
+ Register vpReg = regs.takeAny();
+ masm.moveStackPtrTo(vpReg);
+
+ // Construct a native exit frame.
+ masm.push(argcReg);
+
+ EmitBaselineCreateStubFrameDescriptor(masm, scratch, ExitFrameLayout::Size());
+ masm.push(scratch);
+ masm.push(ICTailCallReg);
+ masm.enterFakeExitFrameForNative(isConstructing_);
+
+ // Execute call.
+ masm.setupUnalignedABICall(scratch);
+ masm.loadJSContext(scratch);
+ masm.passABIArg(scratch);
+ masm.passABIArg(argcReg);
+ masm.passABIArg(vpReg);
+ masm.callWithABI(Address(ICStubReg, ICCall_ClassHook::offsetOfNative()));
+
+ // Test for failure.
+ masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
+
+ // Load the return value into R0.
+ masm.loadValue(Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), R0);
+
+ leaveStubFrame(masm);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICCall_ScriptedApplyArray::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+
+ Register argcReg = R0.scratchReg();
+ regs.take(argcReg);
+ regs.takeUnchecked(ICTailCallReg);
+ regs.takeUnchecked(ArgumentsRectifierReg);
+
+ //
+ // Validate inputs
+ //
+
+ Register target = guardFunApply(masm, regs, argcReg, /*checkNative=*/false,
+ FunApply_Array, &failure);
+ if (regs.has(target)) {
+ regs.take(target);
+ } else {
+ // If target is already a reserved reg, take another register for it, because it's
+ // probably currently an ExtractTemp, which might get clobbered later.
+ Register targetTemp = regs.takeAny();
+ masm.movePtr(target, targetTemp);
+ target = targetTemp;
+ }
+
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, regs.getAny());
+
+ //
+ // Push arguments
+ //
+
+ // Stack now looks like:
+ // BaselineFrameReg -------------------.
+ // v
+ // [..., fun_apply, TargetV, TargetThisV, ArgsArrayV, StubFrameHeader]
+
+ // Push all array elements onto the stack:
+ Address arrayVal(BaselineFrameReg, STUB_FRAME_SIZE);
+ pushArrayArguments(masm, arrayVal, regs);
+
+ // Stack now looks like:
+ // BaselineFrameReg -------------------.
+ // v
+ // [..., fun_apply, TargetV, TargetThisV, ArgsArrayV, StubFrameHeader,
+ // PushedArgN, ..., PushedArg0]
+ // Can't fail after this, so it's ok to clobber argcReg.
+
+ // Push actual argument 0 as |thisv| for call.
+ masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + sizeof(Value)));
+
+ // All pushes after this use Push instead of push to make sure ARM can align
+ // stack properly for call.
+ Register scratch = regs.takeAny();
+ EmitBaselineCreateStubFrameDescriptor(masm, scratch, JitFrameLayout::Size());
+
+ // Reload argc from length of array.
+ masm.extractObject(arrayVal, argcReg);
+ masm.loadPtr(Address(argcReg, NativeObject::offsetOfElements()), argcReg);
+ masm.load32(Address(argcReg, ObjectElements::offsetOfInitializedLength()), argcReg);
+
+ masm.Push(argcReg);
+ masm.Push(target);
+ masm.Push(scratch);
+
+ // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
+ masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
+ masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), target);
+ masm.loadBaselineOrIonRaw(target, target, nullptr);
+
+ // Handle arguments underflow.
+ Label noUnderflow;
+ masm.branch32(Assembler::AboveOrEqual, argcReg, scratch, &noUnderflow);
+ {
+ // Call the arguments rectifier.
+ MOZ_ASSERT(ArgumentsRectifierReg != target);
+ MOZ_ASSERT(ArgumentsRectifierReg != argcReg);
+
+ JitCode* argumentsRectifier =
+ cx->runtime()->jitRuntime()->getArgumentsRectifier();
+
+ masm.movePtr(ImmGCPtr(argumentsRectifier), target);
+ masm.loadPtr(Address(target, JitCode::offsetOfCode()), target);
+ masm.movePtr(argcReg, ArgumentsRectifierReg);
+ }
+ masm.bind(&noUnderflow);
+ regs.add(argcReg);
+
+ // Do call
+ masm.callJit(target);
+ leaveStubFrame(masm, true);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICCall_ScriptedApplyArguments::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+
+ Register argcReg = R0.scratchReg();
+ regs.take(argcReg);
+ regs.takeUnchecked(ICTailCallReg);
+ regs.takeUnchecked(ArgumentsRectifierReg);
+
+ //
+ // Validate inputs
+ //
+
+ Register target = guardFunApply(masm, regs, argcReg, /*checkNative=*/false,
+ FunApply_MagicArgs, &failure);
+ if (regs.has(target)) {
+ regs.take(target);
+ } else {
+ // If target is already a reserved reg, take another register for it, because it's
+ // probably currently an ExtractTemp, which might get clobbered later.
+ Register targetTemp = regs.takeAny();
+ masm.movePtr(target, targetTemp);
+ target = targetTemp;
+ }
+
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, regs.getAny());
+
+ //
+ // Push arguments
+ //
+
+ // Stack now looks like:
+ // [..., fun_apply, TargetV, TargetThisV, MagicArgsV, StubFrameHeader]
+
+ // Push all arguments supplied to caller function onto the stack.
+ pushCallerArguments(masm, regs);
+
+ // Stack now looks like:
+ // BaselineFrameReg -------------------.
+ // v
+ // [..., fun_apply, TargetV, TargetThisV, MagicArgsV, StubFrameHeader,
+ // PushedArgN, ..., PushedArg0]
+ // Can't fail after this, so it's ok to clobber argcReg.
+
+ // Push actual argument 0 as |thisv| for call.
+ masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + sizeof(Value)));
+
+ // All pushes after this use Push instead of push to make sure ARM can align
+ // stack properly for call.
+ Register scratch = regs.takeAny();
+ EmitBaselineCreateStubFrameDescriptor(masm, scratch, JitFrameLayout::Size());
+
+ masm.loadPtr(Address(BaselineFrameReg, 0), argcReg);
+ masm.loadPtr(Address(argcReg, BaselineFrame::offsetOfNumActualArgs()), argcReg);
+ masm.Push(argcReg);
+ masm.Push(target);
+ masm.Push(scratch);
+
+ // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
+ masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
+ masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), target);
+ masm.loadBaselineOrIonRaw(target, target, nullptr);
+
+ // Handle arguments underflow.
+ Label noUnderflow;
+ masm.branch32(Assembler::AboveOrEqual, argcReg, scratch, &noUnderflow);
+ {
+ // Call the arguments rectifier.
+ MOZ_ASSERT(ArgumentsRectifierReg != target);
+ MOZ_ASSERT(ArgumentsRectifierReg != argcReg);
+
+ JitCode* argumentsRectifier =
+ cx->runtime()->jitRuntime()->getArgumentsRectifier();
+
+ masm.movePtr(ImmGCPtr(argumentsRectifier), target);
+ masm.loadPtr(Address(target, JitCode::offsetOfCode()), target);
+ masm.movePtr(argcReg, ArgumentsRectifierReg);
+ }
+ masm.bind(&noUnderflow);
+ regs.add(argcReg);
+
+ // Do call
+ masm.callJit(target);
+ leaveStubFrame(masm, true);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+bool
+ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+ bool canUseTailCallReg = regs.has(ICTailCallReg);
+
+ Register argcReg = R0.scratchReg();
+ MOZ_ASSERT(argcReg != ArgumentsRectifierReg);
+
+ regs.take(argcReg);
+ regs.take(ArgumentsRectifierReg);
+ regs.takeUnchecked(ICTailCallReg);
+
+ // Load the callee in R1.
+ // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, +ICStackValueOffset+ ]
+ BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + sizeof(Value));
+ masm.loadValue(calleeSlot, R1);
+ regs.take(R1);
+
+ // Ensure callee is fun_call.
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+
+ Register callee = masm.extractObject(R1, ExtractTemp0);
+ masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
+ &failure);
+ masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+ masm.branchPtr(Assembler::NotEqual, callee, ImmPtr(fun_call), &failure);
+
+ // Ensure |this| is a scripted function with JIT code.
+ BaseIndex thisSlot(masm.getStackPointer(), argcReg, TimesEight, ICStackValueOffset);
+ masm.loadValue(thisSlot, R1);
+
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+ callee = masm.extractObject(R1, ExtractTemp0);
+
+ masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
+ &failure);
+ masm.branchIfFunctionHasNoScript(callee, &failure);
+ masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+
+ // Load the start of the target JitCode.
+ Register code = regs.takeAny();
+ masm.loadBaselineOrIonRaw(callee, code, &failure);
+
+ // We no longer need R1.
+ regs.add(R1);
+
+ // Push a stub frame so that we can perform a non-tail call.
+ enterStubFrame(masm, regs.getAny());
+ if (canUseTailCallReg)
+ regs.add(ICTailCallReg);
+
+ // Decrement argc if argc > 0. If argc == 0, push |undefined| as |this|.
+ Label zeroArgs, done;
+ masm.branchTest32(Assembler::Zero, argcReg, argcReg, &zeroArgs);
+
+ // Avoid the copy of the callee (function.call).
+ masm.sub32(Imm32(1), argcReg);
+
+ // Values are on the stack left-to-right. Calling convention wants them
+ // right-to-left so duplicate them on the stack in reverse order.
+
+ pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true);
+
+ // Pop scripted callee (the original |this|).
+ ValueOperand val = regs.takeAnyValue();
+ masm.popValue(val);
+
+ masm.jump(&done);
+ masm.bind(&zeroArgs);
+
+ // Copy scripted callee (the original |this|).
+ Address thisSlotFromStubFrame(BaselineFrameReg, STUB_FRAME_SIZE);
+ masm.loadValue(thisSlotFromStubFrame, val);
+
+ // Align the stack.
+ masm.alignJitStackBasedOnNArgs(0);
+
+ // Store the new |this|.
+ masm.pushValue(UndefinedValue());
+
+ masm.bind(&done);
+
+ // Unbox scripted callee.
+ callee = masm.extractObject(val, ExtractTemp0);
+
+ Register scratch = regs.takeAny();
+ EmitBaselineCreateStubFrameDescriptor(masm, scratch, JitFrameLayout::Size());
+
+ // Note that we use Push, not push, so that callJit will align the stack
+ // properly on ARM.
+ masm.Push(argcReg);
+ masm.Push(callee);
+ masm.Push(scratch);
+
+ // Handle arguments underflow.
+ Label noUnderflow;
+ masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), callee);
+ masm.branch32(Assembler::AboveOrEqual, argcReg, callee, &noUnderflow);
+ {
+ // Call the arguments rectifier.
+ MOZ_ASSERT(ArgumentsRectifierReg != code);
+ MOZ_ASSERT(ArgumentsRectifierReg != argcReg);
+
+ JitCode* argumentsRectifier =
+ cx->runtime()->jitRuntime()->getArgumentsRectifier();
+
+ masm.movePtr(ImmGCPtr(argumentsRectifier), code);
+ masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
+ masm.movePtr(argcReg, ArgumentsRectifierReg);
+ }
+
+ masm.bind(&noUnderflow);
+ masm.callJit(code);
+
+ leaveStubFrame(masm, true);
+
+ // Enter type monitor IC to type-check result.
+ EmitEnterTypeMonitorIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+static bool
+DoubleValueToInt32ForSwitch(Value* v)
+{
+ double d = v->toDouble();
+ int32_t truncated = int32_t(d);
+ if (d != double(truncated))
+ return false;
+
+ v->setInt32(truncated);
+ return true;
+}
+
+bool
+ICTableSwitch::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label isInt32, notInt32, outOfRange;
+ Register scratch = R1.scratchReg();
+
+ masm.branchTestInt32(Assembler::NotEqual, R0, &notInt32);
+
+ Register key = masm.extractInt32(R0, ExtractTemp0);
+
+ masm.bind(&isInt32);
+
+ masm.load32(Address(ICStubReg, offsetof(ICTableSwitch, min_)), scratch);
+ masm.sub32(scratch, key);
+ masm.branch32(Assembler::BelowOrEqual,
+ Address(ICStubReg, offsetof(ICTableSwitch, length_)), key, &outOfRange);
+
+ masm.loadPtr(Address(ICStubReg, offsetof(ICTableSwitch, table_)), scratch);
+ masm.loadPtr(BaseIndex(scratch, key, ScalePointer), scratch);
+
+ EmitChangeICReturnAddress(masm, scratch);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&notInt32);
+
+ masm.branchTestDouble(Assembler::NotEqual, R0, &outOfRange);
+ if (cx->runtime()->jitSupportsFloatingPoint) {
+ masm.unboxDouble(R0, FloatReg0);
+
+ // N.B. -0 === 0, so convert -0 to a 0 int32.
+ masm.convertDoubleToInt32(FloatReg0, key, &outOfRange, /* negativeZeroCheck = */ false);
+ } else {
+ // Pass pointer to double value.
+ masm.pushValue(R0);
+ masm.moveStackPtrTo(R0.scratchReg());
+
+ masm.setupUnalignedABICall(scratch);
+ masm.passABIArg(R0.scratchReg());
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, DoubleValueToInt32ForSwitch));
+
+ // If the function returns |true|, the value has been converted to
+ // int32.
+ masm.movePtr(ReturnReg, scratch);
+ masm.popValue(R0);
+ masm.branchIfFalseBool(scratch, &outOfRange);
+ masm.unboxInt32(R0, key);
+ }
+ masm.jump(&isInt32);
+
+ masm.bind(&outOfRange);
+
+ masm.loadPtr(Address(ICStubReg, offsetof(ICTableSwitch, defaultTarget_)), scratch);
+
+ EmitChangeICReturnAddress(masm, scratch);
+ EmitReturnFromIC(masm);
+ return true;
+}
+
+ICStub*
+ICTableSwitch::Compiler::getStub(ICStubSpace* space)
+{
+ JitCode* code = getStubCode();
+ if (!code)
+ return nullptr;
+
+ jsbytecode* pc = pc_;
+ pc += JUMP_OFFSET_LEN;
+ int32_t low = GET_JUMP_OFFSET(pc);
+ pc += JUMP_OFFSET_LEN;
+ int32_t high = GET_JUMP_OFFSET(pc);
+ int32_t length = high - low + 1;
+ pc += JUMP_OFFSET_LEN;
+
+ void** table = (void**) space->alloc(sizeof(void*) * length);
+ if (!table) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+
+ jsbytecode* defaultpc = pc_ + GET_JUMP_OFFSET(pc_);
+
+ for (int32_t i = 0; i < length; i++) {
+ int32_t off = GET_JUMP_OFFSET(pc);
+ if (off)
+ table[i] = pc_ + off;
+ else
+ table[i] = defaultpc;
+ pc += JUMP_OFFSET_LEN;
+ }
+
+ return newStub<ICTableSwitch>(space, code, table, low, length, defaultpc);
+}
+
+void
+ICTableSwitch::fixupJumpTable(JSScript* script, BaselineScript* baseline)
+{
+ defaultTarget_ = baseline->nativeCodeForPC(script, (jsbytecode*) defaultTarget_);
+
+ for (int32_t i = 0; i < length_; i++)
+ table_[i] = baseline->nativeCodeForPC(script, (jsbytecode*) table_[i]);
+}
+
+//
+// IteratorNew_Fallback
+//
+
+static bool
+DoIteratorNewFallback(JSContext* cx, BaselineFrame* frame, ICIteratorNew_Fallback* stub,
+ HandleValue value, MutableHandleValue res)
+{
+ jsbytecode* pc = stub->icEntry()->pc(frame->script());
+ FallbackICSpew(cx, stub, "IteratorNew");
+
+ uint8_t flags = GET_UINT8(pc);
+ res.set(value);
+ RootedObject iterobj(cx, ValueToIterator(cx, flags, res));
+ if (!iterobj)
+ return false;
+ res.setObject(*iterobj);
+ return true;
+}
+
+typedef bool (*DoIteratorNewFallbackFn)(JSContext*, BaselineFrame*, ICIteratorNew_Fallback*,
+ HandleValue, MutableHandleValue);
+static const VMFunction DoIteratorNewFallbackInfo =
+ FunctionInfo<DoIteratorNewFallbackFn>(DoIteratorNewFallback, "DoIteratorNewFallback",
+ TailCall, PopValues(1));
+
+bool
+ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ EmitRestoreTailCallReg(masm);
+
+ // Sync stack for the decompiler.
+ masm.pushValue(R0);
+
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoIteratorNewFallbackInfo, masm);
+}
+
+//
+// IteratorMore_Fallback
+//
+
+static bool
+DoIteratorMoreFallback(JSContext* cx, BaselineFrame* frame, ICIteratorMore_Fallback* stub_,
+ HandleObject iterObj, MutableHandleValue res)
+{
+ // This fallback stub may trigger debug mode toggling.
+ DebugModeOSRVolatileStub<ICIteratorMore_Fallback*> stub(frame, stub_);
+
+ FallbackICSpew(cx, stub, "IteratorMore");
+
+ if (!IteratorMore(cx, iterObj, res))
+ return false;
+
+ // Check if debug mode toggling made the stub invalid.
+ if (stub.invalid())
+ return true;
+
+ if (!res.isMagic(JS_NO_ITER_VALUE) && !res.isString())
+ stub->setHasNonStringResult();
+
+ if (iterObj->is<PropertyIteratorObject>() &&
+ !stub->hasStub(ICStub::IteratorMore_Native))
+ {
+ ICIteratorMore_Native::Compiler compiler(cx);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
+ if (!newStub)
+ return false;
+ stub->addNewStub(newStub);
+ }
+
+ return true;
+}
+
+typedef bool (*DoIteratorMoreFallbackFn)(JSContext*, BaselineFrame*, ICIteratorMore_Fallback*,
+ HandleObject, MutableHandleValue);
+static const VMFunction DoIteratorMoreFallbackInfo =
+ FunctionInfo<DoIteratorMoreFallbackFn>(DoIteratorMoreFallback, "DoIteratorMoreFallback",
+ TailCall);
+
+bool
+ICIteratorMore_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ EmitRestoreTailCallReg(masm);
+
+ masm.unboxObject(R0, R0.scratchReg());
+ masm.push(R0.scratchReg());
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoIteratorMoreFallbackInfo, masm);
+}
+
+//
+// IteratorMore_Native
+//
+
+bool
+ICIteratorMore_Native::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+
+ Register obj = masm.extractObject(R0, ExtractTemp0);
+
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
+ Register nativeIterator = regs.takeAny();
+ Register scratch = regs.takeAny();
+
+ masm.branchTestObjClass(Assembler::NotEqual, obj, scratch,
+ &PropertyIteratorObject::class_, &failure);
+ masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, nativeIterator);
+
+ masm.branchTest32(Assembler::NonZero, Address(nativeIterator, offsetof(NativeIterator, flags)),
+ Imm32(JSITER_FOREACH), &failure);
+
+ // If props_cursor < props_end, load the next string and advance the cursor.
+ // Else, return MagicValue(JS_NO_ITER_VALUE).
+ Label iterDone;
+ Address cursorAddr(nativeIterator, offsetof(NativeIterator, props_cursor));
+ Address cursorEndAddr(nativeIterator, offsetof(NativeIterator, props_end));
+ masm.loadPtr(cursorAddr, scratch);
+ masm.branchPtr(Assembler::BelowOrEqual, cursorEndAddr, scratch, &iterDone);
+
+ // Get next string.
+ masm.loadPtr(Address(scratch, 0), scratch);
+
+ // Increase the cursor.
+ masm.addPtr(Imm32(sizeof(JSString*)), cursorAddr);
+
+ masm.tagValue(JSVAL_TYPE_STRING, scratch, R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&iterDone);
+ masm.moveValue(MagicValue(JS_NO_ITER_VALUE), R0);
+ EmitReturnFromIC(masm);
+
+ // Failure case - jump to next stub
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// IteratorClose_Fallback
+//
+
+static bool
+DoIteratorCloseFallback(JSContext* cx, ICIteratorClose_Fallback* stub, HandleValue iterValue)
+{
+ FallbackICSpew(cx, stub, "IteratorClose");
+
+ RootedObject iteratorObject(cx, &iterValue.toObject());
+ return CloseIterator(cx, iteratorObject);
+}
+
+typedef bool (*DoIteratorCloseFallbackFn)(JSContext*, ICIteratorClose_Fallback*, HandleValue);
+static const VMFunction DoIteratorCloseFallbackInfo =
+ FunctionInfo<DoIteratorCloseFallbackFn>(DoIteratorCloseFallback, "DoIteratorCloseFallback",
+ TailCall);
+
+bool
+ICIteratorClose_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ EmitRestoreTailCallReg(masm);
+
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+
+ return tailCallVM(DoIteratorCloseFallbackInfo, masm);
+}
+
+//
+// InstanceOf_Fallback
+//
+
+static bool
+TryAttachInstanceOfStub(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub,
+ HandleFunction fun, bool* attached)
+{
+ MOZ_ASSERT(!*attached);
+ if (fun->isBoundFunction())
+ return true;
+
+ // If the user has supplied their own @@hasInstance method we shouldn't
+ // clobber it.
+ if (!js::FunctionHasDefaultHasInstance(fun, cx->wellKnownSymbols()))
+ return true;
+
+ // Refuse to optimize any function whose [[Prototype]] isn't
+ // Function.prototype.
+ if (!fun->hasStaticPrototype() || fun->hasUncacheableProto())
+ return true;
+
+ Value funProto = cx->global()->getPrototype(JSProto_Function);
+ if (funProto.isObject() && fun->staticPrototype() != &funProto.toObject())
+ return true;
+
+ Shape* shape = fun->lookupPure(cx->names().prototype);
+ if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
+ return true;
+
+ uint32_t slot = shape->slot();
+ MOZ_ASSERT(fun->numFixedSlots() == 0, "Stub code relies on this");
+
+ if (!fun->getSlot(slot).isObject())
+ return true;
+
+ JSObject* protoObject = &fun->getSlot(slot).toObject();
+
+ JitSpew(JitSpew_BaselineIC, " Generating InstanceOf(Function) stub");
+ ICInstanceOf_Function::Compiler compiler(cx, fun->lastProperty(), protoObject, slot);
+ ICStub* newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
+ if (!newStub)
+ return false;
+
+ stub->addNewStub(newStub);
+ *attached = true;
+ return true;
+}
+
+static bool
+DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub,
+ HandleValue lhs, HandleValue rhs, MutableHandleValue res)
+{
+ FallbackICSpew(cx, stub, "InstanceOf");
+
+ if (!rhs.isObject()) {
+ ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rhs, nullptr);
+ return false;
+ }
+
+ RootedObject obj(cx, &rhs.toObject());
+ bool cond = false;
+ if (!HasInstance(cx, obj, lhs, &cond))
+ return false;
+
+ res.setBoolean(cond);
+
+ if (!obj->is<JSFunction>()) {
+ stub->noteUnoptimizableAccess();
+ return true;
+ }
+
+ // For functions, keep track of the |prototype| property in type information,
+ // for use during Ion compilation.
+ EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
+
+ if (stub->numOptimizedStubs() >= ICInstanceOf_Fallback::MAX_OPTIMIZED_STUBS)
+ return true;
+
+ RootedFunction fun(cx, &obj->as<JSFunction>());
+ bool attached = false;
+ if (!TryAttachInstanceOfStub(cx, frame, stub, fun, &attached))
+ return false;
+ if (!attached)
+ stub->noteUnoptimizableAccess();
+ return true;
+}
+
+typedef bool (*DoInstanceOfFallbackFn)(JSContext*, BaselineFrame*, ICInstanceOf_Fallback*,
+ HandleValue, HandleValue, MutableHandleValue);
+static const VMFunction DoInstanceOfFallbackInfo =
+ FunctionInfo<DoInstanceOfFallbackFn>(DoInstanceOfFallback, "DoInstanceOfFallback", TailCall,
+ PopValues(2));
+
+bool
+ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ EmitRestoreTailCallReg(masm);
+
+ // Sync stack for the decompiler.
+ masm.pushValue(R0);
+ masm.pushValue(R1);
+
+ masm.pushValue(R1);
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoInstanceOfFallbackInfo, masm);
+}
+
+bool
+ICInstanceOf_Function::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ Label failure;
+
+ // Ensure RHS is an object.
+ masm.branchTestObject(Assembler::NotEqual, R1, &failure);
+ Register rhsObj = masm.extractObject(R1, ExtractTemp0);
+
+ // Allow using R1's type register as scratch. We have to restore it when
+ // we want to jump to the next stub.
+ Label failureRestoreR1;
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
+ regs.takeUnchecked(rhsObj);
+
+ Register scratch1 = regs.takeAny();
+ Register scratch2 = regs.takeAny();
+
+ // Shape guard.
+ masm.loadPtr(Address(ICStubReg, ICInstanceOf_Function::offsetOfShape()), scratch1);
+ masm.branchTestObjShape(Assembler::NotEqual, rhsObj, scratch1, &failureRestoreR1);
+
+ // Guard on the .prototype object.
+ masm.loadPtr(Address(rhsObj, NativeObject::offsetOfSlots()), scratch1);
+ masm.load32(Address(ICStubReg, ICInstanceOf_Function::offsetOfSlot()), scratch2);
+ BaseValueIndex prototypeSlot(scratch1, scratch2);
+ masm.branchTestObject(Assembler::NotEqual, prototypeSlot, &failureRestoreR1);
+ masm.unboxObject(prototypeSlot, scratch1);
+ masm.branchPtr(Assembler::NotEqual,
+ Address(ICStubReg, ICInstanceOf_Function::offsetOfPrototypeObject()),
+ scratch1, &failureRestoreR1);
+
+ // If LHS is a primitive, return false.
+ Label returnFalse, returnTrue;
+ masm.branchTestObject(Assembler::NotEqual, R0, &returnFalse);
+
+ // LHS is an object. Load its proto.
+ masm.unboxObject(R0, scratch2);
+ masm.loadObjProto(scratch2, scratch2);
+
+ {
+ // Walk the proto chain until we either reach the target object,
+ // nullptr or LazyProto.
+ Label loop;
+ masm.bind(&loop);
+
+ masm.branchPtr(Assembler::Equal, scratch2, scratch1, &returnTrue);
+ masm.branchTestPtr(Assembler::Zero, scratch2, scratch2, &returnFalse);
+
+ MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1);
+ masm.branchPtr(Assembler::Equal, scratch2, ImmWord(1), &failureRestoreR1);
+
+ masm.loadObjProto(scratch2, scratch2);
+ masm.jump(&loop);
+ }
+
+ EmitReturnFromIC(masm);
+
+ masm.bind(&returnFalse);
+ masm.moveValue(BooleanValue(false), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&returnTrue);
+ masm.moveValue(BooleanValue(true), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&failureRestoreR1);
+ masm.tagValue(JSVAL_TYPE_OBJECT, rhsObj, R1);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+//
+// TypeOf_Fallback
+//
+
+static bool
+DoTypeOfFallback(JSContext* cx, BaselineFrame* frame, ICTypeOf_Fallback* stub, HandleValue val,
+ MutableHandleValue res)
+{
+ FallbackICSpew(cx, stub, "TypeOf");
+ JSType type = js::TypeOfValue(val);
+ RootedString string(cx, TypeName(type, cx->names()));
+
+ res.setString(string);
+
+ MOZ_ASSERT(type != JSTYPE_NULL);
+ if (type != JSTYPE_OBJECT && type != JSTYPE_FUNCTION) {
+ // Create a new TypeOf stub.
+ JitSpew(JitSpew_BaselineIC, " Generating TypeOf stub for JSType (%d)", (int) type);
+ ICTypeOf_Typed::Compiler compiler(cx, type, string);
+ ICStub* typeOfStub = compiler.getStub(compiler.getStubSpace(frame->script()));
+ if (!typeOfStub)
+ return false;
+ stub->addNewStub(typeOfStub);
+ }
+
+ return true;
+}
+
+typedef bool (*DoTypeOfFallbackFn)(JSContext*, BaselineFrame* frame, ICTypeOf_Fallback*,
+ HandleValue, MutableHandleValue);
+static const VMFunction DoTypeOfFallbackInfo =
+ FunctionInfo<DoTypeOfFallbackFn>(DoTypeOfFallback, "DoTypeOfFallback", TailCall);
+
+bool
+ICTypeOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ EmitRestoreTailCallReg(masm);
+
+ masm.pushValue(R0);
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoTypeOfFallbackInfo, masm);
+}
+
+bool
+ICTypeOf_Typed::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+ MOZ_ASSERT(type_ != JSTYPE_NULL);
+ MOZ_ASSERT(type_ != JSTYPE_FUNCTION);
+ MOZ_ASSERT(type_ != JSTYPE_OBJECT);
+
+ Label failure;
+ switch(type_) {
+ case JSTYPE_VOID:
+ masm.branchTestUndefined(Assembler::NotEqual, R0, &failure);
+ break;
+
+ case JSTYPE_STRING:
+ masm.branchTestString(Assembler::NotEqual, R0, &failure);
+ break;
+
+ case JSTYPE_NUMBER:
+ masm.branchTestNumber(Assembler::NotEqual, R0, &failure);
+ break;
+
+ case JSTYPE_BOOLEAN:
+ masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
+ break;
+
+ case JSTYPE_SYMBOL:
+ masm.branchTestSymbol(Assembler::NotEqual, R0, &failure);
+ break;
+
+ default:
+ MOZ_CRASH("Unexpected type");
+ }
+
+ masm.movePtr(ImmGCPtr(typeString_), R0.scratchReg());
+ masm.tagValue(JSVAL_TYPE_STRING, R0.scratchReg(), R0);
+ EmitReturnFromIC(masm);
+
+ masm.bind(&failure);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+static bool
+DoRetSubFallback(JSContext* cx, BaselineFrame* frame, ICRetSub_Fallback* stub,
+ HandleValue val, uint8_t** resumeAddr)
+{
+ FallbackICSpew(cx, stub, "RetSub");
+
+ // |val| is the bytecode offset where we should resume.
+
+ MOZ_ASSERT(val.isInt32());
+ MOZ_ASSERT(val.toInt32() >= 0);
+
+ JSScript* script = frame->script();
+ uint32_t offset = uint32_t(val.toInt32());
+
+ *resumeAddr = script->baselineScript()->nativeCodeForPC(script, script->offsetToPC(offset));
+
+ if (stub->numOptimizedStubs() >= ICRetSub_Fallback::MAX_OPTIMIZED_STUBS)
+ return true;
+
+ // Attach an optimized stub for this pc offset.
+ JitSpew(JitSpew_BaselineIC, " Generating RetSub stub for pc offset %u", offset);
+ ICRetSub_Resume::Compiler compiler(cx, offset, *resumeAddr);
+ ICStub* optStub = compiler.getStub(compiler.getStubSpace(script));
+ if (!optStub)
+ return false;
+
+ stub->addNewStub(optStub);
+ return true;
+}
+
+typedef bool(*DoRetSubFallbackFn)(JSContext* cx, BaselineFrame*, ICRetSub_Fallback*,
+ HandleValue, uint8_t**);
+static const VMFunction DoRetSubFallbackInfo =
+ FunctionInfo<DoRetSubFallbackFn>(DoRetSubFallback, "DoRetSubFallback");
+
+typedef bool (*ThrowFn)(JSContext*, HandleValue);
+static const VMFunction ThrowInfoBaseline =
+ FunctionInfo<ThrowFn>(js::Throw, "ThrowInfoBaseline", TailCall);
+
+bool
+ICRetSub_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ // If R0 is BooleanValue(true), rethrow R1.
+ Label rethrow;
+ masm.branchTestBooleanTruthy(true, R0, &rethrow);
+ {
+ // Call a stub to get the native code address for the pc offset in R1.
+ AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
+ regs.take(R1);
+ regs.takeUnchecked(ICTailCallReg);
+ Register scratch = regs.getAny();
+
+ enterStubFrame(masm, scratch);
+
+ masm.pushValue(R1);
+ masm.push(ICStubReg);
+ pushStubPayload(masm, scratch);
+
+ if (!callVM(DoRetSubFallbackInfo, masm))
+ return false;
+
+ leaveStubFrame(masm);
+
+ EmitChangeICReturnAddress(masm, ReturnReg);
+ EmitReturnFromIC(masm);
+ }
+
+ masm.bind(&rethrow);
+ EmitRestoreTailCallReg(masm);
+ masm.pushValue(R1);
+ return tailCallVM(ThrowInfoBaseline, masm);
+}
+
+bool
+ICRetSub_Resume::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ // If R0 is BooleanValue(true), rethrow R1.
+ Label fail, rethrow;
+ masm.branchTestBooleanTruthy(true, R0, &rethrow);
+
+ // R1 is the pc offset. Ensure it matches this stub's offset.
+ Register offset = masm.extractInt32(R1, ExtractTemp0);
+ masm.branch32(Assembler::NotEqual,
+ Address(ICStubReg, ICRetSub_Resume::offsetOfPCOffset()),
+ offset,
+ &fail);
+
+ // pc offset matches, resume at the target pc.
+ masm.loadPtr(Address(ICStubReg, ICRetSub_Resume::offsetOfAddr()), R0.scratchReg());
+ EmitChangeICReturnAddress(masm, R0.scratchReg());
+ EmitReturnFromIC(masm);
+
+ // Rethrow the Value stored in R1.
+ masm.bind(&rethrow);
+ EmitRestoreTailCallReg(masm);
+ masm.pushValue(R1);
+ if (!tailCallVM(ThrowInfoBaseline, masm))
+ return false;
+
+ masm.bind(&fail);
+ EmitStubGuardFailure(masm);
+ return true;
+}
+
+ICTypeMonitor_SingleObject::ICTypeMonitor_SingleObject(JitCode* stubCode, JSObject* obj)
+ : ICStub(TypeMonitor_SingleObject, stubCode),
+ obj_(obj)
+{ }
+
+ICTypeMonitor_ObjectGroup::ICTypeMonitor_ObjectGroup(JitCode* stubCode, ObjectGroup* group)
+ : ICStub(TypeMonitor_ObjectGroup, stubCode),
+ group_(group)
+{ }
+
+ICTypeUpdate_SingleObject::ICTypeUpdate_SingleObject(JitCode* stubCode, JSObject* obj)
+ : ICStub(TypeUpdate_SingleObject, stubCode),
+ obj_(obj)
+{ }
+
+ICTypeUpdate_ObjectGroup::ICTypeUpdate_ObjectGroup(JitCode* stubCode, ObjectGroup* group)
+ : ICStub(TypeUpdate_ObjectGroup, stubCode),
+ group_(group)
+{ }
+
+ICGetElemNativeStub::ICGetElemNativeStub(ICStub::Kind kind, JitCode* stubCode,
+ ICStub* firstMonitorStub,
+ ReceiverGuard guard, AccessType acctype,
+ bool needsAtomize, bool isSymbol)
+ : ICMonitoredStub(kind, stubCode, firstMonitorStub),
+ receiverGuard_(guard)
+{
+ extra_ = (static_cast<uint16_t>(acctype) << ACCESSTYPE_SHIFT) |
+ (static_cast<uint16_t>(needsAtomize) << NEEDS_ATOMIZE_SHIFT) |
+ (static_cast<uint16_t>(isSymbol) << ISSYMBOL_SHIFT);
+}
+
+ICGetElemNativeStub::~ICGetElemNativeStub()
+{ }
+
+template <class T>
+ICGetElemNativeGetterStub<T>::ICGetElemNativeGetterStub(
+ ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
+ ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize,
+ JSFunction* getter, uint32_t pcOffset)
+ : ICGetElemNativeStubImpl<T>(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize),
+ getter_(getter),
+ pcOffset_(pcOffset)
+{
+ MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNativeName ||
+ kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
+ kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
+ kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol);
+ MOZ_ASSERT(acctype == ICGetElemNativeStub::NativeGetter ||
+ acctype == ICGetElemNativeStub::ScriptedGetter);
+}
+
+template <class T>
+ICGetElem_NativePrototypeSlot<T>::ICGetElem_NativePrototypeSlot(
+ JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
+ const T* key, AccType acctype, bool needsAtomize, uint32_t offset,
+ JSObject* holder, Shape* holderShape)
+ : ICGetElemNativeSlotStub<T>(getGetElemStubKind<T>(ICStub::GetElem_NativePrototypeSlotName),
+ stubCode, firstMonitorStub, guard, key, acctype, needsAtomize, offset),
+ holder_(holder),
+ holderShape_(holderShape)
+{ }
+
+template <class T>
+ICGetElemNativePrototypeCallStub<T>::ICGetElemNativePrototypeCallStub(
+ ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
+ ReceiverGuard guard, const T* key, AccType acctype,
+ bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
+ JSObject* holder, Shape* holderShape)
+ : ICGetElemNativeGetterStub<T>(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize,
+ getter, pcOffset),
+ holder_(holder),
+ holderShape_(holderShape)
+{}
+
+template <class T>
+/* static */ ICGetElem_NativePrototypeCallNative<T>*
+ICGetElem_NativePrototypeCallNative<T>::Clone(JSContext* cx,
+ ICStubSpace* space,
+ ICStub* firstMonitorStub,
+ ICGetElem_NativePrototypeCallNative<T>& other)
+{
+ return ICStub::New<ICGetElem_NativePrototypeCallNative<T>>(cx, space, other.jitCode(),
+ firstMonitorStub, other.receiverGuard(), &other.key().get(), other.accessType(),
+ other.needsAtomize(), other.getter(), other.pcOffset_, other.holder(),
+ other.holderShape());
+}
+
+template ICGetElem_NativePrototypeCallNative<JS::Symbol*>*
+ICGetElem_NativePrototypeCallNative<JS::Symbol*>::Clone(JSContext*, ICStubSpace*, ICStub*,
+ ICGetElem_NativePrototypeCallNative<JS::Symbol*>&);
+template ICGetElem_NativePrototypeCallNative<js::PropertyName*>*
+ICGetElem_NativePrototypeCallNative<js::PropertyName*>::Clone(JSContext*, ICStubSpace*, ICStub*,
+ ICGetElem_NativePrototypeCallNative<js::PropertyName*>&);
+
+template <class T>
+/* static */ ICGetElem_NativePrototypeCallScripted<T>*
+ICGetElem_NativePrototypeCallScripted<T>::Clone(JSContext* cx,
+ ICStubSpace* space,
+ ICStub* firstMonitorStub,
+ ICGetElem_NativePrototypeCallScripted<T>& other)
+{
+ return ICStub::New<ICGetElem_NativePrototypeCallScripted<T>>(cx, space, other.jitCode(),
+ firstMonitorStub, other.receiverGuard(), &other.key().get(), other.accessType(),
+ other.needsAtomize(), other.getter(), other.pcOffset_, other.holder(),
+ other.holderShape());
+}
+
+template ICGetElem_NativePrototypeCallScripted<JS::Symbol*>*
+ICGetElem_NativePrototypeCallScripted<JS::Symbol*>::Clone(JSContext*, ICStubSpace*, ICStub*,
+ ICGetElem_NativePrototypeCallScripted<JS::Symbol*>&);
+template ICGetElem_NativePrototypeCallScripted<js::PropertyName*>*
+ICGetElem_NativePrototypeCallScripted<js::PropertyName*>::Clone(JSContext*, ICStubSpace*, ICStub*,
+ ICGetElem_NativePrototypeCallScripted<js::PropertyName*>&);
+
+ICGetElem_Dense::ICGetElem_Dense(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape)
+ : ICMonitoredStub(GetElem_Dense, stubCode, firstMonitorStub),
+ shape_(shape)
+{ }
+
+/* static */ ICGetElem_Dense*
+ICGetElem_Dense::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICGetElem_Dense& other)
+{
+ return New<ICGetElem_Dense>(cx, space, other.jitCode(), firstMonitorStub, other.shape_);
+}
+
+ICGetElem_UnboxedArray::ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub,
+ ObjectGroup *group)
+ : ICMonitoredStub(GetElem_UnboxedArray, stubCode, firstMonitorStub),
+ group_(group)
+{ }
+
+/* static */ ICGetElem_UnboxedArray*
+ICGetElem_UnboxedArray::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICGetElem_UnboxedArray& other)
+{
+ return New<ICGetElem_UnboxedArray>(cx, space, other.jitCode(), firstMonitorStub, other.group_);
+}
+
+ICGetElem_TypedArray::ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type)
+ : ICStub(GetElem_TypedArray, stubCode),
+ shape_(shape)
+{
+ extra_ = uint16_t(type);
+ MOZ_ASSERT(extra_ == type);
+}
+
+/* static */ ICGetElem_Arguments*
+ICGetElem_Arguments::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICGetElem_Arguments& other)
+{
+ return New<ICGetElem_Arguments>(cx, space, other.jitCode(), firstMonitorStub, other.which());
+}
+
+ICSetElem_DenseOrUnboxedArray::ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group)
+ : ICUpdatedStub(SetElem_DenseOrUnboxedArray, stubCode),
+ shape_(shape),
+ group_(group)
+{ }
+
+ICSetElem_DenseOrUnboxedArrayAdd::ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stubCode, ObjectGroup* group,
+ size_t protoChainDepth)
+ : ICUpdatedStub(SetElem_DenseOrUnboxedArrayAdd, stubCode),
+ group_(group)
+{
+ MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
+ extra_ = protoChainDepth;
+}
+
+template <size_t ProtoChainDepth>
+ICUpdatedStub*
+ICSetElemDenseOrUnboxedArrayAddCompiler::getStubSpecific(ICStubSpace* space,
+ Handle<ShapeVector> shapes)
+{
+ RootedObjectGroup group(cx, obj_->getGroup(cx));
+ if (!group)
+ return nullptr;
+ Rooted<JitCode*> stubCode(cx, getStubCode());
+ return newStub<ICSetElem_DenseOrUnboxedArrayAddImpl<ProtoChainDepth>>(space, stubCode, group, shapes);
+}
+
+ICSetElem_TypedArray::ICSetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type,
+ bool expectOutOfBounds)
+ : ICStub(SetElem_TypedArray, stubCode),
+ shape_(shape)
+{
+ extra_ = uint8_t(type);
+ MOZ_ASSERT(extra_ == type);
+ extra_ |= (static_cast<uint16_t>(expectOutOfBounds) << 8);
+}
+
+ICInNativeStub::ICInNativeStub(ICStub::Kind kind, JitCode* stubCode, HandleShape shape,
+ HandlePropertyName name)
+ : ICStub(kind, stubCode),
+ shape_(shape),
+ name_(name)
+{ }
+
+ICIn_NativePrototype::ICIn_NativePrototype(JitCode* stubCode, HandleShape shape,
+ HandlePropertyName name, HandleObject holder,
+ HandleShape holderShape)
+ : ICInNativeStub(In_NativePrototype, stubCode, shape, name),
+ holder_(holder),
+ holderShape_(holderShape)
+{ }
+
+ICIn_NativeDoesNotExist::ICIn_NativeDoesNotExist(JitCode* stubCode, size_t protoChainDepth,
+ HandlePropertyName name)
+ : ICStub(In_NativeDoesNotExist, stubCode),
+ name_(name)
+{
+ MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
+ extra_ = protoChainDepth;
+}
+
+/* static */ size_t
+ICIn_NativeDoesNotExist::offsetOfShape(size_t idx)
+{
+ MOZ_ASSERT(ICIn_NativeDoesNotExistImpl<0>::offsetOfShape(idx) ==
+ ICIn_NativeDoesNotExistImpl<
+ ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH>::offsetOfShape(idx));
+ return ICIn_NativeDoesNotExistImpl<0>::offsetOfShape(idx);
+}
+
+template <size_t ProtoChainDepth>
+ICIn_NativeDoesNotExistImpl<ProtoChainDepth>::ICIn_NativeDoesNotExistImpl(
+ JitCode* stubCode, Handle<ShapeVector> shapes, HandlePropertyName name)
+ : ICIn_NativeDoesNotExist(stubCode, ProtoChainDepth, name)
+{
+ MOZ_ASSERT(shapes.length() == NumShapes);
+ for (size_t i = 0; i < NumShapes; i++)
+ shapes_[i].init(shapes[i]);
+}
+
+ICInNativeDoesNotExistCompiler::ICInNativeDoesNotExistCompiler(
+ JSContext* cx, HandleObject obj, HandlePropertyName name, size_t protoChainDepth)
+ : ICStubCompiler(cx, ICStub::In_NativeDoesNotExist, Engine::Baseline),
+ obj_(cx, obj),
+ name_(cx, name),
+ protoChainDepth_(protoChainDepth)
+{
+ MOZ_ASSERT(protoChainDepth_ <= ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH);
+}
+
+ICIn_Dense::ICIn_Dense(JitCode* stubCode, HandleShape shape)
+ : ICStub(In_Dense, stubCode),
+ shape_(shape)
+{ }
+
+ICGetName_GlobalLexical::ICGetName_GlobalLexical(JitCode* stubCode, ICStub* firstMonitorStub,
+ uint32_t slot)
+ : ICMonitoredStub(GetName_GlobalLexical, stubCode, firstMonitorStub),
+ slot_(slot)
+{ }
+
+template <size_t NumHops>
+ICGetName_Env<NumHops>::ICGetName_Env(JitCode* stubCode, ICStub* firstMonitorStub,
+ Handle<ShapeVector> shapes, uint32_t offset)
+ : ICMonitoredStub(GetStubKind(), stubCode, firstMonitorStub),
+ offset_(offset)
+{
+ JS_STATIC_ASSERT(NumHops <= MAX_HOPS);
+ MOZ_ASSERT(shapes.length() == NumHops + 1);
+ for (size_t i = 0; i < NumHops + 1; i++)
+ shapes_[i].init(shapes[i]);
+}
+
+ICGetIntrinsic_Constant::ICGetIntrinsic_Constant(JitCode* stubCode, const Value& value)
+ : ICStub(GetIntrinsic_Constant, stubCode),
+ value_(value)
+{ }
+
+ICGetIntrinsic_Constant::~ICGetIntrinsic_Constant()
+{ }
+
+ICGetName_Global::ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub,
+ ReceiverGuard guard, uint32_t offset,
+ JSObject* holder, Shape* holderShape, Shape* globalShape)
+ : ICGetPropNativePrototypeStub(GetName_Global, stubCode, firstMonitorStub, guard, offset,
+ holder, holderShape),
+ globalShape_(globalShape)
+{ }
+
+/* static */ ICGetName_Global*
+ICGetName_Global::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICGetName_Global& other)
+{
+ return New<ICGetName_Global>(cx, space, other.jitCode(), firstMonitorStub,
+ other.receiverGuard(), other.offset(),
+ other.holder(), other.holderShape(), other.globalShape());
+}
+
+ICInstanceOf_Function::ICInstanceOf_Function(JitCode* stubCode, Shape* shape,
+ JSObject* prototypeObj, uint32_t slot)
+ : ICStub(InstanceOf_Function, stubCode),
+ shape_(shape),
+ prototypeObj_(prototypeObj),
+ slot_(slot)
+{ }
+
+ICSetProp_Native::ICSetProp_Native(JitCode* stubCode, ObjectGroup* group, Shape* shape,
+ uint32_t offset)
+ : ICUpdatedStub(SetProp_Native, stubCode),
+ group_(group),
+ shape_(shape),
+ offset_(offset)
+{ }
+
+ICSetProp_Native*
+ICSetProp_Native::Compiler::getStub(ICStubSpace* space)
+{
+ RootedObjectGroup group(cx, obj_->getGroup(cx));
+ if (!group)
+ return nullptr;
+
+ RootedShape shape(cx, LastPropertyForSetProp(obj_));
+ ICSetProp_Native* stub = newStub<ICSetProp_Native>(space, getStubCode(), group, shape, offset_);
+ if (!stub || !stub->initUpdatingChain(cx, space))
+ return nullptr;
+ return stub;
+}
+
+ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode* stubCode, ObjectGroup* group,
+ size_t protoChainDepth,
+ Shape* newShape,
+ ObjectGroup* newGroup,
+ uint32_t offset)
+ : ICUpdatedStub(SetProp_NativeAdd, stubCode),
+ group_(group),
+ newShape_(newShape),
+ newGroup_(newGroup),
+ offset_(offset)
+{
+ MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
+ extra_ = protoChainDepth;
+}
+
+template <size_t ProtoChainDepth>
+ICSetProp_NativeAddImpl<ProtoChainDepth>::ICSetProp_NativeAddImpl(JitCode* stubCode,
+ ObjectGroup* group,
+ Handle<ShapeVector> shapes,
+ Shape* newShape,
+ ObjectGroup* newGroup,
+ uint32_t offset)
+ : ICSetProp_NativeAdd(stubCode, group, ProtoChainDepth, newShape, newGroup, offset)
+{
+ MOZ_ASSERT(shapes.length() == NumShapes);
+ for (size_t i = 0; i < NumShapes; i++)
+ shapes_[i].init(shapes[i]);
+}
+
+ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext* cx, HandleObject obj,
+ HandleShape oldShape,
+ HandleObjectGroup oldGroup,
+ size_t protoChainDepth,
+ bool isFixedSlot,
+ uint32_t offset)
+ : ICStubCompiler(cx, ICStub::SetProp_NativeAdd, Engine::Baseline),
+ obj_(cx, obj),
+ oldShape_(cx, oldShape),
+ oldGroup_(cx, oldGroup),
+ protoChainDepth_(protoChainDepth),
+ isFixedSlot_(isFixedSlot),
+ offset_(offset)
+{
+ MOZ_ASSERT(protoChainDepth_ <= ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH);
+}
+
+ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard receiverGuard,
+ JSObject* holder, Shape* holderShape,
+ JSFunction* setter, uint32_t pcOffset)
+ : ICStub(kind, stubCode),
+ receiverGuard_(receiverGuard),
+ holder_(holder),
+ holderShape_(holderShape),
+ setter_(setter),
+ pcOffset_(pcOffset)
+{
+ MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
+}
+
+/* static */ ICSetProp_CallScripted*
+ICSetProp_CallScripted::Clone(JSContext* cx, ICStubSpace* space, ICStub*,
+ ICSetProp_CallScripted& other)
+{
+ return New<ICSetProp_CallScripted>(cx, space, other.jitCode(), other.receiverGuard(),
+ other.holder_, other.holderShape_, other.setter_,
+ other.pcOffset_);
+}
+
+/* static */ ICSetProp_CallNative*
+ICSetProp_CallNative::Clone(JSContext* cx, ICStubSpace* space, ICStub*, ICSetProp_CallNative& other)
+{
+ return New<ICSetProp_CallNative>(cx, space, other.jitCode(), other.receiverGuard(),
+ other.holder_, other.holderShape_, other.setter_,
+ other.pcOffset_);
+}
+
+ICCall_Scripted::ICCall_Scripted(JitCode* stubCode, ICStub* firstMonitorStub,
+ JSFunction* callee, JSObject* templateObject,
+ uint32_t pcOffset)
+ : ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub),
+ callee_(callee),
+ templateObject_(templateObject),
+ pcOffset_(pcOffset)
+{ }
+
+/* static */ ICCall_Scripted*
+ICCall_Scripted::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICCall_Scripted& other)
+{
+ return New<ICCall_Scripted>(cx, space, other.jitCode(), firstMonitorStub, other.callee_,
+ other.templateObject_, other.pcOffset_);
+}
+
+/* static */ ICCall_AnyScripted*
+ICCall_AnyScripted::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICCall_AnyScripted& other)
+{
+ return New<ICCall_AnyScripted>(cx, space, other.jitCode(), firstMonitorStub, other.pcOffset_);
+}
+
+ICCall_Native::ICCall_Native(JitCode* stubCode, ICStub* firstMonitorStub,
+ JSFunction* callee, JSObject* templateObject,
+ uint32_t pcOffset)
+ : ICMonitoredStub(ICStub::Call_Native, stubCode, firstMonitorStub),
+ callee_(callee),
+ templateObject_(templateObject),
+ pcOffset_(pcOffset)
+{
+#ifdef JS_SIMULATOR
+ // The simulator requires VM calls to be redirected to a special swi
+ // instruction to handle them. To make this work, we store the redirected
+ // pointer in the stub.
+ native_ = Simulator::RedirectNativeFunction(JS_FUNC_TO_DATA_PTR(void*, callee->native()),
+ Args_General3);
+#endif
+}
+
+/* static */ ICCall_Native*
+ICCall_Native::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICCall_Native& other)
+{
+ return New<ICCall_Native>(cx, space, other.jitCode(), firstMonitorStub, other.callee_,
+ other.templateObject_, other.pcOffset_);
+}
+
+ICCall_ClassHook::ICCall_ClassHook(JitCode* stubCode, ICStub* firstMonitorStub,
+ const Class* clasp, Native native,
+ JSObject* templateObject, uint32_t pcOffset)
+ : ICMonitoredStub(ICStub::Call_ClassHook, stubCode, firstMonitorStub),
+ clasp_(clasp),
+ native_(JS_FUNC_TO_DATA_PTR(void*, native)),
+ templateObject_(templateObject),
+ pcOffset_(pcOffset)
+{
+#ifdef JS_SIMULATOR
+ // The simulator requires VM calls to be redirected to a special swi
+ // instruction to handle them. To make this work, we store the redirected
+ // pointer in the stub.
+ native_ = Simulator::RedirectNativeFunction(native_, Args_General3);
+#endif
+}
+
+/* static */ ICCall_ClassHook*
+ICCall_ClassHook::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICCall_ClassHook& other)
+{
+ ICCall_ClassHook* res = New<ICCall_ClassHook>(cx, space, other.jitCode(), firstMonitorStub,
+ other.clasp(), nullptr, other.templateObject_,
+ other.pcOffset_);
+ if (res)
+ res->native_ = other.native();
+ return res;
+}
+
+/* static */ ICCall_ScriptedApplyArray*
+ICCall_ScriptedApplyArray::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICCall_ScriptedApplyArray& other)
+{
+ return New<ICCall_ScriptedApplyArray>(cx, space, other.jitCode(), firstMonitorStub,
+ other.pcOffset_);
+}
+
+/* static */ ICCall_ScriptedApplyArguments*
+ICCall_ScriptedApplyArguments::Clone(JSContext* cx,
+ ICStubSpace* space,
+ ICStub* firstMonitorStub,
+ ICCall_ScriptedApplyArguments& other)
+{
+ return New<ICCall_ScriptedApplyArguments>(cx, space, other.jitCode(), firstMonitorStub,
+ other.pcOffset_);
+}
+
+/* static */ ICCall_ScriptedFunCall*
+ICCall_ScriptedFunCall::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+ ICCall_ScriptedFunCall& other)
+{
+ return New<ICCall_ScriptedFunCall>(cx, space, other.jitCode(), firstMonitorStub,
+ other.pcOffset_);
+}
+
+//
+// Rest_Fallback
+//
+
+static bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback* stub,
+ MutableHandleValue res)
+{
+ unsigned numFormals = frame->numFormalArgs() - 1;
+ unsigned numActuals = frame->numActualArgs();
+ unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
+ Value* rest = frame->argv() + numFormals;
+
+ JSObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject,
+ ObjectGroup::NewArrayKind::UnknownIndex);
+ if (!obj)
+ return false;
+ res.setObject(*obj);
+ return true;
+}
+
+typedef bool (*DoRestFallbackFn)(JSContext*, BaselineFrame*, ICRest_Fallback*,
+ MutableHandleValue);
+static const VMFunction DoRestFallbackInfo =
+ FunctionInfo<DoRestFallbackFn>(DoRestFallback, "DoRestFallback", TailCall);
+
+bool
+ICRest_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+ MOZ_ASSERT(engine_ == Engine::Baseline);
+
+ EmitRestoreTailCallReg(masm);
+
+ masm.push(ICStubReg);
+ pushStubPayload(masm, R0.scratchReg());
+
+ return tailCallVM(DoRestFallbackInfo, masm);
+}
+
+} // namespace jit
+} // namespace js