From 4d206d61cf53d43bf2bb55bcf5c0f613fc7466cd Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Tue, 24 Jan 2017 19:47:48 +0100 Subject: [PATCH] Bug 1332946 - CacheIR: IC for function.length. r=jandem --- .../jit-test/tests/cacheir/function-length.js | 48 +++++++++++++++++ js/src/jit/CacheIR.cpp | 38 ++++++++++++++ js/src/jit/CacheIR.h | 12 +++-- js/src/jit/CacheIRCompiler.cpp | 52 +++++++++++++++++++ js/src/jit/CacheIRCompiler.h | 1 + js/src/jsscript.h | 4 ++ 6 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 js/src/jit-test/tests/cacheir/function-length.js diff --git a/js/src/jit-test/tests/cacheir/function-length.js b/js/src/jit-test/tests/cacheir/function-length.js new file mode 100644 index 000000000000..14e852683f37 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/function-length.js @@ -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(); \ No newline at end of file diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index dab92876866d..0da1456d5341 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -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()) + 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(); + + 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) { diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 70ca137646f6..0cb2ff348a2a 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -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); diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index d2a3bced9b40..969a6b5f639e 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -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() { diff --git a/js/src/jit/CacheIRCompiler.h b/js/src/jit/CacheIRCompiler.h index 30f41fb3ab43..19f7bf3107dc 100644 --- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -38,6 +38,7 @@ namespace jit { _(LoadInt32ArrayLengthResult) \ _(LoadUnboxedArrayLengthResult) \ _(LoadArgumentsObjectLengthResult) \ + _(LoadFunctionLengthResult) \ _(LoadStringLengthResult) \ _(LoadStringCharResult) \ _(LoadArgumentsObjectArgResult) \ diff --git a/js/src/jsscript.h b/js/src/jsscript.h index e3877347b172..d2f7bbc24e68 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -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_; }