Bug 1332946 - CacheIR: IC for function.length. r=jandem

This commit is contained in:
Tom Schuster 2017-01-24 19:47:48 +01:00
Родитель 397131ee0d
Коммит 4d206d61cf
6 изменённых файлов: 152 добавлений и 3 удалений

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

@ -0,0 +1,48 @@
function interpreted() {
for (var i = 0; i < 50; i++) {
var f = function () {};
assertEq(f.length, 0);
}
for (var i = 0; i < 50; i++) {
var f = function (a, b) {};
assertEq(f.length, 2);
}
}
function bound() {
for (var i = 0; i < 50; i++) {
var f = (function () {}).bind({});
assertEq(f.length, 0);
}
for (var i = 0; i < 50; i++) {
var f = (function (a, b) {}).bind({});
assertEq(f.length, 2);
}
}
function native() {
for (var i = 0; i < 50; i++) {
// Use the interpreted function for getting the IC generated in the first place.
var f = function (a) {};
if (i == 15) {
f = Math.sin;
} else if (i == 20) {
f = Math.cos;
} else if (i == 25) {
f = Math.ceil;
} else if (i == 30) {
f = Math.tan;
} else if (i == 35) {
f = Math.tanh;
}
assertEq(f.length, 1);
}
}
interpreted();
bound();
native();

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

@ -149,6 +149,8 @@ GetPropIRGenerator::tryAttachStub()
return true;
if (tryAttachWindowProxy(obj, objId, id))
return true;
if (tryAttachFunction(obj, objId, id))
return true;
if (tryAttachProxy(obj, objId, id))
return true;
return false;
@ -911,6 +913,42 @@ GetPropIRGenerator::tryAttachObjectLength(HandleObject obj, ObjOperandId objId,
return false;
}
bool
GetPropIRGenerator::tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id)
{
// Function properties are lazily resolved so they might not be defined yet.
// And we might end up in a situation where we always have a fresh function
// object during the IC generation.
if (!obj->is<JSFunction>())
return false;
JSObject* holder = nullptr;
PropertyResult prop;
// This property exists already, don't attach the stub.
if (LookupPropertyPure(cx_, obj, id, &holder, &prop))
return false;
JSFunction* fun = &obj->as<JSFunction>();
if (JSID_IS_ATOM(id, cx_->names().length)) {
// length was probably deleted from the function.
if (fun->hasResolvedLength())
return false;
// Lazy functions don't store the length.
if (fun->isInterpretedLazy())
return false;
maybeEmitIdGuard(id);
writer.guardClass(objId, GuardClassKind::JSFunction);
writer.loadFunctionLengthResult(objId);
writer.returnFromIC();
return true;
}
return false;
}
bool
GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id)
{

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

@ -189,6 +189,7 @@ enum class CacheKind : uint8_t
_(LoadUnboxedArrayLengthResult) \
_(LoadArgumentsObjectArgResult) \
_(LoadArgumentsObjectLengthResult) \
_(LoadFunctionLengthResult) \
_(LoadStringCharResult) \
_(LoadStringLengthResult) \
_(LoadFrameCalleeResult) \
@ -280,6 +281,7 @@ enum class GuardClassKind : uint8_t
MappedArguments,
UnmappedArguments,
WindowProxy,
JSFunction,
};
// Class to record CacheIR + some additional metadata for code generation.
@ -628,6 +630,12 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
void loadUnboxedArrayLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj);
}
void loadArgumentsObjectLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj);
}
void loadFunctionLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadFunctionLengthResult, obj);
}
void loadArgumentsObjectArgResult(ObjOperandId obj, Int32OperandId index) {
writeOpWithOperandId(CacheOp::LoadArgumentsObjectArgResult, obj);
writeOperandId(index);
@ -652,9 +660,6 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
buffer_.writeByte(uint32_t(layout));
buffer_.writeByte(uint32_t(elementType));
}
void loadArgumentsObjectLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj);
}
void loadStringLengthResult(StringOperandId str) {
writeOpWithOperandId(CacheOp::LoadStringLengthResult, str);
}
@ -804,6 +809,7 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator
bool tryAttachObjectLength(HandleObject obj, ObjOperandId objId, HandleId id);
bool tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id);
bool tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id);
bool tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id);
bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id);
bool tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id);

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

@ -1228,6 +1228,9 @@ CacheIRCompiler::emitGuardClass()
case GuardClassKind::WindowProxy:
clasp = cx_->maybeWindowProxyClass();
break;
case GuardClassKind::JSFunction:
clasp = &JSFunction::class_;
break;
}
MOZ_ASSERT(clasp);
@ -1514,6 +1517,55 @@ CacheIRCompiler::emitLoadArgumentsObjectLengthResult()
return true;
}
bool
CacheIRCompiler::emitLoadFunctionLengthResult()
{
AutoOutputRegister output(*this);
Register obj = allocator.useRegister(masm, reader.objOperandId());
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
// Get the JSFunction flags.
masm.load16ZeroExtend(Address(obj, JSFunction::offsetOfFlags()), scratch);
// Functions with lazy scripts don't store their length.
// If the length was resolved before the length property might be shadowed.
masm.branchTest32(Assembler::NonZero,
scratch,
Imm32(JSFunction::INTERPRETED_LAZY |
JSFunction::RESOLVED_LENGTH),
failure->label());
Label boundFunction;
masm.branchTest32(Assembler::NonZero, scratch, Imm32(JSFunction::BOUND_FUN), &boundFunction);
Label interpreted;
masm.branchTest32(Assembler::NonZero, scratch, Imm32(JSFunction::INTERPRETED), &interpreted);
// Load the length of the native function.
masm.load16ZeroExtend(Address(obj, JSFunction::offsetOfNargs()), scratch);
Label done;
masm.jump(&done);
masm.bind(&boundFunction);
// Bound functions might have a non-int32 length.
Address boundLength(obj, FunctionExtended::offsetOfExtendedSlot(BOUND_FUN_LENGTH_SLOT));
masm.branchTestInt32(Assembler::NotEqual, boundLength, failure->label());
masm.unboxInt32(boundLength, scratch);
masm.jump(&done);
masm.bind(&interpreted);
// Load the length from the function's script.
masm.loadPtr(Address(obj, JSFunction::offsetOfNativeOrScript()), scratch);
masm.load16ZeroExtend(Address(scratch, JSScript::offsetOfFunLength()), scratch);
masm.bind(&done);
EmitStoreResult(masm, scratch, JSVAL_TYPE_INT32, output);
return true;
}
bool
CacheIRCompiler::emitLoadStringLengthResult()
{

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

@ -38,6 +38,7 @@ namespace jit {
_(LoadInt32ArrayLengthResult) \
_(LoadUnboxedArrayLengthResult) \
_(LoadArgumentsObjectLengthResult) \
_(LoadFunctionLengthResult) \
_(LoadStringLengthResult) \
_(LoadStringCharResult) \
_(LoadArgumentsObjectArgResult) \

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

@ -1171,6 +1171,10 @@ class JSScript : public js::gc::TenuredCell
return funLength_;
}
static size_t offsetOfFunLength() {
return offsetof(JSScript, funLength_);
}
size_t sourceStart() const {
return sourceStart_;
}