Bug 1170372 - Use unboxed arrays for Array() and other functions keyed to allocation sites, r=jandem.

This commit is contained in:
Brian Hackett 2015-06-10 09:42:22 -07:00
parent d225788d50
commit a6cbfd3be7
38 changed files with 718 additions and 867 deletions

View File

@ -10989,7 +10989,7 @@ class CGDOMJSProxyHandler_getElements(ClassMethod):
'jsvalRef': 'temp',
'jsvalHandle': '&temp',
'obj': 'proxy',
'successCode': ("adder->append(cx, temp);\n"
'successCode': ("if (!adder->append(cx, temp)) return false;\n"
"continue;\n")
}
get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define()

View File

@ -406,7 +406,7 @@ class JS_FRIEND_API(ElementAdder)
GetBehavior getBehavior() const { return getBehavior_; }
void append(JSContext* cx, JS::HandleValue v);
bool append(JSContext* cx, JS::HandleValue v);
void appendHole();
};

View File

@ -253,8 +253,7 @@ function ArrayMap(callbackfn/*, thisArg*/) {
if (k in O) {
/* Step c.i-iii. */
var mappedValue = callFunction(callbackfn, T, O[k], k, O);
// UnsafePutElements doesn't invoke setters, so we can use it here.
UnsafePutElements(A, k, mappedValue);
_DefineDataProperty(A, k, mappedValue);
}
}
@ -718,9 +717,6 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn));
var T = thisArg;
// All elements defined by this algorithm have the same attrs:
var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE;
// Steps 4-5.
var usingIterator = GetMethod(items, std_iterator);
@ -757,7 +753,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue;
// Steps 6.g.ix-xi.
_DefineDataProperty(A, k++, mappedValue, attrs);
_DefineDataProperty(A, k++, mappedValue);
}
}
@ -782,7 +778,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue;
// Steps 16.f-g.
_DefineDataProperty(A, k, mappedValue, attrs);
_DefineDataProperty(A, k, mappedValue);
}
// Steps 17-18.

View File

@ -929,15 +929,6 @@ function GetNumberOption(options, property, minimum, maximum, fallback) {
/********** Property access for Intl objects **********/
/**
* Set a normal public property p of o to value v, but use Object.defineProperty
* to avoid interference from setters on Object.prototype.
*/
function defineProperty(o, p, v) {
_DefineDataProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
}
/**
* Weak map used to track the initialize-as-Intl status (and, if an object has
* been so initialized, the Intl-specific internal properties) of all objects.
@ -1458,7 +1449,7 @@ function Intl_Collator_resolvedOptions() {
for (var i = 0; i < relevantExtensionKeys.length; i++) {
var key = relevantExtensionKeys[i];
var property = (key === "co") ? "collation" : collatorKeyMappings[key].property;
defineProperty(result, property, internals[property]);
_DefineDataProperty(result, property, internals[property]);
}
return result;
}
@ -1901,7 +1892,7 @@ function Intl_NumberFormat_resolvedOptions() {
for (var i = 0; i < optionalProperties.length; i++) {
var p = optionalProperties[i];
if (callFunction(std_Object_hasOwnProperty, internals, p))
defineProperty(result, p, internals[p]);
_DefineDataProperty(result, p, internals[p]);
}
return result;
}
@ -2366,17 +2357,17 @@ function ToDateTimeOptions(options, required, defaults) {
// the Throw parameter, while Object.defineProperty uses true. For the
// calls here, the difference doesn't matter because we're adding
// properties to a new object.
defineProperty(options, "year", "numeric");
defineProperty(options, "month", "numeric");
defineProperty(options, "day", "numeric");
_DefineDataProperty(options, "year", "numeric");
_DefineDataProperty(options, "month", "numeric");
_DefineDataProperty(options, "day", "numeric");
}
// Step 8.
if (needDefaults && (defaults === "time" || defaults === "all")) {
// See comment for step 7.
defineProperty(options, "hour", "numeric");
defineProperty(options, "minute", "numeric");
defineProperty(options, "second", "numeric");
_DefineDataProperty(options, "hour", "numeric");
_DefineDataProperty(options, "minute", "numeric");
_DefineDataProperty(options, "second", "numeric");
}
// Step 9.
@ -2680,11 +2671,11 @@ function resolveICUPattern(pattern, result) {
// skip other pattern characters and literal text
}
if (callFunction(std_Object_hasOwnProperty, icuPatternCharToComponent, c))
defineProperty(result, icuPatternCharToComponent[c], value);
_DefineDataProperty(result, icuPatternCharToComponent[c], value);
if (c === "h" || c === "K")
defineProperty(result, "hour12", true);
_DefineDataProperty(result, "hour12", true);
else if (c === "H" || c === "k")
defineProperty(result, "hour12", false);
_DefineDataProperty(result, "hour12", false);
}
}
}

View File

@ -14,9 +14,7 @@
AssertionFailed: false,
MakeConstructible: false, DecompileArg: false,
RuntimeDefaultLocale: false,
ParallelDo: false, ParallelSlices: false, NewDenseArray: false,
UnsafePutElements: false,
ParallelTestsShouldPass: false,
NewDenseArray: false,
Dump: false,
callFunction: false,
TO_UINT32: false,

View File

@ -9306,8 +9306,8 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
}
static bool
GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc,
Native native, const CallArgs& args, MutableHandleObject res)
GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
MutableHandleObject res)
{
// Check for natives to which template objects can be attached. This is
// done to provide templates to Ion for inlining these natives later on.
@ -9321,27 +9321,16 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc,
count = args.length();
else if (args.length() == 1 && args[0].isInt32() && args[0].toInt32() >= 0)
count = args[0].toInt32();
res.set(NewDenseUnallocatedArray(cx, count, nullptr, TenuredObject));
if (!res)
return false;
ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
if (!group)
return false;
res->setGroup(group);
return true;
}
if (native == intrinsic_NewDenseArray) {
res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
if (!res)
return false;
ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
if (!group)
return false;
res->setGroup(group);
return true;
if (count <= ArrayObject::EagerAllocationMaxLength) {
// With this and other array templates, set forceAnalyze so that we
// don't end up with a template whose structure might change later.
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject,
/* forceAnalyze = */ true));
if (!res)
return false;
return true;
}
}
if (native == js::array_concat || native == js::array_slice) {
@ -9354,14 +9343,10 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc,
}
if (native == js::str_split && args.length() == 1 && args[0].isString()) {
res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject,
/* forceAnalyze = */ true));
if (!res)
return false;
ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
if (!group)
return false;
res->setGroup(group);
return true;
}
@ -9655,7 +9640,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
RootedObject templateObject(cx);
if (MOZ_LIKELY(!isSpread)) {
CallArgs args = CallArgsFromVp(argc, vp);
if (!GetTemplateObjectForNative(cx, script, pc, fun->native(), args, &templateObject))
if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject))
return false;
}
@ -9677,24 +9662,16 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
}
static bool
CopyArray(JSContext* cx, HandleArrayObject obj, MutableHandleValue result)
CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result)
{
MOZ_ASSERT(obj->is<ArrayObject>());
uint32_t length = obj->as<ArrayObject>().length();
MOZ_ASSERT(obj->getDenseInitializedLength() == length);
RootedObjectGroup group(cx, obj->getGroup(cx));
if (!group)
uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject,
/* forceAnalyze = */ true);
if (!nobj)
return false;
CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
RootedArrayObject newObj(cx, NewDenseFullyAllocatedArray(cx, length, nullptr, TenuredObject));
if (!newObj)
return false;
newObj->setGroup(group);
newObj->setDenseInitializedLength(length);
newObj->initDenseElements(0, obj->getDenseElements(), length);
result.setObject(*newObj);
result.setObject(*nobj);
return true;
}
@ -9722,7 +9699,7 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
RootedString thisString(cx, thisv.toString());
RootedString argString(cx, args[0].toString());
RootedArrayObject obj(cx, &res.toObject().as<ArrayObject>());
RootedObject obj(cx, &res.toObject());
RootedValue arr(cx);
// Copy the array before storing in stub.
@ -9730,14 +9707,17 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
return false;
// Atomize all elements of the array.
RootedArrayObject arrObj(cx, &arr.toObject().as<ArrayObject>());
uint32_t initLength = arrObj->length();
RootedObject arrObj(cx, &arr.toObject());
uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj);
for (uint32_t i = 0; i < initLength; i++) {
JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString());
JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString());
if (!str)
return false;
arrObj->setDenseElement(i, StringValue(str));
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(),
@ -10595,7 +10575,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
return true;
}
typedef bool (*CopyArrayFn)(JSContext*, HandleArrayObject, MutableHandleValue);
typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue);
static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray);
bool

View File

@ -609,6 +609,11 @@ class ICNewArray_Fallback : public ICFallbackStub
HeapPtrObjectGroup& templateGroup() {
return templateGroup_;
}
void setTemplateGroup(ObjectGroup* group) {
templateObject_ = nullptr;
templateGroup_ = group;
}
};
class ICNewObject_Fallback : public ICFallbackStub
@ -4914,10 +4919,10 @@ class ICCall_StringSplit : public ICMonitoredStub
uint32_t pcOffset_;
HeapPtrString expectedThis_;
HeapPtrString expectedArg_;
HeapPtrArrayObject templateObject_;
HeapPtrObject templateObject_;
ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* thisString,
JSString* argString, ArrayObject* templateObject)
JSString* argString, JSObject* templateObject)
: ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub),
pcOffset_(pcOffset), expectedThis_(thisString), expectedArg_(argString),
templateObject_(templateObject)
@ -4944,7 +4949,7 @@ class ICCall_StringSplit : public ICMonitoredStub
return expectedArg_;
}
HeapPtrArrayObject& templateObject() {
HeapPtrObject& templateObject() {
return templateObject_;
}
@ -4954,7 +4959,7 @@ class ICCall_StringSplit : public ICMonitoredStub
uint32_t pcOffset_;
RootedString expectedThis_;
RootedString expectedArg_;
RootedArrayObject templateObject_;
RootedObject templateObject_;
bool generateStubCode(MacroAssembler& masm);
@ -4971,7 +4976,7 @@ class ICCall_StringSplit : public ICMonitoredStub
pcOffset_(pcOffset),
expectedThis_(cx, thisString),
expectedArg_(cx, argString),
templateObject_(cx, &templateObject.toObject().as<ArrayObject>())
templateObject_(cx, &templateObject.toObject())
{ }
ICStub* getStub(ICStubSpace* space) {

View File

@ -521,7 +521,7 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode* pc, Native native)
bool
BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg,
NativeObject** objOut)
JSObject** objOut)
{
if (!hasBaselineScript())
return false;
@ -733,6 +733,7 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
return MIRType_Value;
case ICStub::GetProp_ArrayLength:
case ICStub::GetProp_UnboxedArrayLength:
case ICStub::GetProp_Native:
case ICStub::GetProp_NativeDoesNotExist:
case ICStub::GetProp_NativePrototype:
@ -750,6 +751,7 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
case ICStub::GetElem_String:
case ICStub::GetElem_Dense:
case ICStub::GetElem_TypedArray:
case ICStub::GetElem_UnboxedArray:
stubType = MIRType_Object;
break;

View File

@ -113,7 +113,7 @@ class BaselineInspector
bool hasSeenNonStringIterMore(jsbytecode* pc);
bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg,
NativeObject** objOut);
JSObject** objOut);
JSObject* getTemplateObject(jsbytecode* pc);
JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native);
JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp);

View File

@ -4154,9 +4154,20 @@ typedef JSObject* (*NewArrayOperationFn)(JSContext*, HandleScript, jsbytecode*,
static const VMFunction NewArrayOperationInfo =
FunctionInfo<NewArrayOperationFn>(NewArrayOperation);
typedef ArrayObject* (*NewDenseArrayFn)(ExclusiveContext*, uint32_t, HandleObjectGroup,
AllocatingBehaviour, bool);
static const VMFunction NewDenseArrayInfo = FunctionInfo<NewDenseArrayFn>(NewDenseArray);
static JSObject*
NewArrayWithGroup(JSContext* cx, uint32_t length, HandleObjectGroup group,
bool convertDoubleElements)
{
JSObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
if (!res)
return nullptr;
if (convertDoubleElements)
res->as<ArrayObject>().setShouldConvertDoubleElements();
return res;
}
typedef JSObject* (*NewArrayWithGroupFn)(JSContext*, uint32_t, HandleObjectGroup, bool);
static const VMFunction NewArrayWithGroupInfo = FunctionInfo<NewArrayWithGroupFn>(NewArrayWithGroup);
void
CodeGenerator::visitNewArrayCallVM(LNewArray* lir)
@ -4168,13 +4179,12 @@ CodeGenerator::visitNewArrayCallVM(LNewArray* lir)
JSObject* templateObject = lir->mir()->templateObject();
if (templateObject && !templateObject->is<UnboxedArrayObject>()) {
if (templateObject) {
pushArg(Imm32(lir->mir()->convertDoubleElements()));
pushArg(Imm32(lir->mir()->allocatingBehaviour()));
pushArg(ImmGCPtr(templateObject->group()));
pushArg(Imm32(lir->mir()->count()));
callVM(NewDenseArrayInfo, lir);
callVM(NewArrayWithGroupInfo, lir);
} else {
pushArg(Imm32(GenericObject));
pushArg(Imm32(lir->mir()->count()));
@ -4297,7 +4307,7 @@ CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir)
masm.bind(ool->rejoin());
}
typedef ArrayObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length);
typedef JSObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length);
static const VMFunction ArrayConstructorOneArgInfo =
FunctionInfo<ArrayConstructorOneArgFn>(ArrayConstructorOneArg);
@ -4308,32 +4318,47 @@ CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir)
Register objReg = ToRegister(lir->output());
Register tempReg = ToRegister(lir->temp());
ArrayObject* templateObject = lir->mir()->templateObject();
JSObject* templateObject = lir->mir()->templateObject();
gc::InitialHeap initialHeap = lir->mir()->initialHeap();
OutOfLineCode* ool = oolCallVM(ArrayConstructorOneArgInfo, lir,
(ArgList(), ImmGCPtr(templateObject->group()), lengthReg),
StoreRegisterTo(objReg));
size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
size_t inlineLength = numSlots >= ObjectElements::VALUES_PER_HEADER
? numSlots - ObjectElements::VALUES_PER_HEADER
: 0;
bool canInline = true;
size_t inlineLength = 0;
if (templateObject->is<ArrayObject>()) {
if (templateObject->as<ArrayObject>().hasFixedElements()) {
size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
} else {
canInline = false;
}
} else {
if (templateObject->as<UnboxedArrayObject>().hasInlineElements()) {
size_t nbytes =
templateObject->tenuredSizeOfThis() - UnboxedArrayObject::offsetOfInlineElements();
inlineLength = nbytes / templateObject->as<UnboxedArrayObject>().elementSize();
} else {
canInline = false;
}
}
// Try to do the allocation inline if the template object is big enough
// for the length in lengthReg. If the length is bigger we could still
// use the template object and not allocate the elements, but it's more
// efficient to do a single big allocation than (repeatedly) reallocating
// the array later on when filling it.
if (!templateObject->isSingleton() && templateObject->length() <= inlineLength)
masm.branch32(Assembler::Above, lengthReg, Imm32(templateObject->length()), ool->entry());
else
if (canInline) {
// Try to do the allocation inline if the template object is big enough
// for the length in lengthReg. If the length is bigger we could still
// use the template object and not allocate the elements, but it's more
// efficient to do a single big allocation than (repeatedly) reallocating
// the array later on when filling it.
masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength), ool->entry());
masm.createGCObject(objReg, tempReg, templateObject, initialHeap, ool->entry());
size_t lengthOffset = NativeObject::offsetOfFixedElements() + ObjectElements::offsetOfLength();
masm.store32(lengthReg, Address(objReg, lengthOffset));
} else {
masm.jump(ool->entry());
masm.createGCObject(objReg, tempReg, templateObject, initialHeap, ool->entry());
size_t lengthOffset = NativeObject::offsetOfFixedElements() + ObjectElements::offsetOfLength();
masm.store32(lengthReg, Address(objReg, lengthOffset));
}
masm.bind(ool->rejoin());
}
@ -6398,6 +6423,25 @@ CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArra
masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
}
void
CodeGenerator::visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir)
{
Register obj = ToRegister(lir->object());
Int32Key key = ToInt32Key(lir->length());
Register temp = ToRegister(lir->temp());
Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
masm.load32(initLengthAddr, temp);
masm.and32(Imm32(UnboxedArrayObject::CapacityMask), temp);
if (key.isRegister())
masm.or32(key.reg(), temp);
else
masm.or32(Imm32(key.constant()), temp);
masm.store32(temp, initLengthAddr);
}
void
CodeGenerator::visitNotO(LNotO* lir)
{
@ -9360,7 +9404,7 @@ CodeGenerator::visitInArray(LInArray* lir)
}
masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength);
if (mir->needsHoleCheck()) {
if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) {
NativeObject::elementsSizeMustNotOverflow();
Address address = Address(elements, index * sizeof(Value));
masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
@ -9373,7 +9417,7 @@ CodeGenerator::visitInArray(LInArray* lir)
failedInitLength = &negativeIntCheck;
masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
if (mir->needsHoleCheck()) {
if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) {
BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight);
masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
}

View File

@ -198,6 +198,7 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitUnboxedArrayLength(LUnboxedArrayLength* lir);
void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir);
void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir);
void visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir);
void visitNotO(LNotO* ins);
void visitNotV(LNotV* ins);
void visitBoundsCheck(LBoundsCheck* lir);

View File

@ -6504,8 +6504,7 @@ IonBuilder::jsop_newarray(uint32_t count)
}
current->add(templateConst);
MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst,
heap, NewArray_FullyAllocating, pc);
MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, heap, pc);
current->add(ins);
current->push(ins);
@ -6623,7 +6622,15 @@ IonBuilder::jsop_initelem_array()
return resumeAfter(store);
}
MConstant* id = MConstant::New(alloc(), Int32Value(GET_UINT24(pc)));
return initializeArrayElement(obj, GET_UINT24(pc), value, unboxedType, /* addResumePoint = */ true);
}
bool
IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
JSValueType unboxedType,
bool addResumePointAndIncrementInitializedLength)
{
MConstant* id = MConstant::New(alloc(), Int32Value(index));
current->add(id);
// Get the elements vector.
@ -6634,11 +6641,13 @@ IonBuilder::jsop_initelem_array()
// Note: storeUnboxedValue takes care of any post barriers on the value.
storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false);
MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
current->add(increment);
if (addResumePointAndIncrementInitializedLength) {
MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
current->add(increment);
if (!resumeAfter(increment))
return false;
if (!resumeAfter(increment))
return false;
}
} else {
if (NeedsPostBarrier(info(), value))
current->add(MPostWriteBarrier::New(alloc(), obj, value));
@ -6654,14 +6663,16 @@ IonBuilder::jsop_initelem_array()
/* needsHoleCheck = */ false);
current->add(store);
// Update the initialized length. (The template object for this array has
// the array's ultimate length, so the length field is already correct: no
// updating needed.)
MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
current->add(initLength);
if (addResumePointAndIncrementInitializedLength) {
// Update the initialized length. (The template object for this
// array has the array's ultimate length, so the length field is
// already correct: no updating needed.)
MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
current->add(initLength);
if (!resumeAfter(initLength))
return false;
if (!resumeAfter(initLength))
return false;
}
}
return true;
@ -8530,12 +8541,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
// Note: to help GVN, use the original MElements instruction and not
// MConvertElementsToDoubles as operand. This is fine because converting
// elements to double does not change the initialized length.
MInstruction* initLength;
if (unboxedType != JSVAL_TYPE_MAGIC)
initLength = MUnboxedArrayInitializedLength::New(alloc(), obj);
else
initLength = MInitializedLength::New(alloc(), elements);
current->add(initLength);
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
// If we can load the element as a definite double, make sure to check that
// the array has been converted to homogenous doubles first.
@ -8825,9 +8831,13 @@ IonBuilder::jsop_setelem()
if (!setElemTryTypedArray(&emitted, object, index, value) || emitted)
return emitted;
trackOptimizationAttempt(TrackedStrategy::SetElem_Dense);
if (!setElemTryDense(&emitted, object, index, value) || emitted)
return emitted;
{
trackOptimizationAttempt(TrackedStrategy::SetElem_Dense);
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
bool writeHole = icInspect.sawOOBDenseWrite();
if (!setElemTryDense(&emitted, object, index, value, writeHole) || emitted)
return emitted;
}
trackOptimizationAttempt(TrackedStrategy::SetElem_Arguments);
if (!setElemTryArguments(&emitted, object, index, value) || emitted)
@ -9020,7 +9030,7 @@ IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object,
}
// Emit typed setelem variant.
if (!jsop_setelem_typed(arrayType, SetElem_Normal, object, index, value))
if (!jsop_setelem_typed(arrayType, object, index, value))
return false;
trackOptimizationSuccess();
@ -9030,7 +9040,7 @@ IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object,
bool
IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value)
MDefinition* index, MDefinition* value, bool writeHole)
{
MOZ_ASSERT(*emitted == false);
@ -9073,7 +9083,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
}
// Emit dense setelem variant.
if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value, unboxedType))
if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole))
return false;
trackOptimizationSuccess();
@ -9159,9 +9169,8 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
bool
IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
SetElemSafety safety,
MDefinition* obj, MDefinition* id, MDefinition* value,
JSValueType unboxedType)
JSValueType unboxedType, bool writeHole)
{
MIRType elementType = MIRType_None;
if (unboxedType == JSVAL_TYPE_MAGIC)
@ -9213,20 +9222,12 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
MOZ_CRASH("Unknown double conversion");
}
bool writeHole = false;
if (safety == SetElem_Normal) {
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
writeHole = icInspect.sawOOBDenseWrite();
}
// Use MStoreElementHole if this SETELEM has written to out-of-bounds
// indexes in the past. Otherwise, use MStoreElement so that we can hoist
// the initialized length and bounds check.
MInstruction* store;
MStoreElementCommon *common = nullptr;
if (writeHole && writeOutOfBounds) {
MOZ_ASSERT(safety == SetElem_Normal);
MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
store = ins;
common = ins;
@ -9234,20 +9235,10 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
current->add(ins);
current->push(value);
} else {
MInstruction* initLength;
if (unboxedType != JSVAL_TYPE_MAGIC)
initLength = MUnboxedArrayInitializedLength::New(alloc(), obj);
else
initLength = MInitializedLength::New(alloc(), elements);
current->add(initLength);
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
bool needsHoleCheck;
if (safety == SetElem_Normal) {
id = addBoundsCheck(id, initLength);
needsHoleCheck = !packed && !writeOutOfBounds;
} else {
needsHoleCheck = false;
}
id = addBoundsCheck(id, initLength);
bool needsHoleCheck = !packed && !writeOutOfBounds;
if (unboxedType != JSVAL_TYPE_MAGIC) {
store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue);
@ -9259,8 +9250,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
current->add(store);
}
if (safety == SetElem_Normal)
current->push(value);
current->push(value);
}
if (!resumeAfter(store))
@ -9280,16 +9270,11 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
bool
IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety,
IonBuilder::jsop_setelem_typed(Scalar::Type arrayType,
MDefinition* obj, MDefinition* id, MDefinition* value)
{
bool expectOOB;
if (safety == SetElem_Normal) {
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
expectOOB = icInspect.sawOOBTypedArrayWrite();
} else {
expectOOB = false;
}
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
bool expectOOB = icInspect.sawOOBTypedArrayWrite();
if (expectOOB)
spew("Emitting OOB TypedArray SetElem");
@ -9302,9 +9287,7 @@ IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety,
// Get length, bounds-check, then get elements, and add all instructions.
MInstruction* length;
MInstruction* elements;
BoundsChecking checking = (!expectOOB && safety == SetElem_Normal)
? DoBoundsCheck
: SkipBoundsCheck;
BoundsChecking checking = expectOOB ? SkipBoundsCheck : DoBoundsCheck;
addTypedArrayLengthAndData(obj, checking, &id, &length, &elements);
// Clamp value to [0, 255] for Uint8ClampedArray.
@ -9325,31 +9308,11 @@ IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety,
}
current->add(ins);
if (safety == SetElem_Normal)
current->push(value);
current->push(value);
return resumeAfter(ins);
}
bool
IonBuilder::jsop_setelem_typed_object(Scalar::Type arrayType, SetElemSafety safety,
MDefinition* object, MDefinition* index, MDefinition* value)
{
MOZ_ASSERT(safety == SetElem_Unsafe); // Can be fixed, but there's been no reason to as of yet
MInstruction* intIndex = MToInt32::New(alloc(), index);
current->add(intIndex);
size_t elemSize = ScalarTypeDescr::alignment(arrayType);
LinearSum byteOffset(alloc());
if (!byteOffset.add(intIndex, elemSize))
setForceAbort();
return storeScalarTypedObjectValue(object, byteOffset, arrayType, value);
}
bool
IonBuilder::jsop_length()
{
@ -9511,8 +9474,7 @@ IonBuilder::jsop_rest()
current->add(templateConst);
MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst,
templateObject->group()->initialHeap(constraints()),
NewArray_FullyAllocating, pc);
templateObject->group()->initialHeap(constraints()), pc);
current->add(array);
if (numRest == 0) {
@ -12396,12 +12358,21 @@ IonBuilder::jsop_in()
MDefinition* obj = current->peek(-1);
MDefinition* id = current->peek(-2);
if (!shouldAbortOnPreliminaryGroups(obj) &&
ElementAccessIsDenseNative(constraints(), obj, id) &&
!ElementAccessHasExtraIndexedProperty(constraints(), obj))
{
return jsop_in_dense();
}
do {
if (shouldAbortOnPreliminaryGroups(obj))
break;
JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
if (unboxedType == JSVAL_TYPE_MAGIC) {
if (!ElementAccessIsDenseNative(constraints(), obj, id))
break;
}
if (ElementAccessHasExtraIndexedProperty(constraints(), obj))
break;
return jsop_in_dense(unboxedType);
} while (false);
current->pop();
current->pop();
@ -12414,7 +12385,7 @@ IonBuilder::jsop_in()
}
bool
IonBuilder::jsop_in_dense()
IonBuilder::jsop_in_dense(JSValueType unboxedType)
{
MDefinition* obj = current->pop();
MDefinition* id = current->pop();
@ -12427,11 +12398,10 @@ IonBuilder::jsop_in_dense()
id = idInt32;
// Get the elements vector.
MElements* elements = MElements::New(alloc(), obj);
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
current->add(elements);
MInitializedLength* initLength = MInitializedLength::New(alloc(), elements);
current->add(initLength);
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
// If there are no holes, speculate the InArray check will not fail.
if (!needsHoleCheck && !failedBoundsCheck_) {
@ -12440,7 +12410,8 @@ IonBuilder::jsop_in_dense()
}
// Check if id < initLength and elem[id] not a hole.
MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck);
MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck,
unboxedType);
current->add(ins);
current->push(ins);
@ -13053,6 +13024,37 @@ IonBuilder::constantInt(int32_t i)
return constant(Int32Value(i));
}
MInstruction*
IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType)
{
MInstruction* res;
if (unboxedType != JSVAL_TYPE_MAGIC)
res = MUnboxedArrayInitializedLength::New(alloc(), obj);
else
res = MInitializedLength::New(alloc(), elements);
current->add(res);
return res;
}
MInstruction*
IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count)
{
MOZ_ASSERT(count);
MInstruction* res;
if (unboxedType != JSVAL_TYPE_MAGIC) {
res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count)));
} else {
// MSetInitializedLength takes the index of the last element, rather
// than the count itself.
MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
current->add(elements);
res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
}
current->add(res);
return res;
}
MDefinition*
IonBuilder::getCallee()
{

View File

@ -46,16 +46,6 @@ class IonBuilder
ControlStatus_None // No control flow.
};
enum SetElemSafety {
// Normal write like a[b] = c.
SetElem_Normal,
// Write due to UnsafePutElements:
// - assumed to be in bounds,
// - not checked for data races
SetElem_Unsafe,
};
struct DeferredEdge : public TempObject
{
MBasicBlock* block;
@ -352,6 +342,9 @@ class IonBuilder
MConstant* constant(const Value& v);
MConstant* constantInt(int32_t i);
MInstruction* initializedLength(MDefinition* obj, MDefinition* elements,
JSValueType unboxedType);
MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count);
// Improve the type information at tests
bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test);
@ -548,7 +541,7 @@ class IonBuilder
bool setElemTryTypedStatic(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value);
bool setElemTryDense(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value);
MDefinition* index, MDefinition* value, bool writeHole);
bool setElemTryArguments(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value);
bool setElemTryCache(bool* emitted, MDefinition* object,
@ -566,6 +559,9 @@ class IonBuilder
MDefinition* value,
TypedObjectPrediction elemTypeReprs,
int32_t elemSize);
bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
JSValueType unboxedType,
bool addResumePointAndIncrementInitializedLength);
// jsop_getelem() helpers.
bool getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index);
@ -661,15 +657,10 @@ class IonBuilder
bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType);
bool jsop_setelem();
bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
SetElemSafety safety,
MDefinition* object, MDefinition* index, MDefinition* value,
JSValueType unboxedType);
JSValueType unboxedType, bool writeHole);
bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
SetElemSafety safety,
MDefinition* object, MDefinition* index, MDefinition* value);
bool jsop_setelem_typed_object(ScalarTypeDescr::Type arrayType,
SetElemSafety safety,
MDefinition* object, MDefinition* index, MDefinition* value);
bool jsop_length();
bool jsop_length_fastPath();
bool jsop_arguments();
@ -702,7 +693,7 @@ class IonBuilder
bool jsop_isnoiter();
bool jsop_iterend();
bool jsop_in();
bool jsop_in_dense();
bool jsop_in_dense(JSValueType unboxedType);
bool jsop_instanceof();
bool jsop_getaliasedvar(ScopeCoordinate sc);
bool jsop_setaliasedvar(ScopeCoordinate sc);
@ -781,8 +772,9 @@ class IonBuilder
InliningStatus inlineRegExpExec(CallInfo& callInfo);
InliningStatus inlineRegExpTest(CallInfo& callInfo);
// Object natives.
// Object natives and intrinsics.
InliningStatus inlineObjectCreate(CallInfo& callInfo);
InliningStatus inlineDefineDataProperty(CallInfo& callInfo);
// Atomics natives.
InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo);
@ -791,14 +783,6 @@ class IonBuilder
InliningStatus inlineAtomicsFence(CallInfo& callInfo);
InliningStatus inlineAtomicsBinop(CallInfo& callInfo, JSFunction* target);
// Array intrinsics.
InliningStatus inlineUnsafePutElements(CallInfo& callInfo);
bool inlineUnsafeSetDenseArrayElement(CallInfo& callInfo, uint32_t base);
bool inlineUnsafeSetTypedArrayElement(CallInfo& callInfo, uint32_t base,
ScalarTypeDescr::Type arrayType);
bool inlineUnsafeSetTypedObjectArrayElement(CallInfo& callInfo, uint32_t base,
ScalarTypeDescr::Type arrayType);
// Slot intrinsics.
InliningStatus inlineUnsafeSetReservedSlot(CallInfo& callInfo);
InliningStatus inlineUnsafeGetReservedSlot(CallInfo& callInfo,
@ -815,8 +799,6 @@ class IonBuilder
// TypedObject intrinsics and natives.
InliningStatus inlineObjectIsTypeDescr(CallInfo& callInfo);
InliningStatus inlineSetTypedObjectOffset(CallInfo& callInfo);
bool elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id,
ScalarTypeDescr::Type* arrayType);
InliningStatus inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* target);
// SIMD intrinsics and natives.

View File

@ -4198,6 +4198,30 @@ class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1,
}
};
class LSetUnboxedArrayInitializedLength : public LInstructionHelper<0, 2, 1>
{
public:
LIR_HEADER(SetUnboxedArrayInitializedLength)
explicit LSetUnboxedArrayInitializedLength(const LAllocation& object,
const LAllocation& length,
const LDefinition& temp) {
setOperand(0, object);
setOperand(1, length);
setTemp(0, temp);
}
const LAllocation* object() {
return getOperand(0);
}
const LAllocation* length() {
return getOperand(1);
}
const LDefinition* temp() {
return getTemp(0);
}
};
// Load the length from an elements header.
class LArrayLength : public LInstructionHelper<1, 1, 0>
{

View File

@ -217,6 +217,7 @@
_(UnboxedArrayLength) \
_(UnboxedArrayInitializedLength) \
_(IncrementUnboxedArrayInitializedLength) \
_(SetUnboxedArrayInitializedLength) \
_(BoundsCheck) \
_(BoundsCheckRange) \
_(BoundsCheckLower) \

View File

@ -2518,6 +2518,14 @@ LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArray
add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins);
}
void
LIRGenerator::visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins)
{
add(new(alloc()) LSetUnboxedArrayInitializedLength(useRegister(ins->object()),
useRegisterOrConstant(ins->length()),
temp()), ins);
}
void
LIRGenerator::visitNot(MNot* ins)
{

View File

@ -190,6 +190,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitUnboxedArrayLength(MUnboxedArrayLength* ins);
void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins);
void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins);
void visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins);
void visitNot(MNot* ins);
void visitBoundsCheck(MBoundsCheck* ins);
void visitBoundsCheckLower(MBoundsCheckLower* ins);

View File

@ -21,6 +21,7 @@
#include "vm/NativeObject-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/UnboxedObject-inl.h"
using mozilla::ArrayLength;
@ -184,10 +185,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
// Object natives.
if (native == obj_create)
return inlineObjectCreate(callInfo);
// Array intrinsics.
if (native == intrinsic_UnsafePutElements)
return inlineUnsafePutElements(callInfo);
if (native == intrinsic_DefineDataProperty)
return inlineDefineDataProperty(callInfo);
// Slot intrinsics.
if (native == intrinsic_UnsafeSetReservedSlot)
@ -524,21 +523,18 @@ IonBuilder::InliningStatus
IonBuilder::inlineArray(CallInfo& callInfo)
{
uint32_t initLength = 0;
AllocatingBehaviour allocating = NewArray_Unallocating;
JSObject* templateObject = inspector->getTemplateObjectForNative(pc, ArrayConstructor);
if (!templateObject) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
return InliningStatus_NotInlined;
}
ArrayObject* templateArray = &templateObject->as<ArrayObject>();
// Multiple arguments imply array initialization, not just construction.
if (callInfo.argc() >= 2) {
initLength = callInfo.argc();
allocating = NewArray_FullyAllocating;
TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateArray);
TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateObject);
if (!key->unknownProperties()) {
HeapTypeSetKey elemTypes = key->property(JSID_VOID);
@ -561,8 +557,8 @@ IonBuilder::inlineArray(CallInfo& callInfo)
if (!arg->isConstantValue()) {
callInfo.setImplicitlyUsedUnchecked();
MNewArrayDynamicLength* ins =
MNewArrayDynamicLength::New(alloc(), constraints(), templateArray,
templateArray->group()->initialHeap(constraints()),
MNewArrayDynamicLength::New(alloc(), constraints(), templateObject,
templateObject->group()->initialHeap(constraints()),
arg);
current->add(ins);
current->push(ins);
@ -580,63 +576,34 @@ IonBuilder::inlineArray(CallInfo& callInfo)
// Make sure initLength matches the template object's length. This is
// not guaranteed to be the case, for instance if we're inlining the
// MConstant may come from an outer script.
if (initLength != templateArray->as<ArrayObject>().length())
if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject))
return InliningStatus_NotInlined;
// Don't inline large allocations.
if (initLength > ArrayObject::EagerAllocationMaxLength)
return InliningStatus_NotInlined;
allocating = NewArray_FullyAllocating;
}
callInfo.setImplicitlyUsedUnchecked();
MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateArray);
MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
current->add(templateConst);
MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
templateArray->group()->initialHeap(constraints()),
allocating, pc);
templateObject->group()->initialHeap(constraints()), pc);
current->add(ins);
current->push(ins);
if (callInfo.argc() >= 2) {
// Get the elements vector.
MElements* elements = MElements::New(alloc(), ins);
current->add(elements);
// Store all values, no need to initialize the length after each as
// jsop_initelem_array is doing because we do not expect to bailout
// because the memory is supposed to be allocated by now.
MConstant* id = nullptr;
JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
for (uint32_t i = 0; i < initLength; i++) {
id = MConstant::New(alloc(), Int32Value(i));
current->add(id);
MDefinition* value = callInfo.getArg(i);
if (ins->convertDoubleElements()) {
MInstruction* valueDouble = MToDouble::New(alloc(), value);
current->add(valueDouble);
value = valueDouble;
}
// There is normally no need for a post barrier on these writes
// because the new array will be in the nursery. However, this
// assumption is volated if we specifically requested pre-tenuring.
if (ins->initialHeap() == gc::TenuredHeap)
current->add(MPostWriteBarrier::New(alloc(), ins, value));
MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
/* needsHoleCheck = */ false);
current->add(store);
if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false))
return InliningStatus_Error;
}
// Update the length.
MSetInitializedLength* length = MSetInitializedLength::New(alloc(), elements, id);
current->add(length);
if (!resumeAfter(length))
MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength);
if (!resumeAfter(setLength))
return InliningStatus_Error;
}
@ -1671,14 +1638,13 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
// Check if exist a template object in stub.
JSString* stringThis = nullptr;
JSString* stringArg = nullptr;
NativeObject* templateObject = nullptr;
JSObject* templateObject = nullptr;
if (!inspector->isOptimizableCallStringSplit(pc, &stringThis, &stringArg, &templateObject))
return InliningStatus_NotInlined;
MOZ_ASSERT(stringThis);
MOZ_ASSERT(stringArg);
MOZ_ASSERT(templateObject);
MOZ_ASSERT(templateObject->is<ArrayObject>());
if (strval->toString() != stringThis)
return InliningStatus_NotInlined;
@ -1698,13 +1664,13 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
if (!key.maybeTypes()->hasType(TypeSet::StringType()))
return InliningStatus_NotInlined;
uint32_t initLength = templateObject->as<ArrayObject>().length();
if (templateObject->getDenseInitializedLength() != initLength)
uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject);
if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength)
return InliningStatus_NotInlined;
Vector<MConstant*, 0, SystemAllocPolicy> arrayValues;
for (uint32_t i = 0; i < initLength; i++) {
Value str = templateObject->getDenseElement(i);
Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i);
MOZ_ASSERT(str.toString()->isAtom());
MConstant* value = MConstant::New(alloc(), str, constraints());
if (!TypeSetIncludes(key.maybeTypes(), value->type(), value->resultTypeSet()))
@ -1724,8 +1690,7 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
current->add(templateConst);
MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
templateObject->group()->initialHeap(constraints()),
NewArray_FullyAllocating, pc);
templateObject->group()->initialHeap(constraints()), pc);
current->add(ins);
current->push(ins);
@ -1736,37 +1701,21 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
return InliningStatus_Inlined;
}
// Get the elements vector.
MElements* elements = MElements::New(alloc(), ins);
current->add(elements);
JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
// Store all values, no need to initialize the length after each as
// jsop_initelem_array is doing because we do not expect to bailout
// because the memory is supposed to be allocated by now.
MConstant* id = nullptr;
for (uint32_t i = 0; i < initLength; i++) {
id = MConstant::New(alloc(), Int32Value(i));
current->add(id);
MConstant* value = arrayValues[i];
current->add(value);
MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
/* needsHoleCheck = */ false);
current->add(store);
// There is normally no need for a post barrier on these writes
// because the new array will be in the nursery. However, this
// assumption is volated if we specifically requested pre-tenuring.
if (ins->initialHeap() == gc::TenuredHeap)
current->add(MPostWriteBarrier::New(alloc(), ins, value));
if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false))
return InliningStatus_Error;
}
// Update the length.
MSetInitializedLength* length = MSetInitializedLength::New(alloc(), elements, id);
current->add(length);
if (!resumeAfter(length))
MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength);
if (!resumeAfter(setLength))
return InliningStatus_Error;
return InliningStatus_Inlined;
@ -1792,7 +1741,6 @@ IonBuilder::inlineStringSplit(CallInfo& callInfo)
JSObject* templateObject = inspector->getTemplateObjectForNative(pc, js::str_split);
if (!templateObject)
return InliningStatus_NotInlined;
MOZ_ASSERT(templateObject->is<ArrayObject>());
TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(templateObject);
if (retKey->unknownProperties())
@ -2147,174 +2095,36 @@ IonBuilder::inlineObjectCreate(CallInfo& callInfo)
}
IonBuilder::InliningStatus
IonBuilder::inlineUnsafePutElements(CallInfo& callInfo)
IonBuilder::inlineDefineDataProperty(CallInfo& callInfo)
{
uint32_t argc = callInfo.argc();
if (argc < 3 || (argc % 3) != 0 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
MOZ_ASSERT(!callInfo.constructing());
// Only handle definitions of plain data properties.
if (callInfo.argc() != 3)
return InliningStatus_NotInlined;
}
/* Important:
*
* Here we inline each of the stores resulting from a call to
* UnsafePutElements(). It is essential that these stores occur
* atomically and cannot be interrupted by a stack or recursion
* check. If this is not true, race conditions can occur.
*/
MDefinition* obj = callInfo.getArg(0);
MDefinition* id = callInfo.getArg(1);
MDefinition* value = callInfo.getArg(2);
for (uint32_t base = 0; base < argc; base += 3) {
uint32_t arri = base + 0;
uint32_t idxi = base + 1;
uint32_t elemi = base + 2;
if (ElementAccessHasExtraIndexedProperty(constraints(), obj))
return InliningStatus_NotInlined;
MDefinition* obj = callInfo.getArg(arri);
MDefinition* id = callInfo.getArg(idxi);
MDefinition* elem = callInfo.getArg(elemi);
// setElemTryDense will push the value as the result of the define instead
// of |undefined|, but this is fine if the rval is ignored (as it should be
// in self hosted code.)
MOZ_ASSERT(*GetNextPc(pc) == JSOP_POP);
bool isDenseNative = ElementAccessIsDenseNative(constraints(), obj, id);
bool writeNeedsBarrier = false;
if (isDenseNative) {
writeNeedsBarrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
&obj, nullptr, &elem,
/* canModify = */ false);
}
// We can only inline setelem on dense arrays that do not need type
// barriers and on typed arrays and on typed object arrays.
Scalar::Type arrayType;
if ((!isDenseNative || writeNeedsBarrier) &&
!ElementAccessIsAnyTypedArray(constraints(), obj, id, &arrayType) &&
!elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType))
{
return InliningStatus_NotInlined;
}
}
bool emitted = false;
if (!setElemTryDense(&emitted, obj, id, value, /* writeHole = */ true))
return InliningStatus_Error;
if (!emitted)
return InliningStatus_NotInlined;
callInfo.setImplicitlyUsedUnchecked();
// Push the result first so that the stack depth matches up for
// the potential bailouts that will occur in the stores below.
MConstant* udef = MConstant::New(alloc(), UndefinedValue());
current->add(udef);
current->push(udef);
for (uint32_t base = 0; base < argc; base += 3) {
uint32_t arri = base + 0;
uint32_t idxi = base + 1;
MDefinition* obj = callInfo.getArg(arri);
MDefinition* id = callInfo.getArg(idxi);
if (ElementAccessIsDenseNative(constraints(), obj, id)) {
if (!inlineUnsafeSetDenseArrayElement(callInfo, base))
return InliningStatus_Error;
continue;
}
Scalar::Type arrayType;
if (ElementAccessIsAnyTypedArray(constraints(), obj, id, &arrayType)) {
if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType))
return InliningStatus_Error;
continue;
}
if (elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) {
if (!inlineUnsafeSetTypedObjectArrayElement(callInfo, base, arrayType))
return InliningStatus_Error;
continue;
}
MOZ_CRASH("Element access not dense array nor typed array");
}
return InliningStatus_Inlined;
}
bool
IonBuilder::elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id,
Scalar::Type* arrayType)
{
if (obj->type() != MIRType_Object) // lookupTypeDescrSet() tests for TypedObject
return false;
if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
return false;
TypedObjectPrediction prediction = typedObjectPrediction(obj);
if (prediction.isUseless() || !prediction.ofArrayKind())
return false;
TypedObjectPrediction elemPrediction = prediction.arrayElementType();
if (elemPrediction.isUseless() || elemPrediction.kind() != type::Scalar)
return false;
*arrayType = elemPrediction.scalarType();
return true;
}
bool
IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo& callInfo, uint32_t base)
{
// Note: we do not check the conditions that are asserted as true
// in intrinsic_UnsafePutElements():
// - arr is a dense array
// - idx < initialized length
// Furthermore, note that inlineUnsafePutElements ensures the type of the
// value is reflected in the JSID_VOID property of the array.
MDefinition* obj = callInfo.getArg(base + 0);
MDefinition* id = callInfo.getArg(base + 1);
MDefinition* elem = callInfo.getArg(base + 2);
TemporaryTypeSet::DoubleConversion conversion =
obj->resultTypeSet()->convertDoubleElements(constraints());
if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem, JSVAL_TYPE_MAGIC))
return false;
return true;
}
bool
IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo& callInfo,
uint32_t base,
Scalar::Type arrayType)
{
// Note: we do not check the conditions that are asserted as true
// in intrinsic_UnsafePutElements():
// - arr is a typed array
// - idx < length
MDefinition* obj = callInfo.getArg(base + 0);
MDefinition* id = callInfo.getArg(base + 1);
MDefinition* elem = callInfo.getArg(base + 2);
if (!jsop_setelem_typed(arrayType, SetElem_Unsafe, obj, id, elem))
return false;
return true;
}
bool
IonBuilder::inlineUnsafeSetTypedObjectArrayElement(CallInfo& callInfo,
uint32_t base,
Scalar::Type arrayType)
{
// Note: we do not check the conditions that are asserted as true
// in intrinsic_UnsafePutElements():
// - arr is a typed array
// - idx < length
MDefinition* obj = callInfo.getArg(base + 0);
MDefinition* id = callInfo.getArg(base + 1);
MDefinition* elem = callInfo.getArg(base + 2);
if (!jsop_setelem_typed_object(arrayType, SetElem_Unsafe, obj, id, elem))
return false;
return true;
}
IonBuilder::InliningStatus
IonBuilder::inlineHasClass(CallInfo& callInfo,
const Class* clasp1, const Class* clasp2,

View File

@ -4024,11 +4024,10 @@ MArrayState::Copy(TempAllocator& alloc, MArrayState* state)
}
MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst,
gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc)
gc::InitialHeap initialHeap, jsbytecode* pc)
: MUnaryInstruction(templateConst),
count_(count),
initialHeap_(initialHeap),
allocating_(allocating),
convertDoubleElements_(false),
pc_(pc)
{
@ -4047,11 +4046,6 @@ MNewArray::shouldUseVM() const
if (!templateObject())
return true;
// Allocate space using the VMCall when mir hints it needs to get allocated
// immediately, but only when data doesn't fit the available array slots.
if (allocatingBehaviour() == NewArray_Unallocating)
return false;
if (templateObject()->is<UnboxedArrayObject>()) {
MOZ_ASSERT(templateObject()->as<UnboxedArrayObject>().capacity() >= count());
return !templateObject()->as<UnboxedArrayObject>().hasInlineElements();

View File

@ -2915,8 +2915,6 @@ class MNewArray
// Heap where the array should be allocated.
gc::InitialHeap initialHeap_;
// Allocate space at initialization or not
AllocatingBehaviour allocating_;
// Whether values written to this array should be converted to double first.
bool convertDoubleElements_;
@ -2924,18 +2922,16 @@ class MNewArray
jsbytecode* pc_;
MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst,
gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc);
gc::InitialHeap initialHeap, jsbytecode* pc);
public:
INSTRUCTION_HEADER(NewArray)
static MNewArray* New(TempAllocator& alloc, CompilerConstraintList* constraints,
uint32_t count, MConstant* templateConst,
gc::InitialHeap initialHeap, AllocatingBehaviour allocating,
jsbytecode* pc)
gc::InitialHeap initialHeap, jsbytecode* pc)
{
return new(alloc) MNewArray(constraints, count, templateConst,
initialHeap, allocating, pc);
return new(alloc) MNewArray(constraints, count, templateConst, initialHeap, pc);
}
uint32_t count() const {
@ -2950,10 +2946,6 @@ class MNewArray
return initialHeap_;
}
AllocatingBehaviour allocatingBehaviour() const {
return allocating_;
}
jsbytecode* pc() const {
return pc_;
}
@ -3027,10 +3019,10 @@ class MNewArrayDynamicLength
: public MUnaryInstruction,
public IntPolicy<0>::Data
{
AlwaysTenured<ArrayObject*> templateObject_;
AlwaysTenuredObject templateObject_;
gc::InitialHeap initialHeap_;
MNewArrayDynamicLength(CompilerConstraintList* constraints, ArrayObject* templateObject,
MNewArrayDynamicLength(CompilerConstraintList* constraints, JSObject* templateObject,
gc::InitialHeap initialHeap, MDefinition* length)
: MUnaryInstruction(length),
templateObject_(templateObject),
@ -3046,7 +3038,7 @@ class MNewArrayDynamicLength
INSTRUCTION_HEADER(NewArrayDynamicLength)
static MNewArrayDynamicLength* New(TempAllocator& alloc, CompilerConstraintList* constraints,
ArrayObject* templateObject, gc::InitialHeap initialHeap,
JSObject* templateObject, gc::InitialHeap initialHeap,
MDefinition* length)
{
return new(alloc) MNewArrayDynamicLength(constraints, templateObject, initialHeap, length);
@ -3055,7 +3047,7 @@ class MNewArrayDynamicLength
MDefinition* length() const {
return getOperand(0);
}
ArrayObject* templateObject() const {
JSObject* templateObject() const {
return templateObject_;
}
gc::InitialHeap initialHeap() const {
@ -7995,6 +7987,36 @@ class MIncrementUnboxedArrayInitializedLength
ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength)
};
// Set the initialized length of an unboxed array object.
class MSetUnboxedArrayInitializedLength
: public MBinaryInstruction,
public SingleObjectPolicy::Data
{
explicit MSetUnboxedArrayInitializedLength(MDefinition* obj, MDefinition* length)
: MBinaryInstruction(obj, length)
{}
public:
INSTRUCTION_HEADER(SetUnboxedArrayInitializedLength)
static MSetUnboxedArrayInitializedLength* New(TempAllocator& alloc, MDefinition* obj,
MDefinition* length) {
return new(alloc) MSetUnboxedArrayInitializedLength(obj, length);
}
MDefinition* object() const {
return getOperand(0);
}
MDefinition* length() const {
return getOperand(1);
}
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::ObjectFields);
}
ALLOW_CLONE(MSetUnboxedArrayInitializedLength)
};
// Load the array length from an elements header.
class MArrayLength
: public MUnaryInstruction,
@ -11891,13 +11913,15 @@ class MInArray
{
bool needsHoleCheck_;
bool needsNegativeIntCheck_;
JSValueType unboxedType_;
MInArray(MDefinition* elements, MDefinition* index,
MDefinition* initLength, MDefinition* object,
bool needsHoleCheck)
bool needsHoleCheck, JSValueType unboxedType)
: MQuaternaryInstruction(elements, index, initLength, object),
needsHoleCheck_(needsHoleCheck),
needsNegativeIntCheck_(true)
needsNegativeIntCheck_(true),
unboxedType_(unboxedType)
{
setResultType(MIRType_Boolean);
setMovable();
@ -11911,9 +11935,10 @@ class MInArray
static MInArray* New(TempAllocator& alloc, MDefinition* elements, MDefinition* index,
MDefinition* initLength, MDefinition* object,
bool needsHoleCheck)
bool needsHoleCheck, JSValueType unboxedType)
{
return new(alloc) MInArray(elements, index, initLength, object, needsHoleCheck);
return new(alloc) MInArray(elements, index, initLength, object, needsHoleCheck,
unboxedType);
}
MDefinition* elements() const {
@ -11934,6 +11959,9 @@ class MInArray
bool needsNegativeIntCheck() const {
return needsNegativeIntCheck_;
}
JSValueType unboxedType() const {
return unboxedType_;
}
void collectRangeInfoPreTrunc() override;
AliasSet getAliasSet() const override {
return AliasSet::Load(AliasSet::Element);
@ -11946,6 +11974,8 @@ class MInArray
return false;
if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
return false;
if (unboxedType() != other->unboxedType())
return false;
return congruentIfOperandsEqual(other);
}
};

View File

@ -182,6 +182,7 @@ namespace jit {
_(UnboxedArrayLength) \
_(UnboxedArrayInitializedLength) \
_(IncrementUnboxedArrayInitializedLength) \
_(SetUnboxedArrayInitializedLength) \
_(Not) \
_(BoundsCheck) \
_(BoundsCheckLower) \

View File

@ -1197,14 +1197,12 @@ MNewArray::writeRecoverData(CompactBufferWriter& writer) const
MOZ_ASSERT(canRecoverOnBailout());
writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArray));
writer.writeUnsigned(count());
writer.writeByte(uint8_t(allocatingBehaviour()));
return true;
}
RNewArray::RNewArray(CompactBufferReader& reader)
{
count_ = reader.readUnsigned();
allocatingBehaviour_ = AllocatingBehaviour(reader.readByte());
}
bool
@ -1212,9 +1210,9 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const
{
RootedObject templateObject(cx, &iter.read().toObject());
RootedValue result(cx);
RootedObjectGroup group(cx);
RootedObjectGroup group(cx, templateObject->group());
JSObject* resultObject = NewDenseArray(cx, count_, group, allocatingBehaviour_);
JSObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_);
if (!resultObject)
return false;

View File

@ -635,7 +635,6 @@ class RNewArray final : public RInstruction
{
private:
uint32_t count_;
AllocatingBehaviour allocatingBehaviour_;
public:
RINSTRUCTION_HEADER_(NewArray)

View File

@ -605,13 +605,6 @@ IsArrayEscaped(MInstruction* ins)
MOZ_ASSERT(ins->isNewArray());
uint32_t count = ins->toNewArray()->count();
// The array is probably too large to be represented efficiently with
// MArrayState, and we do not want to make huge allocations during bailouts.
if (ins->toNewArray()->allocatingBehaviour() == NewArray_Unallocating) {
JitSpewDef(JitSpew_Escape, "Array is not allocated\n", ins);
return true;
}
JSObject* obj = ins->toNewArray()->templateObject();
if (!obj || obj->is<UnboxedArrayObject>())
return true;

View File

@ -2005,6 +2005,12 @@ MacroAssemblerARMCompat::or32(Imm32 imm, Register dest)
ma_orr(imm, dest);
}
void
MacroAssemblerARMCompat::or32(Register src, Register dest)
{
ma_orr(src, dest);
}
void
MacroAssemblerARMCompat::xorPtr(Imm32 imm, Register dest)
{

View File

@ -1277,6 +1277,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void and32(Imm32 imm, Register dest);
void and32(Imm32 imm, const Address& dest);
void and32(const Address& src, Register dest);
void or32(Register src, Register dest);
void or32(Imm32 imm, Register dest);
void or32(Imm32 imm, const Address& dest);
void xorPtr(Imm32 imm, Register dest);

View File

@ -1673,6 +1673,12 @@ MacroAssemblerMIPSCompat::or32(Imm32 imm, const Address& dest)
store32(SecondScratchReg, dest);
}
void
MacroAssemblerMIPSCompat::or32(Register src, Register dest)
{
ma_or(dest, src);
}
void
MacroAssemblerMIPSCompat::xor32(Imm32 imm, Register dest)
{

View File

@ -1207,6 +1207,7 @@ public:
void and32(const Address& src, Register dest);
void or32(Imm32 imm, Register dest);
void or32(Imm32 imm, const Address& dest);
void or32(Register src, Register dest);
void xor32(Imm32 imm, Register dest);
void xorPtr(Imm32 imm, Register dest);
void xorPtr(Register src, Register dest);

View File

@ -243,14 +243,25 @@ GetElement(JSContext* cx, HandleObject obj, IndexType index, bool* hole, Mutable
return GetElement(cx, obj, obj, index, hole, vp);
}
void
bool
ElementAdder::append(JSContext* cx, HandleValue v)
{
MOZ_ASSERT(index_ < length_);
if (resObj_)
resObj_->as<NativeObject>().setDenseElementWithType(cx, index_++, v);
else
vp_[index_++] = v;
if (resObj_) {
DenseElementResult result = SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_,
v.address(), 1,
UpdateTypes);
if (result == DenseElementResult::Failure)
return false;
if (result == DenseElementResult::Incomplete) {
if (!DefineElement(cx, resObj_, index_, v))
return false;
}
} else {
vp_[index_] = v;
}
index_++;
return true;
}
void
@ -258,12 +269,9 @@ ElementAdder::appendHole()
{
MOZ_ASSERT(getBehavior_ == ElementAdder::CheckHasElemPreserveHoles);
MOZ_ASSERT(index_ < length_);
if (resObj_) {
MOZ_ASSERT(resObj_->as<NativeObject>().getDenseElement(index_).isMagic(JS_ELEMENTS_HOLE));
index_++;
} else {
vp_[index_++].setMagic(JS_ELEMENTS_HOLE);
}
if (!resObj_)
vp_[index_].setMagic(JS_ELEMENTS_HOLE);
index_++;
}
bool
@ -287,7 +295,8 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver,
if (!GetElement(cx, obj, receiver, i, &val))
return false;
}
adder->append(cx, val);
if (!adder->append(cx, val))
return false;
}
return true;
@ -2281,84 +2290,6 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
return true;
}
// Return a new array with the default prototype and specified allocated
// capacity and length. If possible, try to reuse the group of the input
// object. The resulting array will have the same boxed/unboxed elements
// representation as the input object, and will either reuse the input
// object's group or will have unknown property types.
JSObject*
js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
NewObjectKind newKind, bool forceAnalyze)
{
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
RootedObjectGroup group(cx, obj->getGroup(cx));
if (!group)
return nullptr;
if (group->maybePreliminaryObjects())
group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze);
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
newKind = TenuredObject;
if (group->maybeUnboxedLayout()) {
if (length > UnboxedArrayObject::MaximumCapacity)
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
return UnboxedArrayObject::create(cx, group, length, newKind);
}
ArrayObject* res = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
if (!res)
return nullptr;
res->setGroup(group);
if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
preliminaryObjects->registerNewObject(res);
return res;
}
// As above, except this might not allocate space up to |length| and will
// definitely return a normal boxed array, instead of an unboxed array. This
// should be used when the result might need sparse elements.
static inline ArrayObject*
NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
{
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
return NewDensePartlyAllocatedArray(cx, length);
if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
return NewDensePartlyAllocatedArray(cx, length);
RootedObjectGroup group(cx, obj->getGroup(cx));
if (!group)
return nullptr;
if (group->maybePreliminaryObjects())
group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
NewObjectKind newKind = GenericObject;
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
newKind = TenuredObject;
if (group->maybeUnboxedLayout())
return NewDensePartlyAllocatedArray(cx, length, nullptr, newKind);
ArrayObject* res = NewDensePartlyAllocatedArray(cx, length, nullptr, newKind);
if (!res)
return nullptr;
res->setGroup(group);
return res;
}
/*
* Returns true if this is a dense or unboxed array whose |count| properties
* starting from |startingIndex| may be accessed (get, set, delete) directly
@ -2960,28 +2891,17 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp)
return true;
}
RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
if (!narr)
return false;
if (js::GetElementsOp op = obj->getOps()->getElements) {
// Ensure that we have dense elements, so that ElementAdder::append can
// use setDenseElementWithType.
DenseElementResult result = narr->ensureDenseElements(cx, 0, end - begin);
if (result == DenseElementResult::Failure)
ElementAdder adder(cx, narr, end - begin, ElementAdder::CheckHasElemPreserveHoles);
if (!op(cx, obj, begin, end, &adder))
return false;
if (result == DenseElementResult::Success) {
ElementAdder adder(cx, narr, end - begin, ElementAdder::CheckHasElemPreserveHoles);
if (!op(cx, obj, begin, end, &adder))
return false;
args.rval().setObject(*narr);
return true;
}
// Fallthrough
MOZ_ASSERT(result == DenseElementResult::Incomplete);
args.rval().setObject(*narr);
return true;
}
if (obj->isNative() && obj->isIndexed() && end - begin > 1000) {
@ -3076,13 +2996,9 @@ array_filter(JSContext* cx, unsigned argc, Value* vp)
RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue());
/* Step 6. */
RootedObject arr(cx, NewDenseFullyAllocatedArray(cx, 0));
RootedObject arr(cx, NewFullyAllocatedArrayForCallingAllocationSite(cx, 0));
if (!arr)
return false;
ObjectGroup* newGroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
if (!newGroup)
return false;
arr->setGroup(newGroup);
/* Step 7. */
uint32_t k = 0;
@ -3157,19 +3073,11 @@ IsArrayConstructor(const Value& v)
}
static bool
ArrayFromCallArgs(JSContext* cx, HandleObjectGroup group, CallArgs& args)
ArrayFromCallArgs(JSContext* cx, CallArgs& args)
{
JSObject* obj = NewDenseFullyAllocatedArray(cx, args.length());
JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length());
if (!obj)
return false;
obj->setGroup(group);
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, args.array(), args.length(),
UpdateTypes);
if (result == DenseElementResult::Failure)
return false;
MOZ_ASSERT(result == DenseElementResult::Success);
args.rval().setObject(*obj);
return true;
@ -3183,10 +3091,7 @@ array_of(JSContext* cx, unsigned argc, Value* vp)
if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) {
// IsArrayConstructor(this) will usually be true in practice. This is
// the most common path.
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
if (!group)
return false;
return ArrayFromCallArgs(cx, group, args);
return ArrayFromCallArgs(cx, args);
}
// Step 4.
@ -3289,16 +3194,12 @@ bool
js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.isConstructing())
MOZ_ASSERT(args.newTarget().toObject().as<JSFunction>().native() == js::ArrayConstructor);
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
if (!group)
return false;
if (args.length() != 1 || !args[0].isNumber())
return ArrayFromCallArgs(cx, group, args);
return ArrayFromCallArgs(cx, args);
uint32_t length;
if (args[0].isInt32()) {
@ -3317,14 +3218,7 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
}
}
/*
* Allocate up to |EagerAllocationMaxLength| dense elements eagerly, to
* avoid reallocating elements when filling the array.
*/
AllocatingBehaviour allocating = (length <= ArrayObject::EagerAllocationMaxLength)
? NewArray_FullyAllocating
: NewArray_PartlyAllocating;
RootedObject obj(cx, NewDenseArray(cx, length, group, allocating));
JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length);
if (!obj)
return false;
@ -3332,7 +3226,7 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
return true;
}
ArrayObject*
JSObject*
js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt)
{
if (lengthInt < 0) {
@ -3341,10 +3235,7 @@ js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengt
}
uint32_t length = uint32_t(lengthInt);
AllocatingBehaviour allocating = (length <= ArrayObject::EagerAllocationMaxLength)
? NewArray_FullyAllocating
: NewArray_PartlyAllocating;
return NewDenseArray(cx, length, group, allocating);
return NewPartlyAllocatedArrayTryUseGroup(cx, group, length);
}
static JSObject*
@ -3532,7 +3423,7 @@ js::NewDenseFullyAllocatedArray(ExclusiveContext* cx, uint32_t length,
HandleObject proto /* = nullptr */,
NewObjectKind newKind /* = GenericObject */)
{
return NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto, newKind);
return NewArray<UINT32_MAX>(cx, length, proto, newKind);
}
ArrayObject * JS_FASTCALL
@ -3551,47 +3442,13 @@ js::NewDenseUnallocatedArray(ExclusiveContext* cx, uint32_t length,
return NewArray<0>(cx, length, proto, newKind);
}
ArrayObject*
js::NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group,
AllocatingBehaviour allocating, bool convertDoubleElements)
{
NewObjectKind newKind = !group ? SingletonObject : GenericObject;
if (group && group->shouldPreTenure())
newKind = TenuredObject;
ArrayObject* arr;
if (allocating == NewArray_Unallocating) {
arr = NewDenseUnallocatedArray(cx, length, nullptr, newKind);
} else if (allocating == NewArray_PartlyAllocating) {
arr = NewDensePartlyAllocatedArray(cx, length, nullptr, newKind);
} else {
MOZ_ASSERT(allocating == NewArray_FullyAllocating);
arr = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
}
if (!arr)
return nullptr;
if (group)
arr->setGroup(group);
if (convertDoubleElements)
arr->setShouldConvertDoubleElements();
// If the length calculation overflowed, make sure that is marked for the
// new group.
if (arr->length() > INT32_MAX)
arr->setLength(cx, arr->length());
return arr;
}
ArrayObject*
js::NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src,
uint32_t elementOffset, HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!src->isIndexed());
ArrayObject* arr = NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto);
ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, proto);
if (!arr)
return nullptr;
@ -3610,7 +3467,7 @@ js::NewDenseCopiedArray(JSContext* cx, uint32_t length, const Value* values,
HandleObject proto /* = nullptr */,
NewObjectKind newKind /* = GenericObject */)
{
ArrayObject* arr = NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto);
ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, proto);
if (!arr)
return nullptr;
@ -3636,7 +3493,7 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb
gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
Rooted<ArrayObject*> arr(cx, ArrayObject::createArray(cx, allocKind,
heap, shape, group, length));
heap, shape, group, length));
if (!arr)
return nullptr;
@ -3661,6 +3518,150 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc
return arr;
}
// Return a new boxed or unboxed array with the specified length and allocated
// capacity (up to maxLength), using the specified group if possible.
template <uint32_t maxLength>
static inline JSObject*
NewArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind = GenericObject, bool forceAnalyze = false)
{
MOZ_ASSERT(newKind != SingletonObject);
if (group->maybePreliminaryObjects())
group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze);
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
newKind = TenuredObject;
if (group->maybeUnboxedLayout()) {
if (length > UnboxedArrayObject::MaximumCapacity)
return NewArray<maxLength>(cx, length, nullptr, newKind);
return UnboxedArrayObject::create(cx, group, length, newKind, maxLength);
}
ArrayObject* res = NewArray<maxLength>(cx, length, nullptr, newKind);
if (!res)
return nullptr;
res->setGroup(group);
// If the length calculation overflowed, make sure that is marked for the
// new group.
if (res->length() > INT32_MAX)
res->setLength(cx, res->length());
if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
preliminaryObjects->registerNewObject(res);
return res;
}
JSObject*
js::NewFullyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length)
{
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length);
}
JSObject*
js::NewPartlyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length)
{
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
}
// Return a new array with the default prototype and specified allocated
// capacity and length. If possible, try to reuse the group of the input
// object. The resulting array will either reuse the input object's group or
// will have unknown property types. Additionally, the result will have the
// same boxed/unboxed elements representation as the input object, unless
// |length| is larger than the input object's initialized length (in which case
// UnboxedArrayObject::MaximumCapacity might be exceeded).
template <uint32_t maxLength>
static inline JSObject*
NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
NewObjectKind newKind = GenericObject, bool forceAnalyze = false)
{
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
return NewArray<maxLength>(cx, length, nullptr, newKind);
if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
return NewArray<maxLength>(cx, length, nullptr, newKind);
RootedObjectGroup group(cx, obj->getGroup(cx));
if (!group)
return nullptr;
return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind, forceAnalyze);
}
JSObject*
js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
NewObjectKind newKind, bool forceAnalyze)
{
return NewArrayTryReuseGroup<UINT32_MAX>(cx, obj, length, newKind, forceAnalyze);
}
JSObject*
js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
{
return NewArrayTryReuseGroup<ArrayObject::EagerAllocationMaxLength>(cx, obj, length);
}
JSObject*
js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
NewObjectKind newKind, bool forceAnalyze)
{
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
if (!group)
return nullptr;
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind, forceAnalyze);
}
JSObject*
js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length)
{
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
if (!group)
return nullptr;
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
}
JSObject*
js::NewCopiedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, const Value* vp, size_t length)
{
JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
if (!obj)
return nullptr;
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, UpdateTypes);
if (result == DenseElementResult::Failure)
return nullptr;
if (result == DenseElementResult::Success)
return obj;
MOZ_ASSERT(obj->is<UnboxedArrayObject>());
if (!UnboxedArrayObject::convertToNative(cx, obj))
return nullptr;
result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length,
UpdateTypes);
MOZ_ASSERT(result != DenseElementResult::Incomplete);
if (result == DenseElementResult::Failure)
return nullptr;
return obj;
}
JSObject*
js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length)
{
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
if (!group)
return nullptr;
return NewCopiedArrayTryUseGroup(cx, group, vp, length);
}
#ifdef DEBUG
bool
js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp)

View File

@ -12,6 +12,8 @@
#include "jsobj.h"
#include "jspubtd.h"
#include "vm/ArrayObject.h"
namespace js {
/* 2^32-2, inclusive */
const uint32_t MAX_ARRAY_INDEX = 4294967294u;
@ -35,7 +37,7 @@ IdIsIndex(jsid id, uint32_t* indexp)
extern JSObject*
InitArrayClass(JSContext* cx, js::HandleObject obj);
class ArrayObject;
// The methods below only create dense boxed arrays.
/* Create a dense array with no capacity allocated, length set to 0. */
extern ArrayObject * JS_FASTCALL
@ -63,20 +65,6 @@ extern ArrayObject * JS_FASTCALL
NewDenseFullyAllocatedArray(ExclusiveContext* cx, uint32_t length, HandleObject proto = nullptr,
NewObjectKind newKind = GenericObject);
enum AllocatingBehaviour {
NewArray_Unallocating,
NewArray_PartlyAllocating,
NewArray_FullyAllocating
};
/*
* Create a dense array with a set length, but only allocates space for the
* contents if the length is not excessive.
*/
extern ArrayObject*
NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group,
AllocatingBehaviour allocating, bool convertDoubleElements = false);
/* Create a dense array with a copy of the dense array elements in src. */
extern ArrayObject*
NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src,
@ -95,12 +83,36 @@ NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject
extern JSObject*
NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap);
/* Create a dense or unboxed array, using the same group as |obj| if possible. */
// The methods below can create either boxed or unboxed arrays.
extern JSObject*
NewFullyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length);
extern JSObject*
NewPartlyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length);
extern JSObject*
NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
NewObjectKind newKind = GenericObject,
bool forceAnalyze = false);
extern JSObject*
NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length);
extern JSObject*
NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
NewObjectKind newKind = GenericObject,
bool forceAnalyze = false);
extern JSObject*
NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length);
extern JSObject*
NewCopiedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, const Value* vp, size_t length);
extern JSObject*
NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length);
/*
* Determines whether a write to the given element on |obj| should fail because
* |obj| is an Array with a non-writable length, and writing that element would
@ -193,7 +205,7 @@ array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, H
extern bool
NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v);
extern ArrayObject*
extern JSObject*
ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt);
#ifdef DEBUG

View File

@ -794,7 +794,6 @@ bool intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, Value* vp);

View File

@ -663,18 +663,15 @@ js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id,
if (IsAnyTypedArray(obj))
return DefinePropertyOnTypedArray(cx, obj, id, desc, result);
if (!MaybeConvertUnboxedObjectToNative(cx, obj))
return false;
if (obj->getOps()->lookupProperty) {
if (obj->is<ProxyObject>()) {
Rooted<PropertyDescriptor> pd(cx, desc);
pd.object().set(obj);
return Proxy::defineProperty(cx, obj, id, pd, result);
}
return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
if (obj->is<ProxyObject>()) {
Rooted<PropertyDescriptor> pd(cx, desc);
pd.object().set(obj);
return Proxy::defineProperty(cx, obj, id, pd, result);
}
if (obj->getOps()->defineProperty)
return obj->getOps()->defineProperty(cx, obj, id, desc, result);
return DefinePropertyOnObject(cx, obj.as<NativeObject>(), id, desc, result);
}

View File

@ -3612,7 +3612,7 @@ class SplitMatchResult {
} /* anonymous namespace */
template<class Matcher>
static ArrayObject*
static JSObject*
SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher& splitMatch,
HandleObjectGroup group)
{
@ -3635,10 +3635,10 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
* assertEq(b.length, 0);
*/
if (!result.isFailure())
return NewDenseEmptyArray(cx);
return NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
RootedValue v(cx, StringValue(str));
return NewDenseCopiedArray(cx, 1, v.address());
return NewCopiedArrayTryUseGroup(cx, group, v.address(), 1);
}
/* Step 12. */
@ -3696,7 +3696,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
/* Step 13(c)(iii)(4). */
if (splits.length() == limit)
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
/* Step 13(c)(iii)(5). */
lastEndIndex = endIndex;
@ -3717,15 +3717,13 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
if (!sub || !splits.append(StringValue(sub)))
return nullptr;
} else {
/* Only string entries have been accounted for so far. */
AddTypePropertyId(cx, group, nullptr, JSID_VOID, UndefinedValue());
if (!splits.append(UndefinedValue()))
return nullptr;
}
/* Step 13(c)(iii)(7)(d). */
if (splits.length() == limit)
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
}
}
@ -3739,12 +3737,12 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
return nullptr;
/* Step 16. */
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
}
// Fast-path for splitting a string into a character array via split("").
static ArrayObject*
CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit)
static JSObject*
CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group)
{
size_t strLength = str->length();
if (strLength == 0)
@ -3764,7 +3762,7 @@ CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit)
splits.infallibleAppend(StringValue(sub));
}
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
}
namespace {
@ -3849,7 +3847,6 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
if (!group)
return false;
AddTypePropertyId(cx, group, nullptr, JSID_VOID, TypeSet::StringType());
/* Step 5: Use the second argument as the split limit, if given. */
uint32_t limit;
@ -3880,10 +3877,9 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
/* Step 9. */
if (limit == 0) {
JSObject* aobj = NewDenseEmptyArray(cx);
JSObject* aobj = NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
if (!aobj)
return false;
aobj->setGroup(group);
args.rval().setObject(*aobj);
return true;
}
@ -3891,10 +3887,9 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
/* Step 10. */
if (!sepDefined) {
RootedValue v(cx, StringValue(str));
JSObject* aobj = NewDenseCopiedArray(cx, 1, v.address());
JSObject* aobj = NewCopiedArrayTryUseGroup(cx, group, v.address(), 1);
if (!aobj)
return false;
aobj->setGroup(group);
args.rval().setObject(*aobj);
return true;
}
@ -3906,7 +3901,7 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
RootedObject aobj(cx);
if (!re.initialized()) {
if (sepstr->length() == 0) {
aobj = CharSplitHelper(cx, linearStr, limit);
aobj = CharSplitHelper(cx, linearStr, limit, group);
} else {
SplitStringMatcher matcher(cx, sepstr);
aobj = SplitHelper(cx, linearStr, limit, matcher, group);
@ -3922,7 +3917,6 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
return false;
/* Step 16. */
aobj->setGroup(group);
args.rval().setObject(*aobj);
return true;
}
@ -3940,19 +3934,11 @@ js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, H
uint32_t limit = UINT32_MAX;
RootedObject aobj(cx);
if (linearSep->length() == 0) {
aobj = CharSplitHelper(cx, linearStr, limit);
} else {
SplitStringMatcher matcher(cx, linearSep);
aobj = SplitHelper(cx, linearStr, limit, matcher, group);
}
if (linearSep->length() == 0)
return CharSplitHelper(cx, linearStr, limit, group);
if (!aobj)
return nullptr;
aobj->setGroup(group);
return aobj;
SplitStringMatcher matcher(cx, linearSep);
return SplitHelper(cx, linearStr, limit, matcher, group);
}
/*

View File

@ -1174,7 +1174,10 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc
}
}
if (JSOp(*pc) == JSOP_NEWARRAY && cx->runtime()->options().unboxedArrays()) {
if (kind == JSProto_Array &&
(JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) &&
cx->runtime()->options().unboxedArrays())
{
PreliminaryObjectArrayWithTemplate* preliminaryObjects =
cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
if (preliminaryObjects)

View File

@ -300,81 +300,11 @@ js::intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp)
uint32_t length = args[0].toInt32();
// Make a new buffer and initialize it up to length.
RootedArrayObject buffer(cx, NewDenseFullyAllocatedArray(cx, length));
RootedObject buffer(cx, NewFullyAllocatedArrayForCallingAllocationSite(cx, length));
if (!buffer)
return false;
ObjectGroup* newgroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
if (!newgroup)
return false;
buffer->setGroup(newgroup);
DenseElementResult edr = buffer->ensureDenseElements(cx, length, 0);
switch (edr) {
case DenseElementResult::Success:
args.rval().setObject(*buffer);
return true;
case DenseElementResult::Incomplete: // shouldn't happen!
MOZ_ASSERT(!"%EnsureDenseArrayElements() would yield sparse array");
JS_ReportError(cx, "%EnsureDenseArrayElements() would yield sparse array");
break;
case DenseElementResult::Failure:
break;
}
return false;
}
/*
* UnsafePutElements(arr0, idx0, elem0, ..., arrN, idxN, elemN): For each set of
* (arr, idx, elem) arguments that are passed, performs the assignment
* |arr[idx] = elem|. |arr| must be either a dense array or a typed array.
*
* If |arr| is a dense array, the index must be an int32 less than the
* initialized length of |arr|. Use |%EnsureDenseResultArrayElements|
* to ensure that the initialized length is long enough.
*
* If |arr| is a typed array, the index must be an int32 less than the
* length of |arr|.
*/
bool
js::intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if ((args.length() % 3) != 0) {
JS_ReportError(cx, "Incorrect number of arguments, not divisible by 3");
return false;
}
for (uint32_t base = 0; base < args.length(); base += 3) {
uint32_t arri = base;
uint32_t idxi = base+1;
uint32_t elemi = base+2;
MOZ_ASSERT(args[arri].isObject());
MOZ_ASSERT(args[arri].toObject().isNative() || IsTypedObjectArray(args[arri].toObject()));
MOZ_ASSERT(args[idxi].isInt32());
RootedObject arrobj(cx, &args[arri].toObject());
uint32_t idx = args[idxi].toInt32();
if (IsAnyTypedArray(arrobj.get()) || arrobj->is<TypedObject>()) {
MOZ_ASSERT_IF(IsAnyTypedArray(arrobj.get()), idx < AnyTypedArrayLength(arrobj.get()));
MOZ_ASSERT_IF(arrobj->is<TypedObject>(), idx < uint32_t(arrobj->as<TypedObject>().length()));
// XXX: Always non-strict.
ObjectOpResult ignored;
RootedValue receiver(cx, ObjectValue(*arrobj));
if (!SetElement(cx, arrobj, idx, args[elemi], receiver, ignored))
return false;
} else {
MOZ_ASSERT(idx < arrobj->as<ArrayObject>().getDenseInitializedLength());
arrobj->as<ArrayObject>().setDenseElementWithType(cx, idx, args[elemi]);
}
}
args.rval().setUndefined();
args.rval().setObject(*buffer);
return true;
}
@ -383,38 +313,47 @@ js::intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 4);
MOZ_ASSERT(args.length() >= 3);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[3].isInt32());
RootedObject obj(cx, &args[0].toObject());
RootedId id(cx);
if (!ValueToId<CanGC>(cx, args[1], &id))
return false;
RootedValue value(cx, args[2]);
unsigned attributes = args[3].toInt32();
unsigned attrs = 0;
if (args.length() >= 4) {
unsigned attributes = args[3].toInt32();
MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
"_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
if (attributes & ATTR_ENUMERABLE)
attrs |= JSPROP_ENUMERATE;
MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
"_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
"ATTR_NONCONFIGURABLE");
if (attributes & ATTR_NONCONFIGURABLE)
attrs |= JSPROP_PERMANENT;
MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
"_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
if (attributes & ATTR_NONWRITABLE)
attrs |= JSPROP_READONLY;
} else {
// If the fourth argument is unspecified, the attributes are for a
// plain data property.
attrs = JSPROP_ENUMERATE;
}
Rooted<PropertyDescriptor> desc(cx);
unsigned attrs = 0;
MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
"_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
if (attributes & ATTR_ENUMERABLE)
attrs |= JSPROP_ENUMERATE;
MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
"_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
"ATTR_NONCONFIGURABLE");
if (attributes & ATTR_NONCONFIGURABLE)
attrs |= JSPROP_PERMANENT;
MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
"_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
if (attributes & ATTR_NONWRITABLE)
attrs |= JSPROP_READONLY;
desc.setDataDescriptor(value, attrs);
return StandardDefineProperty(cx, obj, id, desc);
if (!StandardDefineProperty(cx, obj, id, desc))
return false;
args.rval().setUndefined();
return true;
}
bool
@ -1374,7 +1313,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
JS_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0),
JS_FN("UnsafePutElements", intrinsic_UnsafePutElements, 3,0),
JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0),
JS_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0),
JS_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,0),

View File

@ -320,10 +320,28 @@ SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length)
}
}
static inline bool
SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
{
if (obj->isNative()) {
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
return true;
}
return obj->as<UnboxedArrayObject>().setElement(cx, index, value);
}
/////////////////////////////////////////////////////////////////////
// Template methods for NativeObject and UnboxedArrayObject accesses.
/////////////////////////////////////////////////////////////////////
static inline JSValueType
GetBoxedOrUnboxedType(JSObject* obj)
{
if (obj->isNative())
return JSVAL_TYPE_MAGIC;
return obj->as<UnboxedArrayObject>().elementType();
}
template <JSValueType Type>
static inline bool
HasBoxedOrUnboxedDenseElements(JSObject* obj)
@ -387,6 +405,17 @@ SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Val
obj->as<UnboxedArrayObject>().setElementNoTypeChangeSpecific<Type>(index, value);
}
template <JSValueType Type>
static inline bool
SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
{
if (Type == JSVAL_TYPE_MAGIC) {
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
return true;
}
return obj->as<UnboxedArrayObject>().setElementSpecific<Type>(cx, index, value);
}
template <JSValueType Type>
static inline bool
EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
@ -666,6 +695,9 @@ CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
void
SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
bool
EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
} // namespace js
#endif // vm_UnboxedObject_inl_h

View File

@ -416,6 +416,8 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
RootedObjectGroup replacementGroup(cx);
const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
// Immediately clear any new script on the group. This is done by replacing
// the existing new script with one for a replacement default new group.
// This is done so that the size of the replacment group's objects is the
@ -447,33 +449,33 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
// Similarly, if this group is keyed to an allocation site, replace its
// entry with a new group that has no unboxed layout.
if (layout.allocationScript()) {
MOZ_ASSERT(!layout.isArray());
RootedScript script(cx, layout.allocationScript());
jsbytecode* pc = layout.allocationPc();
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto);
if (!replacementGroup)
return false;
PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>();
replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty());
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc,
JSProto_Object,
JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object;
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key,
replacementGroup);
// Clear any baseline information at this opcode.
// Clear any baseline information at this opcode which might use the old group.
if (script->hasBaselineScript()) {
jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc));
jit::ICFallbackStub* fallback = entry.fallbackStub();
for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++)
iter.unlink(cx);
fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
if (fallback->isNewObject_Fallback())
fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
else if (fallback->isNewArray_Fallback())
fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup);
}
}
const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind());
if (layout.isArray()) {
@ -734,7 +736,7 @@ UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId
if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
// This define is equivalent to setting an existing property.
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, desc.value()))
return true;
return result.succeed();
}
// Trying to incompatibly redefine an existing property requires the
@ -742,8 +744,7 @@ UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId
if (!convertToNative(cx, obj))
return false;
return DefineProperty(cx, obj, id, desc, result) &&
result.checkStrict(cx, obj, id);
return DefineProperty(cx, obj, id, desc, result);
}
// Define the property on the expando object.
@ -1001,13 +1002,14 @@ UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
/* static */ UnboxedArrayObject*
UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length,
NewObjectKind newKind)
NewObjectKind newKind, uint32_t maxLength)
{
MOZ_ASSERT(length <= MaximumCapacity);
MOZ_ASSERT(group->clasp() == &class_);
uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType());
uint32_t nbytes = offsetOfInlineElements() + elementSize * length;
uint32_t capacity = Min(length, maxLength);
uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity;
UnboxedArrayObject* res;
if (nbytes <= JSObject::MAX_BYTE_SIZE) {
@ -1015,7 +1017,7 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32
// If there was no provided length information, pick an allocation kind
// to accommodate small arrays (as is done for normal native arrays).
if (length == 0)
if (capacity == 0)
allocKind = gc::AllocKind::OBJECT8;
res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, allocKind, newKind);
@ -1023,14 +1025,20 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32
return nullptr;
res->setInlineElements();
size_t capacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
res->setCapacityIndex(exactCapacityIndex(capacity));
size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
MOZ_ASSERT(actualCapacity >= capacity);
res->setCapacityIndex(exactCapacityIndex(actualCapacity));
} else {
res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, gc::AllocKind::OBJECT0, newKind);
if (!res)
return nullptr;
res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, length * elementSize);
uint32_t capacityIndex = (capacity == length)
? CapacityMatchesLengthIndex
: chooseCapacityIndex(capacity, length);
uint32_t actualCapacity = computeCapacity(capacityIndex, length);
res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, actualCapacity * elementSize);
if (!res->elements_) {
// Make the object safe for GC.
res->setInlineElements();
@ -1038,7 +1046,7 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32
return nullptr;
}
res->setCapacityIndex(CapacityMatchesLengthIndex);
res->setCapacityIndex(capacityIndex);
}
res->setLength(cx, length);

View File

@ -416,7 +416,8 @@ class UnboxedArrayObject : public JSObject
static bool convertToNative(JSContext* cx, JSObject* obj);
static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group,
uint32_t length, NewObjectKind newKind);
uint32_t length, NewObjectKind newKind,
uint32_t maxLength = MaximumCapacity);
void fillAfterConvert(ExclusiveContext* cx,
const AutoValueVector& values, size_t* valueCursor);