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;
}
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)
{

View File

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

View File

@ -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)
{

View File

@ -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.

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

View File

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

View File

@ -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)

View File

@ -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)
{

View File

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

View File

@ -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)
{

View File

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

View File

@ -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 {

View File

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

View File

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

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.
if (ParallelTestsShouldPass(cx_) && worklist_.length() != 0) {
JS_ReportError(cx_, "ForkJoin: compilation required in par or bailout mode");
return ExecutionFatal;
return SpewEndOp(ExecutionFatal);
}
break;