Bug 881536 - Part 4: Implement ParallelGetElementIC. (r=nbp)

This commit is contained in:
Shu-yu Guo 2013-07-08 03:24:55 -07:00
parent 9d5cb5bd4f
commit b97ad254e9
16 changed files with 461 additions and 97 deletions

View File

@ -5809,6 +5809,24 @@ CodeGenerator::visitParallelGetPropertyIC(OutOfLineUpdateCache *ool, ParallelGet
return true; 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 bool
CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins) CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins)
{ {
@ -5816,9 +5834,7 @@ CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins)
ConstantOrRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index)); ConstantOrRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index));
TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins)); TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
GetElementIC cache(obj, index, output, ins->mir()->monitoredResult()); return addGetElementCache(ins, obj, index, output, ins->mir()->monitoredResult());
return addCache(ins, allocateCache(cache));
} }
bool bool
@ -5828,9 +5844,7 @@ CodeGenerator::visitGetElementCacheT(LGetElementCacheT *ins)
ConstantOrRegister index = TypedOrValueRegister(MIRType_Int32, ToAnyRegister(ins->index())); ConstantOrRegister index = TypedOrValueRegister(MIRType_Int32, ToAnyRegister(ins->index()));
TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output())); TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output()));
GetElementIC cache(obj, index, output, ins->mir()->monitoredResult()); return addGetElementCache(ins, obj, index, output, ins->mir()->monitoredResult());
return addCache(ins, allocateCache(cache));
} }
typedef bool (*GetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue); typedef bool (*GetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue);
@ -5908,6 +5922,29 @@ CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, SetElementIC *ic)
return true; 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 bool
CodeGenerator::visitBindNameCache(LBindNameCache *ins) CodeGenerator::visitBindNameCache(LBindNameCache *ins)
{ {

View File

@ -287,6 +287,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitParallelGetPropertyIC(OutOfLineUpdateCache *ool, ParallelGetPropertyIC *ic); bool visitParallelGetPropertyIC(OutOfLineUpdateCache *ool, ParallelGetPropertyIC *ic);
bool visitSetPropertyIC(OutOfLineUpdateCache *ool, SetPropertyIC *ic); bool visitSetPropertyIC(OutOfLineUpdateCache *ool, SetPropertyIC *ic);
bool visitGetElementIC(OutOfLineUpdateCache *ool, GetElementIC *ic); bool visitGetElementIC(OutOfLineUpdateCache *ool, GetElementIC *ic);
bool visitParallelGetElementIC(OutOfLineUpdateCache *ool, ParallelGetElementIC *ic);
bool visitSetElementIC(OutOfLineUpdateCache *ool, SetElementIC *ic); bool visitSetElementIC(OutOfLineUpdateCache *ool, SetElementIC *ic);
bool visitBindNameIC(OutOfLineUpdateCache *ool, BindNameIC *ic); bool visitBindNameIC(OutOfLineUpdateCache *ool, BindNameIC *ic);
bool visitNameIC(OutOfLineUpdateCache *ool, NameIC *ic); bool visitNameIC(OutOfLineUpdateCache *ool, NameIC *ic);
@ -302,6 +303,8 @@ class CodeGenerator : public CodeGeneratorSpecific
bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg, bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
PropertyName *name, TypedOrValueRegister output, PropertyName *name, TypedOrValueRegister output,
bool allowGetters); bool allowGetters);
bool addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
TypedOrValueRegister output, bool monitoredResult);
bool checkForParallelBailout(LInstruction *lir); bool checkForParallelBailout(LInstruction *lir);
bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr); bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);

View File

@ -1057,8 +1057,8 @@ bool
GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape) HandleShape shape)
{ {
RepatchStubAppender attacher(*this);
MacroAssembler masm(cx); MacroAssembler masm(cx);
RepatchStubAppender attacher(*this);
GenerateReadSlot(cx, ion, masm, attacher, obj, name(), holder, shape, object(), output()); GenerateReadSlot(cx, ion, masm, attacher, obj, name(), holder, shape, object(), output());
const char *attachKind = "non idempotent reading"; const char *attachKind = "non idempotent reading";
if (idempotent()) if (idempotent())
@ -1600,22 +1600,24 @@ ParallelIonCache::destroy()
js_delete(stubbedShapes_); js_delete(stubbedShapes_);
} }
bool /* static */ bool
ParallelGetPropertyIC::canAttachReadSlot(LockedJSContext &cx, JSObject *obj, ParallelGetPropertyIC::canAttachReadSlot(LockedJSContext &cx, IonCache &cache,
MutableHandleObject holder, MutableHandleShape shape) TypedOrValueRegister output, JSObject *obj,
PropertyName *name, MutableHandleObject holder,
MutableHandleShape shape)
{ {
// Bail if we have hooks or are not native. // Bail if we have hooks or are not native.
if (!obj->hasIdempotentProtoChain()) if (!obj->hasIdempotentProtoChain())
return false; return false;
if (!js::LookupPropertyPure(obj, NameToId(name()), holder.address(), shape.address())) if (!js::LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address()))
return false; return false;
// In parallel execution we can't cache getters due to possible // In parallel execution we can't cache getters due to possible
// side-effects, so only check if we can cache slot reads. // side-effects, so only check if we can cache slot reads.
bool readSlot; bool readSlot;
bool callGetter; 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) &readSlot, &callGetter) || !readSlot)
{ {
return false; return false;
@ -1689,7 +1691,9 @@ ParallelGetPropertyIC::update(ForkJoinSlice *slice, size_t cacheIndex,
{ {
RootedShape shape(cx); RootedShape shape(cx);
RootedObject holder(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)) if (!cache.attachReadSlot(cx, ion, obj, holder, shape))
return TP_FATAL; return TP_FATAL;
attachedStub = true; attachedStub = true;
@ -2159,6 +2163,16 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
const size_t GetElementIC::MAX_FAILED_UPDATES = 16; 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 bool
GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj, GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
const Value &idval, HandlePropertyName name) const Value &idval, HandlePropertyName name)
@ -2196,108 +2210,137 @@ GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
return linkAndAttachStub(cx, masm, attacher, ion, "property"); return linkAndAttachStub(cx, masm, attacher, ion, "property");
} }
bool /* static */ bool
GetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval) GetElementIC::canAttachDenseElement(JSObject *obj, const Value &idval)
{ {
JS_ASSERT(obj->isNative()); return obj->isNative() && idval.isInt32();
JS_ASSERT(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; Label failures;
MacroAssembler masm(cx);
RepatchStubAppender attacher(*this);
// Guard object's shape. // Guard object's shape.
RootedObject globalObj(cx, &script->global());
RootedShape shape(cx, obj->lastProperty()); RootedShape shape(cx, obj->lastProperty());
if (!shape) if (!shape)
return false; return false;
masm.branchTestObjShape(Assembler::NotEqual, object(), shape, &failures); masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
// Ensure the index is an int32 value. // Ensure the index is an int32 value.
Register indexReg = InvalidReg; Register indexReg = InvalidReg;
if (index().reg().hasValue()) { if (index.reg().hasValue()) {
indexReg = output().scratchReg().gpr(); indexReg = output.scratchReg().gpr();
JS_ASSERT(indexReg != InvalidReg); JS_ASSERT(indexReg != InvalidReg);
ValueOperand val = index().reg().valueReg(); ValueOperand val = index.reg().valueReg();
masm.branchTestInt32(Assembler::NotEqual, val, &failures); masm.branchTestInt32(Assembler::NotEqual, val, &failures);
// Unbox the index. // Unbox the index.
masm.unboxInt32(val, indexReg); masm.unboxInt32(val, indexReg);
} else { } else {
JS_ASSERT(!index().reg().typedReg().isFloat()); JS_ASSERT(!index.reg().typedReg().isFloat());
indexReg = index().reg().typedReg().gpr(); indexReg = index.reg().typedReg().gpr();
} }
// Load elements vector. // Load elements vector.
masm.push(object()); masm.push(object);
masm.loadPtr(Address(object(), JSObject::offsetOfElements()), object()); masm.loadPtr(Address(object, JSObject::offsetOfElements()), object);
Label hole; Label hole;
// Guard on the initialized length. // Guard on the initialized length.
Address initLength(object(), ObjectElements::offsetOfInitializedLength()); Address initLength(object, ObjectElements::offsetOfInitializedLength());
masm.branch32(Assembler::BelowOrEqual, initLength, indexReg, &hole); masm.branch32(Assembler::BelowOrEqual, initLength, indexReg, &hole);
// Check for holes & load the value. // Check for holes & load the value.
masm.loadElementTypedOrValue(BaseIndex(object(), indexReg, TimesEight), masm.loadElementTypedOrValue(BaseIndex(object, indexReg, TimesEight),
output(), true, &hole); output, true, &hole);
masm.pop(object()); masm.pop(object);
attacher.jumpRejoin(masm); attacher.jumpRejoin(masm);
// All failures flow to here. // All failures flow to here.
masm.bind(&hole); masm.bind(&hole);
masm.pop(object()); masm.pop(object);
masm.bind(&failures); masm.bind(&failures);
attacher.jumpNextStub(masm); 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(); setHasDenseStub();
return linkAndAttachStub(cx, masm, attacher, ion, "dense array"); return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
} }
bool /* static */ bool
GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr, GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
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; Label failures;
MacroAssembler masm(cx);
RepatchStubAppender attacher(*this);
// The array type is the object within the table of typed array classes. // The array type is the object within the table of typed array classes.
int arrayType = tarr->type(); int arrayType = tarr->type();
// The output register is not yet specialized as a float register, the only Register tmpReg = output.scratchReg().gpr();
// 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();
JS_ASSERT(tmpReg != InvalidReg); JS_ASSERT(tmpReg != InvalidReg);
// Check that the typed array is of the same type as the current object // 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. // 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 // Decide to what type index the stub should be optimized
Register indexReg = tmpReg; Register indexReg = tmpReg;
JS_ASSERT(!index().constant()); JS_ASSERT(!index.constant());
if (idval.isString()) { if (idval.isString()) {
JS_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX); JS_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX);
// Part 1: Get the string into a register // Part 1: Get the string into a register
Register str; Register str;
if (index().reg().hasValue()) { if (index.reg().hasValue()) {
ValueOperand val = index().reg().valueReg(); ValueOperand val = index.reg().valueReg();
masm.branchTestString(Assembler::NotEqual, val, &failures); masm.branchTestString(Assembler::NotEqual, val, &failures);
str = masm.extractString(val, indexReg); str = masm.extractString(val, indexReg);
} else { } else {
JS_ASSERT(!index().reg().typedReg().isFloat()); JS_ASSERT(!index.reg().typedReg().isFloat());
str = index().reg().typedReg().gpr(); str = index.reg().typedReg().gpr();
} }
// Part 2: Call to translate the str into index // Part 2: Call to translate the str into index
@ -2321,51 +2364,59 @@ GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayO
} else { } else {
JS_ASSERT(idval.isInt32()); JS_ASSERT(idval.isInt32());
if (index().reg().hasValue()) { if (index.reg().hasValue()) {
ValueOperand val = index().reg().valueReg(); ValueOperand val = index.reg().valueReg();
masm.branchTestInt32(Assembler::NotEqual, val, &failures); masm.branchTestInt32(Assembler::NotEqual, val, &failures);
// Unbox the index. // Unbox the index.
masm.unboxInt32(val, indexReg); masm.unboxInt32(val, indexReg);
} else { } else {
JS_ASSERT(!index().reg().typedReg().isFloat()); JS_ASSERT(!index.reg().typedReg().isFloat());
indexReg = index().reg().typedReg().gpr(); indexReg = index.reg().typedReg().gpr();
} }
} }
// Guard on the initialized length. // Guard on the initialized length.
Address length(object(), TypedArrayObject::lengthOffset()); Address length(object, TypedArrayObject::lengthOffset());
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures); masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
// Save the object register on the stack in case of failure. // Save the object register on the stack in case of failure.
Label popAndFail; Label popAndFail;
Register elementReg = object(); Register elementReg = object;
masm.push(object()); masm.push(object);
// Load elements vector. // 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 // Load the value. We use an invalid register because the destination
// register is necessary a non double register. // register is necessary a non double register.
int width = TypedArrayObject::slotWidth(arrayType); int width = TypedArrayObject::slotWidth(arrayType);
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width)); BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
if (output().hasValue()) if (output.hasValue())
masm.loadFromTypedArray(arrayType, source, output().valueReg(), true, masm.loadFromTypedArray(arrayType, source, output.valueReg(), true,
elementReg, &popAndFail); elementReg, &popAndFail);
else else
masm.loadFromTypedArray(arrayType, source, output().typedReg(), masm.loadFromTypedArray(arrayType, source, output.typedReg(),
elementReg, &popAndFail); elementReg, &popAndFail);
masm.pop(object()); masm.pop(object);
attacher.jumpRejoin(masm); attacher.jumpRejoin(masm);
// Restore the object before continuing to the next stub. // Restore the object before continuing to the next stub.
masm.bind(&popAndFail); masm.bind(&popAndFail);
masm.pop(object()); masm.pop(object);
masm.bind(&failures); masm.bind(&failures);
attacher.jumpNextStub(masm); 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"); return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
} }
@ -2517,34 +2568,22 @@ GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
return false; return false;
attachedStub = true; attachedStub = true;
} }
if (!attachedStub && obj->isNative() && cache.monitoredResult()) { if (!attachedStub && cache.monitoredResult() && canAttachGetProp(obj, idval, id)) {
uint32_t dummy; RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
if (idval.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy)) { if (!cache.attachGetProp(cx, ion, obj, idval, name))
RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName()); return false;
if (!cache.attachGetProp(cx, ion, obj, idval, name)) attachedStub = true;
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)) if (!cache.attachDenseElement(cx, ion, obj, idval))
return false; return false;
attachedStub = true; attachedStub = true;
} }
if (!attachedStub && obj->is<TypedArrayObject>()) { if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
if ((idval.isInt32()) || Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
(idval.isString() && GetIndexFromString(idval.toString()) != UINT32_MAX)) if (!cache.attachTypedArrayElement(cx, ion, tarr, idval))
{ return false;
Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>()); attachedStub = true;
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;
}
}
} }
} }
@ -2603,7 +2642,6 @@ SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, c
RepatchStubAppender attacher(*this); RepatchStubAppender attacher(*this);
// Guard object is a dense array. // Guard object is a dense array.
RootedObject globalObj(cx, &script->global());
RootedShape shape(cx, obj->lastProperty()); RootedShape shape(cx, obj->lastProperty());
if (!shape) if (!shape)
return false; return false;
@ -2713,6 +2751,120 @@ SetElementIC::reset()
hasDenseStub_ = false; 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 bool
BindNameIC::attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain) BindNameIC::attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain)
{ {

View File

@ -29,7 +29,8 @@ namespace ion {
_(BindName) \ _(BindName) \
_(Name) \ _(Name) \
_(CallsiteClone) \ _(CallsiteClone) \
_(ParallelGetProperty) _(ParallelGetProperty) \
_(ParallelGetElement)
// Forward declarations of Cache kinds. // Forward declarations of Cache kinds.
#define FORWARD_DECLARE(kind) class kind##IC; #define FORWARD_DECLARE(kind) class kind##IC;
@ -669,6 +670,11 @@ class GetElementIC : public RepatchIonCache
hasDenseStub_ = true; 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 attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj, const Value &idval, HandlePropertyName name);
bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval);
bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr, bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
@ -924,8 +930,11 @@ class ParallelGetPropertyIC : public ParallelIonCache
return output_; return output_;
} }
bool canAttachReadSlot(LockedJSContext &cx, JSObject *obj, MutableHandleObject holder, static bool canAttachReadSlot(LockedJSContext &cx, IonCache &cache,
MutableHandleShape shape); TypedOrValueRegister output, JSObject *obj,
PropertyName *name, MutableHandleObject holder,
MutableHandleShape shape);
bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, JSObject *holder, bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, JSObject *holder,
Shape *shape); Shape *shape);
bool attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj); bool attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj);
@ -934,6 +943,57 @@ class ParallelGetPropertyIC : public ParallelIonCache
MutableHandleValue vp); 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 #undef CACHE_HEADER
// Implement cache casts now that the compiler can see the inheritance. // Implement cache casts now that the compiler can see the inheritance.

View File

@ -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: public:
LIR_HEADER(GetElementCacheT) LIR_HEADER(GetElementCacheT)
LGetElementCacheT(const LAllocation &object, const LAllocation &index) { LGetElementCacheT(const LAllocation &object, const LAllocation &index,
const LDefinition &temp) {
setOperand(0, object); setOperand(0, object);
setOperand(1, index); setOperand(1, index);
setTemp(0, temp);
} }
const LAllocation *object() { const LAllocation *object() {
return getOperand(0); return getOperand(0);
@ -3743,6 +3745,9 @@ class LGetElementCacheT : public LInstructionHelper<1, 2, 0>
const LDefinition *output() { const LDefinition *output() {
return getDef(0); return getDef(0);
} }
const LDefinition *temp() {
return getTemp(0);
}
const MGetElementCache *mir() const { const MGetElementCache *mir() const {
return mir_->toGetElementCache(); return mir_->toGetElementCache();
} }

View File

@ -2331,8 +2331,7 @@ LIRGenerator::visitGetElementCache(MGetElementCache *ins)
} }
JS_ASSERT(ins->index()->type() == MIRType_Int32); JS_ASSERT(ins->index()->type() == MIRType_Int32);
LGetElementCacheT *lir = new LGetElementCacheT(useRegister(ins->object()), LGetElementCacheT *lir = newLGetElementCacheT(ins);
useRegister(ins->index()));
return define(lir, ins) && assignSafepoint(lir, ins); return define(lir, ins) && assignSafepoint(lir, ins);
} }

View File

@ -196,7 +196,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
SAFE_OP(GetPropertyCache) SAFE_OP(GetPropertyCache)
SAFE_OP(GetPropertyPolymorphic) SAFE_OP(GetPropertyPolymorphic)
UNSAFE_OP(SetPropertyPolymorphic) UNSAFE_OP(SetPropertyPolymorphic)
UNSAFE_OP(GetElementCache) SAFE_OP(GetElementCache)
UNSAFE_OP(SetElementCache) UNSAFE_OP(SetElementCache)
UNSAFE_OP(BindNameCache) UNSAFE_OP(BindNameCache)
SAFE_OP(GuardShape) SAFE_OP(GuardShape)

View File

@ -325,6 +325,14 @@ LIRGeneratorARM::newLGetPropertyCacheT(MGetPropertyCache *ins)
return new LGetPropertyCacheT(useRegister(ins->object()), LDefinition::BogusTemp()); 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 bool
LIRGeneratorARM::visitGuardShape(MGuardShape *ins) LIRGeneratorARM::visitGuardShape(MGuardShape *ins)
{ {

View File

@ -61,6 +61,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
MTableSwitch *ins); MTableSwitch *ins);
LTableSwitchV *newLTableSwitchV(MTableSwitch *ins); LTableSwitchV *newLTableSwitchV(MTableSwitch *ins);
LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
public: public:
bool visitConstant(MConstant *ins); bool visitConstant(MConstant *ins);

View File

@ -175,6 +175,14 @@ LIRGeneratorX64::newLGetPropertyCacheT(MGetPropertyCache *ins)
return new LGetPropertyCacheT(useRegister(ins->object()), LDefinition::BogusTemp()); 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 bool
LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins) LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
{ {

View File

@ -31,6 +31,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
LDefinition tempToUnbox(); LDefinition tempToUnbox();
LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
public: public:
bool visitBox(MBox *box); bool visitBox(MBox *box);

View File

@ -674,6 +674,18 @@ ParallelGetPropertyIC::initializeAddCacheState(LInstruction *ins, AddCacheState
addState->dispatchScratch = ToRegister(ins->toGetPropertyCacheT()->temp()); 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 js {
namespace ion { namespace ion {

View File

@ -286,3 +286,16 @@ LIRGeneratorX86::newLGetPropertyCacheT(MGetPropertyCache *ins)
scratch = LDefinition::BogusTemp(); scratch = LDefinition::BogusTemp();
return new LGetPropertyCacheT(useRegister(ins->object()), scratch); 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);
}

View File

@ -34,6 +34,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
bool defineUntypedPhi(MPhi *phi, size_t lirIndex); bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins); LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
public: public:
bool visitBox(MBox *box); bool visitBox(MBox *box);

View File

@ -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();
}

View File

@ -594,7 +594,7 @@ js::ParallelDo::apply()
// compiled scripts were collected. // compiled scripts were collected.
if (ParallelTestsShouldPass(cx_) && worklist_.length() != 0) { if (ParallelTestsShouldPass(cx_) && worklist_.length() != 0) {
JS_ReportError(cx_, "ForkJoin: compilation required in par or bailout mode"); JS_ReportError(cx_, "ForkJoin: compilation required in par or bailout mode");
return ExecutionFatal; return SpewEndOp(ExecutionFatal);
} }
break; break;