зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1376691
- Improve Ion-inlining of Array.isArray. r=nbp
This commit is contained in:
Родитель
776c3f22b1
Коммит
8430ea8843
|
@ -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, ¬Array);
|
||||
masm.unboxObject(val, temp);
|
||||
|
||||
OutOfLineCode* ool = oolCallVM(IsArrayInfo, lir, ArgList(temp),
|
||||
StoreRegisterTo(output));
|
||||
EmitObjectIsArray(masm, ool, temp, output, ¬Array);
|
||||
}
|
||||
|
||||
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) \
|
||||
|
|
Загрузка…
Ссылка в новой задаче