mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 881536 - Part 4: Implement ParallelGetElementIC. (r=nbp)
This commit is contained in:
parent
9d5cb5bd4f
commit
b97ad254e9
@ -5809,6 +5809,24 @@ CodeGenerator::visitParallelGetPropertyIC(OutOfLineUpdateCache *ool, ParallelGet
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
|
||||
TypedOrValueRegister output, bool monitoredResult)
|
||||
{
|
||||
switch (gen->info().executionMode()) {
|
||||
case SequentialExecution: {
|
||||
GetElementIC cache(obj, index, output, monitoredResult);
|
||||
return addCache(ins, allocateCache(cache));
|
||||
}
|
||||
case ParallelExecution: {
|
||||
ParallelGetElementIC cache(obj, index, output, monitoredResult);
|
||||
return addCache(ins, allocateCache(cache));
|
||||
}
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Bad execution mode");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins)
|
||||
{
|
||||
@ -5816,9 +5834,7 @@ CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins)
|
||||
ConstantOrRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index));
|
||||
TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
|
||||
|
||||
GetElementIC cache(obj, index, output, ins->mir()->monitoredResult());
|
||||
|
||||
return addCache(ins, allocateCache(cache));
|
||||
return addGetElementCache(ins, obj, index, output, ins->mir()->monitoredResult());
|
||||
}
|
||||
|
||||
bool
|
||||
@ -5828,9 +5844,7 @@ CodeGenerator::visitGetElementCacheT(LGetElementCacheT *ins)
|
||||
ConstantOrRegister index = TypedOrValueRegister(MIRType_Int32, ToAnyRegister(ins->index()));
|
||||
TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output()));
|
||||
|
||||
GetElementIC cache(obj, index, output, ins->mir()->monitoredResult());
|
||||
|
||||
return addCache(ins, allocateCache(cache));
|
||||
return addGetElementCache(ins, obj, index, output, ins->mir()->monitoredResult());
|
||||
}
|
||||
|
||||
typedef bool (*GetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue);
|
||||
@ -5908,6 +5922,29 @@ CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, SetElementIC *ic)
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef ParallelResult (*ParallelGetElementICFn)(ForkJoinSlice *, size_t, HandleObject,
|
||||
HandleValue, MutableHandleValue);
|
||||
const VMFunction ParallelGetElementIC::UpdateInfo =
|
||||
FunctionInfo<ParallelGetElementICFn>(ParallelGetElementIC::update);
|
||||
|
||||
bool
|
||||
CodeGenerator::visitParallelGetElementIC(OutOfLineUpdateCache *ool, ParallelGetElementIC *ic)
|
||||
{
|
||||
LInstruction *lir = ool->lir();
|
||||
saveLive(lir);
|
||||
|
||||
pushArg(ic->index());
|
||||
pushArg(ic->object());
|
||||
pushArg(Imm32(ool->getCacheIndex()));
|
||||
if (!callVM(ParallelGetElementIC::UpdateInfo, lir))
|
||||
return false;
|
||||
StoreValueTo(ic->output()).generate(this);
|
||||
restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitBindNameCache(LBindNameCache *ins)
|
||||
{
|
||||
|
@ -287,6 +287,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitParallelGetPropertyIC(OutOfLineUpdateCache *ool, ParallelGetPropertyIC *ic);
|
||||
bool visitSetPropertyIC(OutOfLineUpdateCache *ool, SetPropertyIC *ic);
|
||||
bool visitGetElementIC(OutOfLineUpdateCache *ool, GetElementIC *ic);
|
||||
bool visitParallelGetElementIC(OutOfLineUpdateCache *ool, ParallelGetElementIC *ic);
|
||||
bool visitSetElementIC(OutOfLineUpdateCache *ool, SetElementIC *ic);
|
||||
bool visitBindNameIC(OutOfLineUpdateCache *ool, BindNameIC *ic);
|
||||
bool visitNameIC(OutOfLineUpdateCache *ool, NameIC *ic);
|
||||
@ -302,6 +303,8 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
|
||||
PropertyName *name, TypedOrValueRegister output,
|
||||
bool allowGetters);
|
||||
bool addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
|
||||
TypedOrValueRegister output, bool monitoredResult);
|
||||
bool checkForParallelBailout(LInstruction *lir);
|
||||
|
||||
bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
|
||||
|
@ -1057,8 +1057,8 @@ bool
|
||||
GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
|
||||
HandleShape shape)
|
||||
{
|
||||
RepatchStubAppender attacher(*this);
|
||||
MacroAssembler masm(cx);
|
||||
RepatchStubAppender attacher(*this);
|
||||
GenerateReadSlot(cx, ion, masm, attacher, obj, name(), holder, shape, object(), output());
|
||||
const char *attachKind = "non idempotent reading";
|
||||
if (idempotent())
|
||||
@ -1600,22 +1600,24 @@ ParallelIonCache::destroy()
|
||||
js_delete(stubbedShapes_);
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelGetPropertyIC::canAttachReadSlot(LockedJSContext &cx, JSObject *obj,
|
||||
MutableHandleObject holder, MutableHandleShape shape)
|
||||
/* static */ bool
|
||||
ParallelGetPropertyIC::canAttachReadSlot(LockedJSContext &cx, IonCache &cache,
|
||||
TypedOrValueRegister output, JSObject *obj,
|
||||
PropertyName *name, MutableHandleObject holder,
|
||||
MutableHandleShape shape)
|
||||
{
|
||||
// Bail if we have hooks or are not native.
|
||||
if (!obj->hasIdempotentProtoChain())
|
||||
return false;
|
||||
|
||||
if (!js::LookupPropertyPure(obj, NameToId(name()), holder.address(), shape.address()))
|
||||
if (!js::LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address()))
|
||||
return false;
|
||||
|
||||
// In parallel execution we can't cache getters due to possible
|
||||
// side-effects, so only check if we can cache slot reads.
|
||||
bool readSlot;
|
||||
bool callGetter;
|
||||
if (!DetermineGetPropKind(cx, *this, obj, obj, holder, shape, output(), false,
|
||||
if (!DetermineGetPropKind(cx, cache, obj, obj, holder, shape, output, false,
|
||||
&readSlot, &callGetter) || !readSlot)
|
||||
{
|
||||
return false;
|
||||
@ -1689,7 +1691,9 @@ ParallelGetPropertyIC::update(ForkJoinSlice *slice, size_t cacheIndex,
|
||||
{
|
||||
RootedShape shape(cx);
|
||||
RootedObject holder(cx);
|
||||
if (cache.canAttachReadSlot(cx, obj, &holder, &shape)) {
|
||||
if (canAttachReadSlot(cx, cache, cache.output(), obj, cache.name(),
|
||||
&holder, &shape))
|
||||
{
|
||||
if (!cache.attachReadSlot(cx, ion, obj, holder, shape))
|
||||
return TP_FATAL;
|
||||
attachedStub = true;
|
||||
@ -2159,6 +2163,16 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
|
||||
|
||||
const size_t GetElementIC::MAX_FAILED_UPDATES = 16;
|
||||
|
||||
/* static */ bool
|
||||
GetElementIC::canAttachGetProp(JSObject *obj, const Value &idval, jsid id)
|
||||
{
|
||||
uint32_t dummy;
|
||||
return (obj->isNative() &&
|
||||
idval.isString() &&
|
||||
JSID_IS_ATOM(id) &&
|
||||
!JSID_TO_ATOM(id)->isIndex(&dummy));
|
||||
}
|
||||
|
||||
bool
|
||||
GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
|
||||
const Value &idval, HandlePropertyName name)
|
||||
@ -2196,108 +2210,137 @@ GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "property");
|
||||
}
|
||||
|
||||
bool
|
||||
GetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval)
|
||||
/* static */ bool
|
||||
GetElementIC::canAttachDenseElement(JSObject *obj, const Value &idval)
|
||||
{
|
||||
JS_ASSERT(obj->isNative());
|
||||
JS_ASSERT(idval.isInt32());
|
||||
return obj->isNative() && idval.isInt32();
|
||||
}
|
||||
|
||||
static bool
|
||||
GenerateDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
JSObject *obj, const Value &idval, Register object,
|
||||
ConstantOrRegister index, TypedOrValueRegister output)
|
||||
{
|
||||
JS_ASSERT(GetElementIC::canAttachDenseElement(obj, idval));
|
||||
|
||||
Label failures;
|
||||
MacroAssembler masm(cx);
|
||||
RepatchStubAppender attacher(*this);
|
||||
|
||||
// Guard object's shape.
|
||||
RootedObject globalObj(cx, &script->global());
|
||||
RootedShape shape(cx, obj->lastProperty());
|
||||
if (!shape)
|
||||
return false;
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object(), shape, &failures);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
|
||||
|
||||
// Ensure the index is an int32 value.
|
||||
Register indexReg = InvalidReg;
|
||||
|
||||
if (index().reg().hasValue()) {
|
||||
indexReg = output().scratchReg().gpr();
|
||||
if (index.reg().hasValue()) {
|
||||
indexReg = output.scratchReg().gpr();
|
||||
JS_ASSERT(indexReg != InvalidReg);
|
||||
ValueOperand val = index().reg().valueReg();
|
||||
ValueOperand val = index.reg().valueReg();
|
||||
|
||||
masm.branchTestInt32(Assembler::NotEqual, val, &failures);
|
||||
|
||||
// Unbox the index.
|
||||
masm.unboxInt32(val, indexReg);
|
||||
} else {
|
||||
JS_ASSERT(!index().reg().typedReg().isFloat());
|
||||
indexReg = index().reg().typedReg().gpr();
|
||||
JS_ASSERT(!index.reg().typedReg().isFloat());
|
||||
indexReg = index.reg().typedReg().gpr();
|
||||
}
|
||||
|
||||
// Load elements vector.
|
||||
masm.push(object());
|
||||
masm.loadPtr(Address(object(), JSObject::offsetOfElements()), object());
|
||||
masm.push(object);
|
||||
masm.loadPtr(Address(object, JSObject::offsetOfElements()), object);
|
||||
|
||||
Label hole;
|
||||
|
||||
// Guard on the initialized length.
|
||||
Address initLength(object(), ObjectElements::offsetOfInitializedLength());
|
||||
Address initLength(object, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, indexReg, &hole);
|
||||
|
||||
// Check for holes & load the value.
|
||||
masm.loadElementTypedOrValue(BaseIndex(object(), indexReg, TimesEight),
|
||||
output(), true, &hole);
|
||||
masm.loadElementTypedOrValue(BaseIndex(object, indexReg, TimesEight),
|
||||
output, true, &hole);
|
||||
|
||||
masm.pop(object());
|
||||
masm.pop(object);
|
||||
attacher.jumpRejoin(masm);
|
||||
|
||||
// All failures flow to here.
|
||||
masm.bind(&hole);
|
||||
masm.pop(object());
|
||||
masm.pop(object);
|
||||
masm.bind(&failures);
|
||||
|
||||
attacher.jumpNextStub(masm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
RepatchStubAppender attacher(*this);
|
||||
if (!GenerateDenseElement(cx, masm, attacher, obj, idval, object(), index(), output()))
|
||||
return false;
|
||||
|
||||
setHasDenseStub();
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
|
||||
}
|
||||
|
||||
bool
|
||||
GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
|
||||
const Value &idval)
|
||||
/* static */ bool
|
||||
GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
|
||||
TypedOrValueRegister output)
|
||||
{
|
||||
if (!obj->is<TypedArrayObject>() ||
|
||||
(!(idval.isInt32()) &&
|
||||
!(idval.isString() && GetIndexFromString(idval.toString()) != UINT32_MAX)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The output register is not yet specialized as a float register, the only
|
||||
// way to accept float typed arrays for now is to return a Value type.
|
||||
int arrayType = obj->as<TypedArrayObject>().type();
|
||||
bool floatOutput = arrayType == TypedArrayObject::TYPE_FLOAT32 ||
|
||||
arrayType == TypedArrayObject::TYPE_FLOAT64;
|
||||
return !floatOutput || output.hasValue();
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
TypedArrayObject *tarr, const Value &idval, Register object,
|
||||
ConstantOrRegister index, TypedOrValueRegister output)
|
||||
{
|
||||
JS_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
|
||||
|
||||
Label failures;
|
||||
MacroAssembler masm(cx);
|
||||
RepatchStubAppender attacher(*this);
|
||||
|
||||
// The array type is the object within the table of typed array classes.
|
||||
int arrayType = tarr->type();
|
||||
|
||||
// The output register is not yet specialized as a float register, the only
|
||||
// way to accept float typed arrays for now is to return a Value type.
|
||||
DebugOnly<bool> floatOutput = arrayType == TypedArrayObject::TYPE_FLOAT32 ||
|
||||
arrayType == TypedArrayObject::TYPE_FLOAT64;
|
||||
JS_ASSERT_IF(!output().hasValue(), !floatOutput);
|
||||
|
||||
Register tmpReg = output().scratchReg().gpr();
|
||||
Register tmpReg = output.scratchReg().gpr();
|
||||
JS_ASSERT(tmpReg != InvalidReg);
|
||||
|
||||
// Check that the typed array is of the same type as the current object
|
||||
// because load size differ in function of the typed array data width.
|
||||
masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, tarr->getClass(), &failures);
|
||||
masm.branchTestObjClass(Assembler::NotEqual, object, tmpReg, tarr->getClass(), &failures);
|
||||
|
||||
// Decide to what type index the stub should be optimized
|
||||
Register indexReg = tmpReg;
|
||||
JS_ASSERT(!index().constant());
|
||||
JS_ASSERT(!index.constant());
|
||||
if (idval.isString()) {
|
||||
JS_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX);
|
||||
|
||||
// Part 1: Get the string into a register
|
||||
Register str;
|
||||
if (index().reg().hasValue()) {
|
||||
ValueOperand val = index().reg().valueReg();
|
||||
if (index.reg().hasValue()) {
|
||||
ValueOperand val = index.reg().valueReg();
|
||||
masm.branchTestString(Assembler::NotEqual, val, &failures);
|
||||
|
||||
str = masm.extractString(val, indexReg);
|
||||
} else {
|
||||
JS_ASSERT(!index().reg().typedReg().isFloat());
|
||||
str = index().reg().typedReg().gpr();
|
||||
JS_ASSERT(!index.reg().typedReg().isFloat());
|
||||
str = index.reg().typedReg().gpr();
|
||||
}
|
||||
|
||||
// Part 2: Call to translate the str into index
|
||||
@ -2321,51 +2364,59 @@ GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayO
|
||||
} else {
|
||||
JS_ASSERT(idval.isInt32());
|
||||
|
||||
if (index().reg().hasValue()) {
|
||||
ValueOperand val = index().reg().valueReg();
|
||||
if (index.reg().hasValue()) {
|
||||
ValueOperand val = index.reg().valueReg();
|
||||
masm.branchTestInt32(Assembler::NotEqual, val, &failures);
|
||||
|
||||
// Unbox the index.
|
||||
masm.unboxInt32(val, indexReg);
|
||||
} else {
|
||||
JS_ASSERT(!index().reg().typedReg().isFloat());
|
||||
indexReg = index().reg().typedReg().gpr();
|
||||
JS_ASSERT(!index.reg().typedReg().isFloat());
|
||||
indexReg = index.reg().typedReg().gpr();
|
||||
}
|
||||
}
|
||||
|
||||
// Guard on the initialized length.
|
||||
Address length(object(), TypedArrayObject::lengthOffset());
|
||||
Address length(object, TypedArrayObject::lengthOffset());
|
||||
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
|
||||
|
||||
// Save the object register on the stack in case of failure.
|
||||
Label popAndFail;
|
||||
Register elementReg = object();
|
||||
masm.push(object());
|
||||
Register elementReg = object;
|
||||
masm.push(object);
|
||||
|
||||
// Load elements vector.
|
||||
masm.loadPtr(Address(object(), TypedArrayObject::dataOffset()), elementReg);
|
||||
masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg);
|
||||
|
||||
// Load the value. We use an invalid register because the destination
|
||||
// register is necessary a non double register.
|
||||
int width = TypedArrayObject::slotWidth(arrayType);
|
||||
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
|
||||
if (output().hasValue())
|
||||
masm.loadFromTypedArray(arrayType, source, output().valueReg(), true,
|
||||
if (output.hasValue())
|
||||
masm.loadFromTypedArray(arrayType, source, output.valueReg(), true,
|
||||
elementReg, &popAndFail);
|
||||
else
|
||||
masm.loadFromTypedArray(arrayType, source, output().typedReg(),
|
||||
masm.loadFromTypedArray(arrayType, source, output.typedReg(),
|
||||
elementReg, &popAndFail);
|
||||
|
||||
masm.pop(object());
|
||||
masm.pop(object);
|
||||
attacher.jumpRejoin(masm);
|
||||
|
||||
// Restore the object before continuing to the next stub.
|
||||
masm.bind(&popAndFail);
|
||||
masm.pop(object());
|
||||
masm.pop(object);
|
||||
masm.bind(&failures);
|
||||
|
||||
attacher.jumpNextStub(masm);
|
||||
}
|
||||
|
||||
bool
|
||||
GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
|
||||
const Value &idval)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
RepatchStubAppender attacher(*this);
|
||||
GenerateTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
|
||||
}
|
||||
|
||||
@ -2517,34 +2568,22 @@ GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub && obj->isNative() && cache.monitoredResult()) {
|
||||
uint32_t dummy;
|
||||
if (idval.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy)) {
|
||||
RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
|
||||
if (!cache.attachGetProp(cx, ion, obj, idval, name))
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub && cache.monitoredResult() && canAttachGetProp(obj, idval, id)) {
|
||||
RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
|
||||
if (!cache.attachGetProp(cx, ion, obj, idval, name))
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub && !cache.hasDenseStub() && obj->isNative() && idval.isInt32()) {
|
||||
if (!attachedStub && !cache.hasDenseStub() && canAttachDenseElement(obj, idval)) {
|
||||
if (!cache.attachDenseElement(cx, ion, obj, idval))
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub && obj->is<TypedArrayObject>()) {
|
||||
if ((idval.isInt32()) ||
|
||||
(idval.isString() && GetIndexFromString(idval.toString()) != UINT32_MAX))
|
||||
{
|
||||
Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
|
||||
int arrayType = tarr->type();
|
||||
bool floatOutput = arrayType == TypedArrayObject::TYPE_FLOAT32 ||
|
||||
arrayType == TypedArrayObject::TYPE_FLOAT64;
|
||||
if (!floatOutput || cache.output().hasValue()) {
|
||||
if (!cache.attachTypedArrayElement(cx, ion, tarr, idval))
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
}
|
||||
if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
|
||||
Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
|
||||
if (!cache.attachTypedArrayElement(cx, ion, tarr, idval))
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2603,7 +2642,6 @@ SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, c
|
||||
RepatchStubAppender attacher(*this);
|
||||
|
||||
// Guard object is a dense array.
|
||||
RootedObject globalObj(cx, &script->global());
|
||||
RootedShape shape(cx, obj->lastProperty());
|
||||
if (!shape)
|
||||
return false;
|
||||
@ -2713,6 +2751,120 @@ SetElementIC::reset()
|
||||
hasDenseStub_ = false;
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelGetElementIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj,
|
||||
const Value &idval, PropertyName *name, JSObject *holder,
|
||||
Shape *shape)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
DispatchStubPrepender attacher(*this);
|
||||
|
||||
// Guard on the index value.
|
||||
Label failures;
|
||||
ValueOperand val = index().reg().valueReg();
|
||||
masm.branchTestValue(Assembler::NotEqual, val, idval, &failures);
|
||||
|
||||
GenerateReadSlot(cx, ion, masm, attacher, obj, name, holder, shape, object(), output(),
|
||||
&failures);
|
||||
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "parallel getelem reading");
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelGetElementIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj,
|
||||
const Value &idval)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
DispatchStubPrepender attacher(*this);
|
||||
if (!GenerateDenseElement(cx, masm, attacher, obj, idval, object(), index(), output()))
|
||||
return false;
|
||||
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "parallel dense element");
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelGetElementIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
|
||||
TypedArrayObject *tarr, const Value &idval)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
DispatchStubPrepender attacher(*this);
|
||||
GenerateTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output());
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
|
||||
}
|
||||
|
||||
ParallelResult
|
||||
ParallelGetElementIC::update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj,
|
||||
HandleValue idval, MutableHandleValue vp)
|
||||
{
|
||||
AutoFlushCache afc("ParallelGetElementCache");
|
||||
PerThreadData *pt = slice->perThreadData;
|
||||
|
||||
const SafepointIndex *safepointIndex;
|
||||
void *returnAddr;
|
||||
RootedScript topScript(pt, GetTopIonJSScript(pt, &safepointIndex, &returnAddr));
|
||||
IonScript *ion = topScript->parallelIonScript();
|
||||
|
||||
ParallelGetElementIC &cache = ion->getCache(cacheIndex).toParallelGetElement();
|
||||
|
||||
// Try to get the element early, as the pure path doesn't need a lock. If
|
||||
// we can't do it purely, bail out of parallel execution.
|
||||
if (!GetObjectElementOperationPure(slice, obj, idval, vp.address()))
|
||||
return TP_RETRY_SEQUENTIALLY;
|
||||
|
||||
// Avoid unnecessary locking if cannot attach stubs.
|
||||
if (!cache.canAttachStub())
|
||||
return TP_SUCCESS;
|
||||
|
||||
{
|
||||
LockedJSContext cx(slice);
|
||||
|
||||
if (cache.canAttachStub()) {
|
||||
bool alreadyStubbed;
|
||||
if (!cache.hasOrAddStubbedShape(cx, obj->lastProperty(), &alreadyStubbed))
|
||||
return TP_FATAL;
|
||||
if (alreadyStubbed)
|
||||
return TP_SUCCESS;
|
||||
|
||||
jsid id;
|
||||
if (!ValueToIdPure(idval, &id))
|
||||
return TP_FATAL;
|
||||
|
||||
bool attachedStub = false;
|
||||
|
||||
if (cache.monitoredResult() &&
|
||||
GetElementIC::canAttachGetProp(obj, idval, id))
|
||||
{
|
||||
RootedShape shape(cx);
|
||||
RootedObject holder(cx);
|
||||
PropertyName *name = JSID_TO_ATOM(id)->asPropertyName();
|
||||
if (ParallelGetPropertyIC::canAttachReadSlot(cx, cache, cache.output(), obj,
|
||||
name, &holder, &shape))
|
||||
{
|
||||
if (!cache.attachReadSlot(cx, ion, obj, idval, name, holder, shape))
|
||||
return TP_FATAL;
|
||||
attachedStub = true;
|
||||
}
|
||||
}
|
||||
if (!attachedStub &&
|
||||
GetElementIC::canAttachDenseElement(obj, idval))
|
||||
{
|
||||
if (!cache.attachDenseElement(cx, ion, obj, idval))
|
||||
return TP_FATAL;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub &&
|
||||
GetElementIC::canAttachTypedArrayElement(obj, idval, cache.output()))
|
||||
{
|
||||
if (!cache.attachTypedArrayElement(cx, ion, &obj->as<TypedArrayObject>(), idval))
|
||||
return TP_FATAL;
|
||||
attachedStub = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TP_SUCCESS;
|
||||
}
|
||||
|
||||
bool
|
||||
BindNameIC::attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain)
|
||||
{
|
||||
|
@ -29,7 +29,8 @@ namespace ion {
|
||||
_(BindName) \
|
||||
_(Name) \
|
||||
_(CallsiteClone) \
|
||||
_(ParallelGetProperty)
|
||||
_(ParallelGetProperty) \
|
||||
_(ParallelGetElement)
|
||||
|
||||
// Forward declarations of Cache kinds.
|
||||
#define FORWARD_DECLARE(kind) class kind##IC;
|
||||
@ -669,6 +670,11 @@ class GetElementIC : public RepatchIonCache
|
||||
hasDenseStub_ = true;
|
||||
}
|
||||
|
||||
static bool canAttachGetProp(JSObject *obj, const Value &idval, jsid id);
|
||||
static bool canAttachDenseElement(JSObject *obj, const Value &idval);
|
||||
static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval,
|
||||
TypedOrValueRegister output);
|
||||
|
||||
bool attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj, const Value &idval, HandlePropertyName name);
|
||||
bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval);
|
||||
bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
|
||||
@ -924,8 +930,11 @@ class ParallelGetPropertyIC : public ParallelIonCache
|
||||
return output_;
|
||||
}
|
||||
|
||||
bool canAttachReadSlot(LockedJSContext &cx, JSObject *obj, MutableHandleObject holder,
|
||||
MutableHandleShape shape);
|
||||
static bool canAttachReadSlot(LockedJSContext &cx, IonCache &cache,
|
||||
TypedOrValueRegister output, JSObject *obj,
|
||||
PropertyName *name, MutableHandleObject holder,
|
||||
MutableHandleShape shape);
|
||||
|
||||
bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, JSObject *holder,
|
||||
Shape *shape);
|
||||
bool attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj);
|
||||
@ -934,6 +943,57 @@ class ParallelGetPropertyIC : public ParallelIonCache
|
||||
MutableHandleValue vp);
|
||||
};
|
||||
|
||||
class ParallelGetElementIC : public ParallelIonCache
|
||||
{
|
||||
protected:
|
||||
Register object_;
|
||||
ConstantOrRegister index_;
|
||||
TypedOrValueRegister output_;
|
||||
|
||||
bool monitoredResult_ : 1;
|
||||
|
||||
public:
|
||||
ParallelGetElementIC(Register object, ConstantOrRegister index,
|
||||
TypedOrValueRegister output, bool monitoredResult)
|
||||
: object_(object),
|
||||
index_(index),
|
||||
output_(output),
|
||||
monitoredResult_(monitoredResult)
|
||||
{
|
||||
}
|
||||
|
||||
CACHE_HEADER(ParallelGetElement)
|
||||
|
||||
#ifdef JS_CPU_X86
|
||||
// x86 lacks a general purpose scratch register for dispatch caches and
|
||||
// must be given one manually.
|
||||
void initializeAddCacheState(LInstruction *ins, AddCacheState *addState);
|
||||
#endif
|
||||
|
||||
Register object() const {
|
||||
return object_;
|
||||
}
|
||||
ConstantOrRegister index() const {
|
||||
return index_;
|
||||
}
|
||||
TypedOrValueRegister output() const {
|
||||
return output_;
|
||||
}
|
||||
bool monitoredResult() const {
|
||||
return monitoredResult_;
|
||||
}
|
||||
|
||||
bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval,
|
||||
PropertyName *name, JSObject *holder, Shape *shape);
|
||||
bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval);
|
||||
bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr,
|
||||
const Value &idval);
|
||||
|
||||
static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, HandleValue idval,
|
||||
MutableHandleValue vp);
|
||||
|
||||
};
|
||||
|
||||
#undef CACHE_HEADER
|
||||
|
||||
// Implement cache casts now that the compiler can see the inheritance.
|
||||
|
@ -3725,14 +3725,16 @@ class LGetElementCacheV : public LInstructionHelper<BOX_PIECES, 1 + BOX_PIECES,
|
||||
}
|
||||
};
|
||||
|
||||
class LGetElementCacheT : public LInstructionHelper<1, 2, 0>
|
||||
class LGetElementCacheT : public LInstructionHelper<1, 2, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(GetElementCacheT)
|
||||
|
||||
LGetElementCacheT(const LAllocation &object, const LAllocation &index) {
|
||||
LGetElementCacheT(const LAllocation &object, const LAllocation &index,
|
||||
const LDefinition &temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, index);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
const LAllocation *object() {
|
||||
return getOperand(0);
|
||||
@ -3743,6 +3745,9 @@ class LGetElementCacheT : public LInstructionHelper<1, 2, 0>
|
||||
const LDefinition *output() {
|
||||
return getDef(0);
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const MGetElementCache *mir() const {
|
||||
return mir_->toGetElementCache();
|
||||
}
|
||||
|
@ -2331,8 +2331,7 @@ LIRGenerator::visitGetElementCache(MGetElementCache *ins)
|
||||
}
|
||||
|
||||
JS_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
LGetElementCacheT *lir = new LGetElementCacheT(useRegister(ins->object()),
|
||||
useRegister(ins->index()));
|
||||
LGetElementCacheT *lir = newLGetElementCacheT(ins);
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
SAFE_OP(GetPropertyCache)
|
||||
SAFE_OP(GetPropertyPolymorphic)
|
||||
UNSAFE_OP(SetPropertyPolymorphic)
|
||||
UNSAFE_OP(GetElementCache)
|
||||
SAFE_OP(GetElementCache)
|
||||
UNSAFE_OP(SetElementCache)
|
||||
UNSAFE_OP(BindNameCache)
|
||||
SAFE_OP(GuardShape)
|
||||
|
@ -325,6 +325,14 @@ LIRGeneratorARM::newLGetPropertyCacheT(MGetPropertyCache *ins)
|
||||
return new LGetPropertyCacheT(useRegister(ins->object()), LDefinition::BogusTemp());
|
||||
}
|
||||
|
||||
LGetElementCacheT *
|
||||
LIRGeneratorARM::newLGetElementCacheT(MGetElementCache *ins)
|
||||
{
|
||||
return new LGetElementCacheT(useRegister(ins->object()),
|
||||
useRegister(ins->index()),
|
||||
LDefinition::BogusTemp());
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorARM::visitGuardShape(MGuardShape *ins)
|
||||
{
|
||||
|
@ -61,6 +61,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
||||
MTableSwitch *ins);
|
||||
LTableSwitchV *newLTableSwitchV(MTableSwitch *ins);
|
||||
LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
|
||||
LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
|
||||
|
||||
public:
|
||||
bool visitConstant(MConstant *ins);
|
||||
|
@ -175,6 +175,14 @@ LIRGeneratorX64::newLGetPropertyCacheT(MGetPropertyCache *ins)
|
||||
return new LGetPropertyCacheT(useRegister(ins->object()), LDefinition::BogusTemp());
|
||||
}
|
||||
|
||||
LGetElementCacheT *
|
||||
LIRGeneratorX64::newLGetElementCacheT(MGetElementCache *ins)
|
||||
{
|
||||
return new LGetElementCacheT(useRegister(ins->object()),
|
||||
useRegister(ins->index()),
|
||||
LDefinition::BogusTemp());
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
|
||||
{
|
||||
|
@ -31,6 +31,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
|
||||
LDefinition tempToUnbox();
|
||||
|
||||
LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
|
||||
LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
|
||||
|
||||
public:
|
||||
bool visitBox(MBox *box);
|
||||
|
@ -674,6 +674,18 @@ ParallelGetPropertyIC::initializeAddCacheState(LInstruction *ins, AddCacheState
|
||||
addState->dispatchScratch = ToRegister(ins->toGetPropertyCacheT()->temp());
|
||||
}
|
||||
|
||||
void
|
||||
ParallelGetElementIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
|
||||
{
|
||||
// We don't have a scratch register, but only use the temp if we needed
|
||||
// one, it's BogusTemp otherwise.
|
||||
JS_ASSERT(ins->isGetElementCacheV() || ins->isGetElementCacheT());
|
||||
if (ins->isGetElementCacheV() || ins->toGetElementCacheT()->temp()->isBogusTemp())
|
||||
addState->dispatchScratch = output_.scratchReg().gpr();
|
||||
else
|
||||
addState->dispatchScratch = ToRegister(ins->toGetElementCacheT()->temp());
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace ion {
|
||||
|
||||
|
@ -286,3 +286,16 @@ LIRGeneratorX86::newLGetPropertyCacheT(MGetPropertyCache *ins)
|
||||
scratch = LDefinition::BogusTemp();
|
||||
return new LGetPropertyCacheT(useRegister(ins->object()), scratch);
|
||||
}
|
||||
|
||||
LGetElementCacheT *
|
||||
LIRGeneratorX86::newLGetElementCacheT(MGetElementCache *ins)
|
||||
{
|
||||
LDefinition scratch;
|
||||
if (ins->type() == MIRType_Double)
|
||||
scratch = temp();
|
||||
else
|
||||
scratch = LDefinition::BogusTemp();
|
||||
return new LGetElementCacheT(useRegister(ins->object()),
|
||||
useRegister(ins->index()),
|
||||
scratch);
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
|
||||
bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
|
||||
|
||||
LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
|
||||
LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
|
||||
|
||||
public:
|
||||
bool visitBox(MBox *box);
|
||||
|
@ -0,0 +1,64 @@
|
||||
load(libdir + "parallelarray-helpers.js");
|
||||
|
||||
function f(a) {
|
||||
compareAgainstArray(
|
||||
range(0, minItemsTestingThreshold),
|
||||
"map",
|
||||
function(i) { return a[i]; });
|
||||
}
|
||||
|
||||
function g(a, x) {
|
||||
compareAgainstArray(
|
||||
range(0, minItemsTestingThreshold),
|
||||
"map",
|
||||
function(i) { return a[x]; });
|
||||
}
|
||||
|
||||
function testICDenseElement() {
|
||||
var a1 = [];
|
||||
var a2 = [];
|
||||
var a3 = [];
|
||||
var a4 = [];
|
||||
var len = minItemsTestingThreshold;
|
||||
for (var i = 0; i < len; i++) {
|
||||
a1[i] = Math.random() * 100 | 0;
|
||||
a2[i] = Math.random() * 100 | 0;
|
||||
a3[i] = Math.random() * 100 | 0;
|
||||
a4[i] = Math.random() * 100 | 0;
|
||||
}
|
||||
f(a1); f({}); f(a2); f(a3); f(a4); f(a3); f(a1);
|
||||
}
|
||||
|
||||
function testICTypedArrayElement() {
|
||||
var specs = [Int8Array,
|
||||
Uint8Array,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array,
|
||||
Uint8ClampedArray];
|
||||
var len = minItemsTestingThreshold;
|
||||
f({});
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
var ta = new specs[i](len);
|
||||
for (var j = 0; j < len; j++)
|
||||
ta[j] = Math.random() * 100;
|
||||
f(ta);
|
||||
}
|
||||
}
|
||||
|
||||
function testICSlotElement() {
|
||||
var o1 = { foo: 0 };
|
||||
var o2 = { foo: 0, bar: '' };
|
||||
var o3 = { foo: 0, bar: '', baz: function () { } };
|
||||
var o4 = { foo: { } };
|
||||
g(o1, "foo"); g(o2, "foo"); g(o3, "foo"); g(o2, "foo"); g(o1, "foo"); g(o4, "foo");
|
||||
}
|
||||
|
||||
if (getBuildConfiguration().parallelJS) {
|
||||
testICDenseElement();
|
||||
testICTypedArrayElement();
|
||||
testICSlotElement();
|
||||
}
|
@ -594,7 +594,7 @@ js::ParallelDo::apply()
|
||||
// compiled scripts were collected.
|
||||
if (ParallelTestsShouldPass(cx_) && worklist_.length() != 0) {
|
||||
JS_ReportError(cx_, "ForkJoin: compilation required in par or bailout mode");
|
||||
return ExecutionFatal;
|
||||
return SpewEndOp(ExecutionFatal);
|
||||
}
|
||||
break;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user