Bug 861596 - Add optimized ArgumentsObject stubs to Ion ICs. r=h4writer

This commit is contained in:
Kannan Vijayan 2013-04-19 21:13:53 -04:00
parent 043d238980
commit bd36917c01
7 changed files with 459 additions and 61 deletions

View File

@ -398,8 +398,14 @@ IonCache::linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &a
attachStub(masm, attacher, code);
IonSpew(IonSpew_InlineCaches, "Generated %s %s stub at %p",
attachKind, CacheName(kind()), code->raw());
if (pc) {
IonSpew(IonSpew_InlineCaches, "Cache %p(%s:%d/%d) generated %s %s stub at %p",
this, script->filename(), script->lineno, pc - script->code,
attachKind, CacheName(kind()), code->raw());
} else {
IonSpew(IonSpew_InlineCaches, "Cache %p generated %s %s stub at %p",
this, attachKind, CacheName(kind()), code->raw());
}
return true;
}
@ -543,6 +549,39 @@ IsCacheableNoProperty(JSObject *obj, JSObject *holder, RawShape shape, jsbytecod
return true;
}
static bool
IsOptimizableArgumentsObjectForLength(JSObject *obj)
{
if (!obj->isArguments())
return false;
if (obj->asArguments().hasOverriddenLength())
return false;
return true;
}
static bool
IsOptimizableArgumentsObjectForGetElem(JSObject *obj, Value idval)
{
if (!IsOptimizableArgumentsObjectForLength(obj))
return false;
ArgumentsObject &argsObj = obj->asArguments();
if (argsObj.isAnyElementDeleted())
return false;
if (!idval.isInt32())
return false;
int32_t idint = idval.toInt32();
if (idint < 0 || idint >= argsObj.initialLength())
return false;
return true;
}
static bool
IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, RawShape shape)
{
@ -1076,6 +1115,61 @@ GetPropertyIC::attachTypedArrayLength(JSContext *cx, IonScript *ion, JSObject *o
return linkAndAttachStub(cx, masm, attacher, ion, "typed array length");
}
bool
GetPropertyIC::attachArgumentsLength(JSContext *cx, IonScript *ion, JSObject *obj)
{
JS_ASSERT(obj->isArguments());
JS_ASSERT(!idempotent());
Label failures;
MacroAssembler masm(cx);
RepatchStubAppender attacher(*this);
Register tmpReg;
if (output().hasValue()) {
tmpReg = output().valueReg().scratchReg();
} else {
JS_ASSERT(output().type() == MIRType_Int32);
tmpReg = output().typedReg().gpr();
}
JS_ASSERT(object() != tmpReg);
Class *clasp = obj->isStrictArguments() ? &StrictArgumentsObjectClass
: &NormalArgumentsObjectClass;
Label fail;
Label pass;
masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, clasp, &failures);
// Get initial ArgsObj length value, test if length has been overridden.
masm.unboxInt32(Address(object(), ArgumentsObject::getInitialLengthSlotOffset()), tmpReg);
masm.branchTest32(Assembler::NonZero, tmpReg, Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
&failures);
masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), tmpReg);
// If output is Int32, result is already in right place, otherwise box it into output.
if (output().hasValue())
masm.tagValue(JSVAL_TYPE_INT32, tmpReg, output().valueReg());
// Success.
attacher.jumpRejoin(masm);
// Failure.
masm.bind(&failures);
attacher.jumpNextStub(masm);
if (obj->isStrictArguments()) {
JS_ASSERT(!hasStrictArgumentsLengthStub_);
hasStrictArgumentsLengthStub_ = true;
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (strict)");
}
JS_ASSERT(!hasNormalArgumentsLengthStub_);
hasNormalArgumentsLengthStub_ = true;
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (normal)");
}
static bool
IsIdempotentAndMaybeHasHooks(IonCache &cache, JSObject *obj)
{
@ -1146,6 +1240,7 @@ TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
void *returnAddr, bool *isCacheable)
{
JS_ASSERT(!*isCacheable);
JS_ASSERT(cache.canAttachStub());
RootedObject checkObj(cx, obj);
if (IsCacheableListBase(obj)) {
@ -1186,10 +1281,6 @@ TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
*isCacheable = true;
// Falback to the interpreter function.
if (!cache.canAttachStub())
return true;
if (readSlot)
return cache.attachReadSlot(cx, ion, obj, holder, shape);
else if (obj->isArray() && !cache.hasArrayLengthStub() && cx->names().length == name)
@ -1221,25 +1312,35 @@ GetPropertyIC::update(JSContext *cx, size_t cacheIndex,
// limit. Once we can make calls from within generated stubs, a new call
// stub will be generated instead and the previous stubs unlinked.
bool isCacheable = false;
if (!TryAttachNativeGetPropStub(cx, ion, cache, obj, name,
safepointIndex, returnAddr,
&isCacheable))
{
return false;
}
if (!isCacheable && cache.canAttachStub() &&
!cache.idempotent() && cx->names().length == name)
{
if (cache.output().type() != MIRType_Value && cache.output().type() != MIRType_Int32) {
// The next execution should cause an invalidation because the type
// does not fit.
isCacheable = false;
} else if (obj->isTypedArray() && !cache.hasTypedArrayLengthStub()) {
if (cache.canAttachStub()) {
if (name == cx->names().length &&
IsOptimizableArgumentsObjectForLength(obj) &&
(cache.output().type() == MIRType_Value || cache.output().type() == MIRType_Int32) &&
!cache.hasArgumentsLengthStub(obj->isStrictArguments()))
{
isCacheable = true;
if (!cache.attachTypedArrayLength(cx, ion, obj))
if (!cache.attachArgumentsLength(cx, ion, obj))
return false;
}
if (!isCacheable && !TryAttachNativeGetPropStub(cx, ion, cache, obj, name,
safepointIndex, returnAddr,
&isCacheable))
{
return false;
}
if (!isCacheable && !cache.idempotent() && cx->names().length == name) {
if (cache.output().type() != MIRType_Value && cache.output().type() != MIRType_Int32) {
// The next execution should cause an invalidation because the type
// does not fit.
isCacheable = false;
} else if (obj->isTypedArray() && !cache.hasTypedArrayLengthStub()) {
isCacheable = true;
if (!cache.attachTypedArrayLength(cx, ion, obj))
return false;
}
}
}
if (cache.idempotent() && !isCacheable) {
@ -2115,6 +2216,114 @@ GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, JSObject *o
return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
}
bool
GetElementIC::attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj)
{
JS_ASSERT(obj->isArguments());
Label failures;
MacroAssembler masm(cx);
RepatchStubAppender attacher(*this);
Register tmpReg = output().scratchReg().gpr();
JS_ASSERT(tmpReg != InvalidReg);
Class *clasp = obj->isStrictArguments() ? &StrictArgumentsObjectClass
: &NormalArgumentsObjectClass;
Label fail;
Label pass;
masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, clasp, &failures);
// Get initial ArgsObj length value, test if length has been overridden.
masm.unboxInt32(Address(object(), ArgumentsObject::getInitialLengthSlotOffset()), tmpReg);
masm.branchTest32(Assembler::NonZero, tmpReg, Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
&failures);
// Decide to what type index the stub should be optimized
Register indexReg;
JS_ASSERT(!index().constant());
// Check index against length.
Label failureRestoreIndex;
if (index().reg().hasValue()) {
ValueOperand val = index().reg().valueReg();
masm.branchTestInt32(Assembler::NotEqual, val, &failures);
indexReg = val.scratchReg();
masm.unboxInt32(val, indexReg);
masm.branch32(Assembler::AboveOrEqual, indexReg, tmpReg, &failureRestoreIndex);
} else {
JS_ASSERT(index().reg().type() == MIRType_Int32);
indexReg = index().reg().typedReg().gpr();
masm.branch32(Assembler::AboveOrEqual, indexReg, tmpReg, &failures);
}
// Save indexReg because it needs to be clobbered to check deleted bit.
Label failurePopIndex;
masm.push(indexReg);
// Check if property was deleted on arguments object.
masm.loadPrivate(Address(object(), ArgumentsObject::getDataSlotOffset()), tmpReg);
masm.loadPtr(Address(tmpReg, offsetof(ArgumentsData, deletedBits)), tmpReg);
// In tempReg, calculate index of word containing bit: (idx >> logBitsPerWord)
masm.rshiftPtr(Imm32(JS_BITS_PER_WORD_LOG2), indexReg);
masm.loadPtr(BaseIndex(tmpReg, indexReg, ScaleFromElemWidth(sizeof(size_t))), tmpReg);
// Don't bother testing specific bit, if any bit is set in the word, fail.
masm.branchPtr(Assembler::NotEqual, tmpReg, ImmWord((size_t)0), &failurePopIndex);
// Get the address to load from into tmpReg
masm.loadPrivate(Address(object(), ArgumentsObject::getDataSlotOffset()), tmpReg);
masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), tmpReg);
// Restore original index register value, to use for indexing element.
masm.pop(indexReg);
BaseIndex elemIdx(tmpReg, indexReg, ScaleFromElemWidth(sizeof(Value)));
// Ensure result is not magic value, and type-check result.
masm.branchTestMagic(Assembler::Equal, elemIdx, &failureRestoreIndex);
if (output().hasTyped()) {
JS_ASSERT(!output().typedReg().isFloat());
JS_ASSERT(index().reg().type() == MIRType_Boolean ||
index().reg().type() == MIRType_Int32 ||
index().reg().type() == MIRType_String ||
index().reg().type() == MIRType_Object);
masm.branchTestMIRType(Assembler::NotEqual, elemIdx, index().reg().type(),
&failureRestoreIndex);
}
masm.loadTypedOrValue(elemIdx, output());
// indexReg may need to be reconstructed if it was originally a value.
if (index().reg().hasValue())
masm.tagValue(JSVAL_TYPE_INT32, indexReg, index().reg().valueReg());
// Success.
attacher.jumpRejoin(masm);
// Restore the object before continuing to the next stub.
masm.bind(&failurePopIndex);
masm.pop(indexReg);
masm.bind(&failureRestoreIndex);
if (index().reg().hasValue())
masm.tagValue(JSVAL_TYPE_INT32, indexReg, index().reg().valueReg());
masm.bind(&failures);
attacher.jumpNextStub(masm);
if (obj->isStrictArguments()) {
JS_ASSERT(!hasStrictArgumentsStub_);
hasStrictArgumentsStub_ = true;
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (strict)");
}
JS_ASSERT(!hasNormalArgumentsStub_);
hasNormalArgumentsStub_ = true;
return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (normal)");
}
bool
GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
HandleValue idval, MutableHandleValue res)
@ -2143,7 +2352,17 @@ GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
bool attachedStub = false;
if (cache.canAttachStub()) {
if (obj->isNative() && cache.monitoredResult()) {
if (IsOptimizableArgumentsObjectForGetElem(obj, idval) &&
!cache.hasArgumentsStub(obj->isStrictArguments()) &&
!cache.index().constant() &&
(cache.index().reg().hasValue() ||
cache.index().reg().type() == MIRType_Int32) &&
(cache.output().hasValue() || !cache.output().typedReg().isFloat()))
{
if (!cache.attachArgumentsElement(cx, ion, obj))
return false;
attachedStub = true;
} else if (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());

View File

@ -497,6 +497,8 @@ class GetPropertyIC : public RepatchIonCache
bool allowGetters_ : 1;
bool hasArrayLengthStub_ : 1;
bool hasTypedArrayLengthStub_ : 1;
bool hasStrictArgumentsLengthStub_ : 1;
bool hasNormalArgumentsLengthStub_ : 1;
public:
GetPropertyIC(RegisterSet liveRegs,
@ -535,6 +537,9 @@ class GetPropertyIC : public RepatchIonCache
bool hasTypedArrayLengthStub() const {
return hasTypedArrayLengthStub_;
}
bool hasArgumentsLengthStub(bool strict) const {
return strict ? hasStrictArgumentsLengthStub_ : hasNormalArgumentsLengthStub_;
}
bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape);
@ -543,6 +548,7 @@ class GetPropertyIC : public RepatchIonCache
const SafepointIndex *safepointIndex, void *returnAddr);
bool attachArrayLength(JSContext *cx, IonScript *ion, JSObject *obj);
bool attachTypedArrayLength(JSContext *cx, IonScript *ion, JSObject *obj);
bool attachArgumentsLength(JSContext *cx, IonScript *ion, JSObject *obj);
static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp);
};
@ -609,6 +615,8 @@ class GetElementIC : public RepatchIonCache
bool monitoredResult_ : 1;
bool hasDenseStub_ : 1;
bool hasStrictArgumentsStub_ : 1;
bool hasNormalArgumentsStub_ : 1;
size_t failedUpdates_;
@ -645,6 +653,9 @@ class GetElementIC : public RepatchIonCache
bool hasDenseStub() const {
return hasDenseStub_;
}
bool hasArgumentsStub(bool strict) const {
return strict ? hasStrictArgumentsStub_ : hasNormalArgumentsStub_;
}
void setHasDenseStub() {
JS_ASSERT(!hasDenseStub());
hasDenseStub_ = true;
@ -653,6 +664,7 @@ class GetElementIC : public RepatchIonCache
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, JSObject *obj, const Value &idval);
bool attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj);
static bool
update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,

View File

@ -166,6 +166,31 @@ class MacroAssembler : public MacroAssemblerSpecific
branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
}
template <typename Value>
Condition testMIRType(Condition cond, const Value &val, MIRType type) {
JS_ASSERT(type == MIRType_Null || type == MIRType_Undefined ||
type == MIRType_Boolean || type == MIRType_Int32 ||
type == MIRType_String || type == MIRType_Object ||
type == MIRType_Double);
switch (type) {
case MIRType_Null: return testNull(cond, val);
case MIRType_Undefined: return testUndefined(cond, val);
case MIRType_Boolean: return testBoolean(cond, val);
case MIRType_Int32: return testInt32(cond, val);
case MIRType_String: return testString(cond, val);
case MIRType_Object: return testObject(cond, val);
case MIRType_Double: return testDouble(cond, val);
default:
JS_NOT_REACHED("Bad MIRType");
}
}
template <typename Value>
void branchTestMIRType(Condition cond, const Value &val, MIRType type, Label *label) {
cond = testMIRType(cond, val, type);
j(cond, label);
}
// Branches to |label| if |reg| is false. |reg| should be a C++ bool.
void branchIfFalseBool(const Register &reg, Label *label) {
// Note that C++ bool is only 1 byte, so ignore the higher-order bits.

View File

@ -2299,15 +2299,6 @@ MacroAssemblerARMCompat::testGCThing(Assembler::Condition cond, const Address &a
return cond == Equal ? AboveOrEqual : Below;
}
Assembler::Condition
MacroAssemblerARMCompat::testGCThing(Assembler::Condition cond, const BaseIndex &address)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
return cond == Equal ? AboveOrEqual : Below;
}
Assembler::Condition
MacroAssemblerARMCompat::testMagic(Assembler::Condition cond, const Address &address)
{
@ -2317,15 +2308,6 @@ MacroAssemblerARMCompat::testMagic(Assembler::Condition cond, const Address &add
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testMagic(Assembler::Condition cond, const BaseIndex &address)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_MAGIC));
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testInt32(Assembler::Condition cond, const Address &address)
{
@ -2360,6 +2342,88 @@ MacroAssemblerARMCompat::testNumber(Condition cond, const Register &tag)
return cond == Equal ? BelowOrEqual : Above;
}
Assembler::Condition
MacroAssemblerARMCompat::testUndefined(Condition cond, const BaseIndex &src)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_UNDEFINED));
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testNull(Condition cond, const BaseIndex &src)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_NULL));
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testBoolean(Condition cond, const BaseIndex &src)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_BOOLEAN));
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testString(Condition cond, const BaseIndex &src)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_STRING));
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testInt32(Condition cond, const BaseIndex &src)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_INT32));
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testObject(Condition cond, const BaseIndex &src)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_OBJECT));
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testDouble(Condition cond, const BaseIndex &src)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual;
extractTag(address, ScratchRegister);
ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR));
return actual;
}
Assembler::Condition
MacroAssemblerARMCompat::testMagic(Assembler::Condition cond, const BaseIndex &address)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_MAGIC));
return cond;
}
Assembler::Condition
MacroAssemblerARMCompat::testGCThing(Assembler::Condition cond, const BaseIndex &address)
{
JS_ASSERT(cond == Equal || cond == NotEqual);
extractTag(address, ScratchRegister);
ma_cmp(ScratchRegister, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
return cond == Equal ? AboveOrEqual : Below;
}
void
MacroAssemblerARMCompat::branchTestValue(Condition cond, const ValueOperand &value, const Value &v,
Label *label)

View File

@ -640,12 +640,20 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
Condition testPrimitive(Condition cond, const Register &tag);
Condition testGCThing(Condition cond, const Address &address);
Condition testGCThing(Condition cond, const BaseIndex &address);
Condition testMagic(Condition cond, const Address &address);
Condition testMagic(Condition cond, const BaseIndex &address);
Condition testInt32(Condition cond, const Address &address);
Condition testDouble(Condition cond, const Address &address);
Condition testUndefined(Condition cond, const BaseIndex &src);
Condition testNull(Condition cond, const BaseIndex &src);
Condition testBoolean(Condition cond, const BaseIndex &src);
Condition testString(Condition cond, const BaseIndex &src);
Condition testInt32(Condition cond, const BaseIndex &src);
Condition testObject(Condition cond, const BaseIndex &src);
Condition testDouble(Condition cond, const BaseIndex &src);
Condition testMagic(Condition cond, const BaseIndex &src);
Condition testGCThing(Condition cond, const BaseIndex &src);
template <typename T>
void branchTestGCThing(Condition cond, const T &t, Label *label) {
Condition c = testGCThing(cond, t);

View File

@ -318,23 +318,53 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
splitTag(src, ScratchReg);
return testGCThing(cond, ScratchReg);
}
Condition testGCThing(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testGCThing(cond, ScratchReg);
}
Condition testMagic(Condition cond, const Address &src) {
splitTag(src, ScratchReg);
return testMagic(cond, ScratchReg);
}
Condition testMagic(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testMagic(cond, ScratchReg);
}
Condition testPrimitive(Condition cond, const ValueOperand &src) {
splitTag(src, ScratchReg);
return testPrimitive(cond, ScratchReg);
}
Condition testUndefined(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testUndefined(cond, ScratchReg);
}
Condition testNull(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testNull(cond, ScratchReg);
}
Condition testBoolean(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testBoolean(cond, ScratchReg);
}
Condition testString(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testString(cond, ScratchReg);
}
Condition testInt32(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testInt32(cond, ScratchReg);
}
Condition testObject(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testObject(cond, ScratchReg);
}
Condition testDouble(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testDouble(cond, ScratchReg);
}
Condition testMagic(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testMagic(cond, ScratchReg);
}
Condition testGCThing(Condition cond, const BaseIndex &src) {
splitTag(src, ScratchReg);
return testGCThing(cond, ScratchReg);
}
Condition isMagic(Condition cond, const ValueOperand &src, JSWhyMagic why) {
uint64_t magic = MagicValue(why).asRawBits();
cmpPtr(src.valueReg(), ImmWord(magic));

View File

@ -281,21 +281,11 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
cmpl(tagOf(address), ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
return cond == Equal ? AboveOrEqual : Below;
}
Condition testGCThing(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
return cond == Equal ? AboveOrEqual : Below;
}
Condition testMagic(Condition cond, const Address &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_MAGIC));
return cond;
}
Condition testMagic(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_MAGIC));
return cond;
}
Condition testMagic(Condition cond, const Register &tag) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tag, ImmTag(JSVAL_TAG_MAGIC));
@ -370,6 +360,56 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
return testPrimitive(cond, value.typeReg());
}
Condition testUndefined(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_UNDEFINED));
return cond;
}
Condition testNull(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_NULL));
return cond;
}
Condition testBoolean(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_BOOLEAN));
return cond;
}
Condition testString(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_STRING));
return cond;
}
Condition testInt32(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_INT32));
return cond;
}
Condition testObject(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_OBJECT));
return cond;
}
Condition testDouble(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
Condition actual = (cond == Equal) ? Below : AboveOrEqual;
cmpl(tagOf(address), ImmTag(JSVAL_TAG_CLEAR));
return actual;
}
Condition testMagic(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_TAG_MAGIC));
return cond;
}
Condition testGCThing(Condition cond, const BaseIndex &address) {
JS_ASSERT(cond == Equal || cond == NotEqual);
cmpl(tagOf(address), ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
return cond == Equal ? AboveOrEqual : Below;
}
void branchTestValue(Condition cond, const ValueOperand &value, const Value &v, Label *label);
void branchTestValue(Condition cond, const Address &valaddr, const ValueOperand &value,
Label *label)