Bug 1376691 - Improve Ion-inlining of Array.isArray. r=nbp

This commit is contained in:
Jan de Mooij 2017-06-28 08:51:42 -07:00
Родитель 776c3f22b1
Коммит 8430ea8843
10 изменённых файлов: 216 добавлений и 13 удалений

Просмотреть файл

@ -13,3 +13,35 @@ function f() {
f();
f();
f();
function testObjects() {
var arr = [{}, [1], Array.prototype,
new Proxy(this, {}), new Proxy([], {})];
while (true) {
var arrays = 0;
for (var i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]))
arrays++;
}
assertEq(arrays, 3);
if (inIon())
break;
}
}
testObjects();
function testValues() {
var arr = [1, {}, [1], Array.prototype, null,
new Proxy(this, {}), new Proxy([], {})];
while (true) {
var arrays = 0;
for (var i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]))
arrays++;
}
assertEq(arrays, 3);
if (inIon())
break;
}
}
testValues();

Просмотреть файл

@ -11853,6 +11853,60 @@ CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool)
masm.jump(ool->rejoin());
}
typedef bool (*IsArrayFn)(JSContext*, HandleObject, bool*);
static const VMFunction IsArrayInfo = FunctionInfo<IsArrayFn>(JS::IsArray, "IsArray");
static void
EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool, Register obj, Register output,
Label* notArray = nullptr)
{
masm.loadObjClass(obj, output);
Label isArray;
masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_), &isArray);
masm.branchPtr(Assembler::Equal, output, ImmPtr(&UnboxedArrayObject::class_), &isArray);
// Branch to OOL path if it's a proxy.
masm.branchTestClassIsProxy(true, output, ool->entry());
if (notArray)
masm.bind(notArray);
masm.move32(Imm32(0), output);
masm.jump(ool->rejoin());
masm.bind(&isArray);
masm.move32(Imm32(1), output);
masm.bind(ool->rejoin());
}
void
CodeGenerator::visitIsArrayO(LIsArrayO* lir)
{
Register object = ToRegister(lir->object());
Register output = ToRegister(lir->output());
OutOfLineCode* ool = oolCallVM(IsArrayInfo, lir, ArgList(object),
StoreRegisterTo(output));
EmitObjectIsArray(masm, ool, object, output);
}
void
CodeGenerator::visitIsArrayV(LIsArrayV* lir)
{
ValueOperand val = ToValue(lir, LIsArrayV::Value);
Register output = ToRegister(lir->output());
Register temp = ToRegister(lir->temp());
Label notArray;
masm.branchTestObject(Assembler::NotEqual, val, &notArray);
masm.unboxObject(val, temp);
OutOfLineCode* ool = oolCallVM(IsArrayInfo, lir, ArgList(temp),
StoreRegisterTo(output));
EmitObjectIsArray(masm, ool, temp, output, &notArray);
}
void
CodeGenerator::visitIsObject(LIsObject* ins)
{

Просмотреть файл

@ -380,6 +380,8 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitOutOfLineIsCallable(OutOfLineIsCallable* ool);
void visitIsConstructor(LIsConstructor* lir);
void visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool);
void visitIsArrayO(LIsArrayO* lir);
void visitIsArrayV(LIsArrayV* lir);
void visitIsObject(LIsObject* lir);
void visitIsObjectAndBranch(LIsObjectAndBranch* lir);
void visitHasClass(LHasClass* lir);

Просмотреть файл

@ -4251,6 +4251,23 @@ LIRGenerator::visitCallInstanceOf(MCallInstanceOf* ins)
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitIsArray(MIsArray* ins)
{
MOZ_ASSERT(ins->type() == MIRType::Boolean);
if (ins->value()->type() == MIRType::Object) {
LIsArrayO* lir = new(alloc()) LIsArrayO(useRegister(ins->value()));
define(lir, ins);
assignSafepoint(lir, ins);
} else {
MOZ_ASSERT(ins->value()->type() == MIRType::Value);
LIsArrayV* lir = new(alloc()) LIsArrayV(useBox(ins->value()), temp());
define(lir, ins);
assignSafepoint(lir, ins);
}
}
void
LIRGenerator::visitIsCallable(MIsCallable* ins)
{

Просмотреть файл

@ -293,6 +293,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitCallInstanceOf(MCallInstanceOf* ins);
void visitIsCallable(MIsCallable* ins);
void visitIsConstructor(MIsConstructor* ins);
void visitIsArray(MIsArray* ins);
void visitIsObject(MIsObject* ins);
void visitHasClass(MHasClass* ins);
void visitWasmAddOffset(MWasmAddOffset* ins);

Просмотреть файл

@ -574,6 +574,12 @@ IonBuilder::inlineArray(CallInfo& callInfo)
return InliningStatus_Inlined;
}
static bool
IsArrayClass(const Class* clasp)
{
return clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_;
}
IonBuilder::InliningResult
IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
{
@ -587,22 +593,59 @@ IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
MDefinition* arg = callInfo.getArg(0);
bool isArray;
if (!arg->mightBeType(MIRType::Object)) {
isArray = false;
} else {
if (arg->type() != MIRType::Object)
return InliningStatus_NotInlined;
TemporaryTypeSet* types = arg->resultTypeSet();
const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
if (!clasp || clasp->isProxy())
return InliningStatus_NotInlined;
isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_);
pushConstant(BooleanValue(false));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
pushConstant(BooleanValue(isArray));
using ForAllResult = TemporaryTypeSet::ForAllResult;
TemporaryTypeSet* types = arg->resultTypeSet();
// Fast path for non-proxy objects.
if (arg->type() == MIRType::Object &&
types &&
types->forAllClasses(constraints(), IsProxyClass) == ForAllResult::ALL_FALSE)
{
// Definitely not a proxy. Now check for the array classes.
ForAllResult result = types->forAllClasses(constraints(), IsArrayClass);
if (result == ForAllResult::ALL_FALSE || result == ForAllResult::ALL_TRUE) {
// Definitely an array or definitely not an array, so we can
// constant fold.
pushConstant(BooleanValue(result == ForAllResult::ALL_TRUE));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
// We have some array classes and some non-array classes, so we have to
// check at runtime.
MOZ_ASSERT(result == ForAllResult::MIXED);
MHasClass* hasClass1 = MHasClass::New(alloc(), arg, &ArrayObject::class_);
current->add(hasClass1);
MHasClass* hasClass2 = MHasClass::New(alloc(), arg, &UnboxedArrayObject::class_);
current->add(hasClass2);
MBitOr* either = MBitOr::New(alloc(), hasClass1, hasClass2);
either->infer(inspector, pc);
current->add(either);
MDefinition* def = convertToBoolean(either);
current->push(def);
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
// The value might be a primitive or a proxy. MIsArray handles these cases.
MIsArray* isArray = MIsArray::New(alloc(), arg);
current->add(isArray);
current->push(isArray);
MOZ_TRY(resumeAfter(isArray));
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;

Просмотреть файл

@ -13420,6 +13420,23 @@ class MHasClass
}
};
// Note: we might call a proxy trap, so this instruction is effectful.
class MIsArray
: public MUnaryInstruction,
public BoxExceptPolicy<0, MIRType::Object>::Data
{
explicit MIsArray(MDefinition* value)
: MUnaryInstruction(value)
{
setResultType(MIRType::Boolean);
}
public:
INSTRUCTION_HEADER(IsArray)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, value))
};
class MCheckReturn
: public MBinaryInstruction,
public BoxInputsPolicy::Data

Просмотреть файл

@ -276,6 +276,7 @@ namespace jit {
_(SetDOMProperty) \
_(IsConstructor) \
_(IsCallable) \
_(IsArray) \
_(IsObject) \
_(HasClass) \
_(CopySign) \

Просмотреть файл

@ -8001,6 +8001,40 @@ class LIsConstructor : public LInstructionHelper<1, 1, 0>
}
};
class LIsArrayO : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(IsArrayO);
explicit LIsArrayO(const LAllocation& object) {
setOperand(0, object);
}
const LAllocation* object() {
return getOperand(0);
}
MIsArray* mir() const {
return mir_->toIsArray();
}
};
class LIsArrayV : public LInstructionHelper<1, BOX_PIECES, 1>
{
public:
LIR_HEADER(IsArrayV);
static const size_t Value = 0;
explicit LIsArrayV(const LBoxAllocation& value, const LDefinition& temp) {
setBoxOperand(0, value);
setTemp(0, temp);
}
const LDefinition* temp() {
return getTemp(0);
}
MIsArray* mir() const {
return mir_->toIsArray();
}
};
class LIsObject : public LInstructionHelper<1, BOX_PIECES, 0>
{
public:

Просмотреть файл

@ -391,6 +391,8 @@
_(CallDOMNative) \
_(IsCallable) \
_(IsConstructor) \
_(IsArrayO) \
_(IsArrayV) \
_(IsObject) \
_(IsObjectAndBranch) \
_(HasClass) \