Speed up bound functions a bit
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Apr 2016 02:00:38 +0000 (02:00 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Apr 2016 02:00:38 +0000 (02:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156889

Reviewed by Saam Barati.
Source/JavaScriptCore:

Bound functions are hard to optimize because JSC doesn't have a good notion of non-JS code
that does JS-ey things like make JS calls. What I mean by "non-JS code" is code that did not
originate from JS source. A bound function does a highly polymorphic call to the target
stored in the JSBoundFunction. Prior to this change, we represented it as native code that
used the generic native->JS call API. That's not cheap.

We could model bound functions using a builtin, but it's not clear that this would be easy
to grok, since so much of the code would have to access special parts of the JSBoundFunction
type. Doing it that way might solve the performance problems but it would mean extra work to
arrange for the builtin to have speedy access to the call target, the bound this, and the
bound arguments. Also, optimizing bound functions that way would mean that bound function
performance would be gated on the performance of a bunch of other things in our system. For
example, we'd want this polymorphic call to be handled like the funnel that it is: if we're
compiling the bound function's outgoing call with no context then we should compile it as
fully polymorphic but we can let it assume basic sanity like that the callee is a real
function; but if we're compiling the call with any amount of calling context then we want to
use normal call IC's.

Since the builtin path wouldn't lead to a simpler patch and since I think that the VM will
benefit in the long run from using custom handling for bound functions, I kept the native
code and just added Intrinsic/thunk support.

This just adds an Intrinsic for bound function calls where the JSBoundFunction targets a
JSFunction instance and has no bound arguments (only bound this). This intrinsic is
currently only implemented as a thunk and not yet recognized by the DFG bytecode parser.

I needed to loosen some restrictions to do this. For one, I was really tired of our bad use
of ENABLE(JIT) conditionals, which made it so that any serious client of Intrinsics would
have to have #ifdefs. Really what should happen is that if the JIT is not enabled then we
just ignore intrinsics. Also, the code was previously assuming that having a native
constructor and knowing the Intrinsic for your native call were mutually exclusive. This
change makes it possible to have a native executable that has a custom function, custom
constructor, and an Intrinsic.

This is a >4x speed-up on bound function calls with no bound arguments.

In the future, we should teach the DFG Intrinsic handling to deal with bound functions and
we should teach the inliner (and ByteCodeParser::handleCall() in general) how to deal with
the function call inside the bound function. That would be super awesome.

* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::timesPtr):
(JSC::AbstractMacroAssembler::Address::withOffset):
(JSC::AbstractMacroAssembler::BaseIndex::BaseIndex):
(JSC::MacroAssemblerType>::Address::indexedBy):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::storeCell):
(JSC::AssemblyHelpers::loadCell):
(JSC::AssemblyHelpers::storeValue):
(JSC::AssemblyHelpers::emitSaveCalleeSaves):
(JSC::AssemblyHelpers::emitSaveThenMaterializeTagRegisters):
(JSC::AssemblyHelpers::emitRestoreCalleeSaves):
(JSC::AssemblyHelpers::emitRestoreSavedTagRegisters):
(JSC::AssemblyHelpers::copyCalleeSavesToVMCalleeSavesBuffer):
* jit/JITThunks.cpp:
(JSC::JITThunks::ctiNativeTailCall):
(JSC::JITThunks::ctiNativeTailCallWithoutSavedTags):
(JSC::JITThunks::ctiStub):
(JSC::JITThunks::hostFunctionStub):
(JSC::JITThunks::clearHostFunctionStubs):
* jit/JITThunks.h:
* jit/SpecializedThunkJIT.h:
(JSC::SpecializedThunkJIT::callDoubleToDoublePreservingReturn):
(JSC::SpecializedThunkJIT::tagReturnAsInt32):
(JSC::SpecializedThunkJIT::emitSaveThenMaterializeTagRegisters): Deleted.
(JSC::SpecializedThunkJIT::emitRestoreSavedTagRegisters): Deleted.
* jit/ThunkGenerators.cpp:
(JSC::virtualThunkFor):
(JSC::nativeForGenerator):
(JSC::nativeCallGenerator):
(JSC::nativeTailCallGenerator):
(JSC::nativeTailCallWithoutSavedTagsGenerator):
(JSC::nativeConstructGenerator):
(JSC::randomThunkGenerator):
(JSC::boundThisNoArgsFunctionCallGenerator):
* jit/ThunkGenerators.h:
* runtime/Executable.cpp:
(JSC::NativeExecutable::create):
(JSC::NativeExecutable::destroy):
(JSC::NativeExecutable::createStructure):
(JSC::NativeExecutable::finishCreation):
(JSC::NativeExecutable::NativeExecutable):
(JSC::ScriptExecutable::ScriptExecutable):
* runtime/Executable.h:
* runtime/FunctionPrototype.cpp:
(JSC::functionProtoFuncBind):
* runtime/IntlCollatorPrototype.cpp:
(JSC::IntlCollatorPrototypeGetterCompare):
* runtime/Intrinsic.h:
* runtime/JSBoundFunction.cpp:
(JSC::boundThisNoArgsFunctionCall):
(JSC::boundFunctionCall):
(JSC::boundThisNoArgsFunctionConstruct):
(JSC::boundFunctionConstruct):
(JSC::getBoundFunctionStructure):
(JSC::JSBoundFunction::create):
(JSC::JSBoundFunction::customHasInstance):
(JSC::JSBoundFunction::JSBoundFunction):
* runtime/JSBoundFunction.h:
(JSC::JSBoundFunction::targetFunction):
(JSC::JSBoundFunction::boundThis):
(JSC::JSBoundFunction::boundArgs):
(JSC::JSBoundFunction::createStructure):
(JSC::JSBoundFunction::offsetOfTargetFunction):
(JSC::JSBoundFunction::offsetOfBoundThis):
* runtime/JSFunction.cpp:
(JSC::JSFunction::lookUpOrCreateNativeExecutable):
(JSC::JSFunction::create):
* runtime/VM.cpp:
(JSC::thunkGeneratorForIntrinsic):
(JSC::VM::getHostFunction):
* runtime/VM.h:
(JSC::VM::getCTIStub):
(JSC::VM::exceptionOffset):

LayoutTests:

This microbenchmark speeds up by >4x with this change.

* js/regress/bound-function-call-expected.txt: Added.
* js/regress/bound-function-call.html: Added.
* js/regress/script-tests/bound-function-call.js: Added.
(foo):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@199946 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/bound-function-call-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/bound-function-call.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/bound-function-call.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/assembler/AbstractMacroAssembler.h
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JITThunks.cpp
Source/JavaScriptCore/jit/JITThunks.h
Source/JavaScriptCore/jit/SpecializedThunkJIT.h
Source/JavaScriptCore/jit/ThunkGenerators.cpp
Source/JavaScriptCore/jit/ThunkGenerators.h
Source/JavaScriptCore/runtime/Executable.cpp
Source/JavaScriptCore/runtime/Executable.h
Source/JavaScriptCore/runtime/FunctionPrototype.cpp
Source/JavaScriptCore/runtime/IntlCollatorPrototype.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSBoundFunction.cpp
Source/JavaScriptCore/runtime/JSBoundFunction.h
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h

index 8e9523a..8ff35f5 100644 (file)
@@ -1,3 +1,17 @@
+2016-04-22  Filip Pizlo  <fpizlo@apple.com>
+
+        Speed up bound functions a bit
+        https://bugs.webkit.org/show_bug.cgi?id=156889
+
+        Reviewed by Saam Barati.
+
+        This microbenchmark speeds up by >4x with this change.
+
+        * js/regress/bound-function-call-expected.txt: Added.
+        * js/regress/bound-function-call.html: Added.
+        * js/regress/script-tests/bound-function-call.js: Added.
+        (foo):
+
 2016-04-22  Chris Dumez  <cdumez@apple.com>
 
         Cannot access the SQLTransaction.constructor.prototype
diff --git a/LayoutTests/js/regress/bound-function-call-expected.txt b/LayoutTests/js/regress/bound-function-call-expected.txt
new file mode 100644 (file)
index 0000000..85d96a9
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/bound-function-call
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/bound-function-call.html b/LayoutTests/js/regress/bound-function-call.html
new file mode 100644 (file)
index 0000000..4ea79f2
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/bound-function-call.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/bound-function-call.js b/LayoutTests/js/regress/script-tests/bound-function-call.js
new file mode 100644 (file)
index 0000000..eac569a
--- /dev/null
@@ -0,0 +1,16 @@
+function foo() {
+    return this.f;
+}
+
+var binding = foo.bind({f:42});
+
+(function() {
+    var n = 1000000;
+    var result = 0;
+    for (var i = 0; i < n; ++i) {
+        var myResult = binding();
+        result += myResult;
+    }
+    if (result != n * 42)
+        throw "Error: bad result: " + result;
+})();
index 9fa697d..2be050b 100644 (file)
@@ -1,3 +1,125 @@
+2016-04-22  Filip Pizlo  <fpizlo@apple.com>
+
+        Speed up bound functions a bit
+        https://bugs.webkit.org/show_bug.cgi?id=156889
+
+        Reviewed by Saam Barati.
+        
+        Bound functions are hard to optimize because JSC doesn't have a good notion of non-JS code
+        that does JS-ey things like make JS calls. What I mean by "non-JS code" is code that did not
+        originate from JS source. A bound function does a highly polymorphic call to the target
+        stored in the JSBoundFunction. Prior to this change, we represented it as native code that
+        used the generic native->JS call API. That's not cheap.
+        
+        We could model bound functions using a builtin, but it's not clear that this would be easy
+        to grok, since so much of the code would have to access special parts of the JSBoundFunction
+        type. Doing it that way might solve the performance problems but it would mean extra work to
+        arrange for the builtin to have speedy access to the call target, the bound this, and the
+        bound arguments. Also, optimizing bound functions that way would mean that bound function
+        performance would be gated on the performance of a bunch of other things in our system. For
+        example, we'd want this polymorphic call to be handled like the funnel that it is: if we're
+        compiling the bound function's outgoing call with no context then we should compile it as
+        fully polymorphic but we can let it assume basic sanity like that the callee is a real
+        function; but if we're compiling the call with any amount of calling context then we want to
+        use normal call IC's.
+        
+        Since the builtin path wouldn't lead to a simpler patch and since I think that the VM will
+        benefit in the long run from using custom handling for bound functions, I kept the native
+        code and just added Intrinsic/thunk support.
+        
+        This just adds an Intrinsic for bound function calls where the JSBoundFunction targets a
+        JSFunction instance and has no bound arguments (only bound this). This intrinsic is
+        currently only implemented as a thunk and not yet recognized by the DFG bytecode parser.
+
+        I needed to loosen some restrictions to do this. For one, I was really tired of our bad use
+        of ENABLE(JIT) conditionals, which made it so that any serious client of Intrinsics would
+        have to have #ifdefs. Really what should happen is that if the JIT is not enabled then we
+        just ignore intrinsics. Also, the code was previously assuming that having a native
+        constructor and knowing the Intrinsic for your native call were mutually exclusive. This
+        change makes it possible to have a native executable that has a custom function, custom
+        constructor, and an Intrinsic.
+        
+        This is a >4x speed-up on bound function calls with no bound arguments.
+
+        In the future, we should teach the DFG Intrinsic handling to deal with bound functions and
+        we should teach the inliner (and ByteCodeParser::handleCall() in general) how to deal with
+        the function call inside the bound function. That would be super awesome.
+
+        * assembler/AbstractMacroAssembler.h:
+        (JSC::AbstractMacroAssembler::timesPtr):
+        (JSC::AbstractMacroAssembler::Address::withOffset):
+        (JSC::AbstractMacroAssembler::BaseIndex::BaseIndex):
+        (JSC::MacroAssemblerType>::Address::indexedBy):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::storeCell):
+        (JSC::AssemblyHelpers::loadCell):
+        (JSC::AssemblyHelpers::storeValue):
+        (JSC::AssemblyHelpers::emitSaveCalleeSaves):
+        (JSC::AssemblyHelpers::emitSaveThenMaterializeTagRegisters):
+        (JSC::AssemblyHelpers::emitRestoreCalleeSaves):
+        (JSC::AssemblyHelpers::emitRestoreSavedTagRegisters):
+        (JSC::AssemblyHelpers::copyCalleeSavesToVMCalleeSavesBuffer):
+        * jit/JITThunks.cpp:
+        (JSC::JITThunks::ctiNativeTailCall):
+        (JSC::JITThunks::ctiNativeTailCallWithoutSavedTags):
+        (JSC::JITThunks::ctiStub):
+        (JSC::JITThunks::hostFunctionStub):
+        (JSC::JITThunks::clearHostFunctionStubs):
+        * jit/JITThunks.h:
+        * jit/SpecializedThunkJIT.h:
+        (JSC::SpecializedThunkJIT::callDoubleToDoublePreservingReturn):
+        (JSC::SpecializedThunkJIT::tagReturnAsInt32):
+        (JSC::SpecializedThunkJIT::emitSaveThenMaterializeTagRegisters): Deleted.
+        (JSC::SpecializedThunkJIT::emitRestoreSavedTagRegisters): Deleted.
+        * jit/ThunkGenerators.cpp:
+        (JSC::virtualThunkFor):
+        (JSC::nativeForGenerator):
+        (JSC::nativeCallGenerator):
+        (JSC::nativeTailCallGenerator):
+        (JSC::nativeTailCallWithoutSavedTagsGenerator):
+        (JSC::nativeConstructGenerator):
+        (JSC::randomThunkGenerator):
+        (JSC::boundThisNoArgsFunctionCallGenerator):
+        * jit/ThunkGenerators.h:
+        * runtime/Executable.cpp:
+        (JSC::NativeExecutable::create):
+        (JSC::NativeExecutable::destroy):
+        (JSC::NativeExecutable::createStructure):
+        (JSC::NativeExecutable::finishCreation):
+        (JSC::NativeExecutable::NativeExecutable):
+        (JSC::ScriptExecutable::ScriptExecutable):
+        * runtime/Executable.h:
+        * runtime/FunctionPrototype.cpp:
+        (JSC::functionProtoFuncBind):
+        * runtime/IntlCollatorPrototype.cpp:
+        (JSC::IntlCollatorPrototypeGetterCompare):
+        * runtime/Intrinsic.h:
+        * runtime/JSBoundFunction.cpp:
+        (JSC::boundThisNoArgsFunctionCall):
+        (JSC::boundFunctionCall):
+        (JSC::boundThisNoArgsFunctionConstruct):
+        (JSC::boundFunctionConstruct):
+        (JSC::getBoundFunctionStructure):
+        (JSC::JSBoundFunction::create):
+        (JSC::JSBoundFunction::customHasInstance):
+        (JSC::JSBoundFunction::JSBoundFunction):
+        * runtime/JSBoundFunction.h:
+        (JSC::JSBoundFunction::targetFunction):
+        (JSC::JSBoundFunction::boundThis):
+        (JSC::JSBoundFunction::boundArgs):
+        (JSC::JSBoundFunction::createStructure):
+        (JSC::JSBoundFunction::offsetOfTargetFunction):
+        (JSC::JSBoundFunction::offsetOfBoundThis):
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::lookUpOrCreateNativeExecutable):
+        (JSC::JSFunction::create):
+        * runtime/VM.cpp:
+        (JSC::thunkGeneratorForIntrinsic):
+        (JSC::VM::getHostFunction):
+        * runtime/VM.h:
+        (JSC::VM::getCTIStub):
+        (JSC::VM::exceptionOffset):
+
 2016-04-22  Joonghun Park  <jh718.park@samsung.com>
 
         [JSC] Fix build break since r199866
index 2126313..cc231fc 100644 (file)
@@ -143,7 +143,9 @@ public:
             return TimesFour;
         return TimesEight;
     }
-
+    
+    struct BaseIndex;
+    
     // Address:
     //
     // Describes a simple base-offset address.
@@ -159,6 +161,8 @@ public:
             return Address(base, offset + additionalOffset);
         }
         
+        BaseIndex indexedBy(RegisterID index, Scale) const;
+        
         RegisterID base;
         int32_t offset;
     };
@@ -216,7 +220,7 @@ public:
             , offset(offset)
         {
         }
-
+        
         RegisterID base;
         RegisterID index;
         Scale scale;
@@ -1144,6 +1148,15 @@ protected:
     friend class LinkBuffer;
 }; // class AbstractMacroAssembler
 
+template <class AssemblerType, class MacroAssemblerType>
+inline typename AbstractMacroAssembler<AssemblerType, MacroAssemblerType>::BaseIndex
+AbstractMacroAssembler<AssemblerType, MacroAssemblerType>::Address::indexedBy(
+    typename AbstractMacroAssembler<AssemblerType, MacroAssemblerType>::RegisterID index,
+    typename AbstractMacroAssembler<AssemblerType, MacroAssemblerType>::Scale scale) const
+{
+    return BaseIndex(base, index, scale, offset);
+}
+
 } // namespace JSC
 
 #endif // ENABLE(ASSEMBLER)
index 7732756..ea85c38 100644 (file)
@@ -85,6 +85,15 @@ public:
 #endif
     }
     
+    void loadCell(Address address, GPRReg gpr)
+    {
+#if USE(JSVALUE64)
+        load64(address, gpr);
+#else
+        load32(address.withOffset(PayloadOffset), gpr);
+#endif
+    }
+    
     void storeValue(JSValueRegs regs, Address address)
     {
 #if USE(JSVALUE64)
@@ -274,11 +283,36 @@ public:
         emitSaveCalleeSavesFor(codeBlock());
     }
 
+    void emitSaveThenMaterializeTagRegisters()
+    {
+#if USE(JSVALUE64)
+#if CPU(ARM64)
+        pushPair(GPRInfo::tagTypeNumberRegister, GPRInfo::tagMaskRegister);
+#else
+        push(GPRInfo::tagTypeNumberRegister);
+        push(GPRInfo::tagMaskRegister);
+#endif
+        emitMaterializeTagCheckRegisters();
+#endif
+    }
+    
     void emitRestoreCalleeSaves()
     {
         emitRestoreCalleeSavesFor(codeBlock());
     }
 
+    void emitRestoreSavedTagRegisters()
+    {
+#if USE(JSVALUE64)
+#if CPU(ARM64)
+        popPair(GPRInfo::tagTypeNumberRegister, GPRInfo::tagMaskRegister);
+#else
+        pop(GPRInfo::tagMaskRegister);
+        pop(GPRInfo::tagTypeNumberRegister);
+#endif
+#endif
+    }
+
     void copyCalleeSavesToVMCalleeSavesBuffer(const TempRegisterSet& usedRegisters = { RegisterSet::stubUnavailableRegisters() })
     {
 #if NUMBER_OF_CALLEE_SAVES_REGISTERS > 0
index 47eaf32..1e6c71a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -64,6 +64,12 @@ MacroAssemblerCodePtr JITThunks::ctiNativeTailCall(VM* vm)
     return ctiStub(vm, nativeTailCallGenerator).code();
 }
 
+MacroAssemblerCodePtr JITThunks::ctiNativeTailCallWithoutSavedTags(VM* vm)
+{
+    ASSERT(vm->canUseJIT());
+    return ctiStub(vm, nativeTailCallWithoutSavedTagsGenerator).code();
+}
+
 MacroAssemblerCodeRef JITThunks::ctiStub(VM* vm, ThunkGenerator generator)
 {
     LockHolder locker(m_lock);
@@ -84,27 +90,15 @@ void JITThunks::finalize(Handle<Unknown> handle, void*)
 
 NativeExecutable* JITThunks::hostFunctionStub(VM* vm, NativeFunction function, NativeFunction constructor, const String& name)
 {
-    ASSERT(!isCompilationThread());
-
-    if (NativeExecutable* nativeExecutable = m_hostFunctionStubMap->get(std::make_tuple(function, constructor, name)))
-        return nativeExecutable;
-
-    NativeExecutable* nativeExecutable = NativeExecutable::create(
-        *vm,
-        adoptRef(new NativeJITCode(JIT::compileCTINativeCall(vm, function), JITCode::HostCallThunk)),
-        function,
-        adoptRef(new NativeJITCode(MacroAssemblerCodeRef::createSelfManagedCodeRef(ctiNativeConstruct(vm)), JITCode::HostCallThunk)),
-        constructor, NoIntrinsic, name);
-    weakAdd(*m_hostFunctionStubMap, std::make_tuple(function, constructor, name), Weak<NativeExecutable>(nativeExecutable, this));
-    return nativeExecutable;
+    return hostFunctionStub(vm, function, constructor, nullptr, NoIntrinsic, name);
 }
 
-NativeExecutable* JITThunks::hostFunctionStub(VM* vm, NativeFunction function, ThunkGenerator generator, Intrinsic intrinsic, const String& name)
+NativeExecutable* JITThunks::hostFunctionStub(VM* vm, NativeFunction function, NativeFunction constructor, ThunkGenerator generator, Intrinsic intrinsic, const String& name)
 {
     ASSERT(!isCompilationThread());    
     ASSERT(vm->canUseJIT());
 
-    if (NativeExecutable* nativeExecutable = m_hostFunctionStubMap->get(std::make_tuple(function, &callHostFunctionAsConstructor, name)))
+    if (NativeExecutable* nativeExecutable = m_hostFunctionStubMap->get(std::make_tuple(function, constructor, name)))
         return nativeExecutable;
 
     RefPtr<JITCode> forCall;
@@ -116,11 +110,16 @@ NativeExecutable* JITThunks::hostFunctionStub(VM* vm, NativeFunction function, T
     
     RefPtr<JITCode> forConstruct = adoptRef(new NativeJITCode(MacroAssemblerCodeRef::createSelfManagedCodeRef(ctiNativeConstruct(vm)), JITCode::HostCallThunk));
     
-    NativeExecutable* nativeExecutable = NativeExecutable::create(*vm, forCall, function, forConstruct, callHostFunctionAsConstructor, intrinsic, name);
-    weakAdd(*m_hostFunctionStubMap, std::make_tuple(function, &callHostFunctionAsConstructor, name), Weak<NativeExecutable>(nativeExecutable, this));
+    NativeExecutable* nativeExecutable = NativeExecutable::create(*vm, forCall, function, forConstruct, constructor, intrinsic, name);
+    weakAdd(*m_hostFunctionStubMap, std::make_tuple(function, constructor, name), Weak<NativeExecutable>(nativeExecutable, this));
     return nativeExecutable;
 }
 
+NativeExecutable* JITThunks::hostFunctionStub(VM* vm, NativeFunction function, ThunkGenerator generator, Intrinsic intrinsic, const String& name)
+{
+    return hostFunctionStub(vm, function, callHostFunctionAsConstructor, generator, intrinsic, name);
+}
+
 void JITThunks::clearHostFunctionStubs()
 {
     m_hostFunctionStubMap = nullptr;
index e7ea2b2..d5f41bb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -55,10 +55,12 @@ public:
     MacroAssemblerCodePtr ctiNativeCall(VM*);
     MacroAssemblerCodePtr ctiNativeConstruct(VM*);
     MacroAssemblerCodePtr ctiNativeTailCall(VM*);    
+    MacroAssemblerCodePtr ctiNativeTailCallWithoutSavedTags(VM*);    
 
     MacroAssemblerCodeRef ctiStub(VM*, ThunkGenerator);
 
     NativeExecutable* hostFunctionStub(VM*, NativeFunction, NativeFunction constructor, const String& name);
+    NativeExecutable* hostFunctionStub(VM*, NativeFunction, NativeFunction constructor, ThunkGenerator, Intrinsic, const String& name);
     NativeExecutable* hostFunctionStub(VM*, NativeFunction, ThunkGenerator, Intrinsic, const String& name);
 
     void clearHostFunctionStubs();
index 6a2da6d..644accd 100644 (file)
@@ -193,31 +193,6 @@ namespace JSC {
         }
 
     private:
-        void emitSaveThenMaterializeTagRegisters()
-        {
-#if USE(JSVALUE64)
-#if CPU(ARM64)
-            pushPair(tagTypeNumberRegister, tagMaskRegister);
-#else
-            push(tagTypeNumberRegister);
-            push(tagMaskRegister);
-#endif
-            emitMaterializeTagCheckRegisters();
-#endif
-        }
-
-        void emitRestoreSavedTagRegisters()
-        {
-#if USE(JSVALUE64)
-#if CPU(ARM64)
-            popPair(tagTypeNumberRegister, tagMaskRegister);
-#else
-            pop(tagMaskRegister);
-            pop(tagTypeNumberRegister);
-#endif
-#endif
-        }
-        
         void tagReturnAsInt32()
         {
 #if USE(JSVALUE64)
index 17baf95..9bf9968 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2012, 2013, 2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,6 +31,7 @@
 #include "JITOperations.h"
 #include "JSArray.h"
 #include "JSArrayIterator.h"
+#include "JSBoundFunction.h"
 #include "JSStack.h"
 #include "MathCommon.h"
 #include "MaxFrameExtentForSlowPathCall.h"
@@ -227,7 +228,7 @@ MacroAssemblerCodeRef virtualThunkFor(VM* vm, CallLinkInfo& callLinkInfo)
         callLinkInfo.callMode() == CallMode::Regular ? "call" : callLinkInfo.callMode() == CallMode::Tail ? "tail call" : "construct"));
 }
 
-enum ThunkEntryType { EnterViaCall, EnterViaJump };
+enum ThunkEntryType { EnterViaCall, EnterViaJumpWithSavedTags, EnterViaJumpWithoutSavedTags };
 
 static MacroAssemblerCodeRef nativeForGenerator(VM* vm, CodeSpecializationKind kind, ThunkEntryType entryType = EnterViaCall)
 {
@@ -238,10 +239,12 @@ static MacroAssemblerCodeRef nativeForGenerator(VM* vm, CodeSpecializationKind k
     
     JSInterfaceJIT jit(vm);
 
-    if (entryType == EnterViaCall)
+    switch (entryType) {
+    case EnterViaCall:
         jit.emitFunctionPrologue();
+        break;
+    case EnterViaJumpWithSavedTags:
 #if USE(JSVALUE64)
-    else if (entryType == EnterViaJump) {
         // We're coming from a specialized thunk that has saved the prior tag registers' contents.
         // Restore them now.
 #if CPU(ARM64)
@@ -250,8 +253,12 @@ static MacroAssemblerCodeRef nativeForGenerator(VM* vm, CodeSpecializationKind k
         jit.pop(JSInterfaceJIT::tagMaskRegister);
         jit.pop(JSInterfaceJIT::tagTypeNumberRegister);
 #endif
-    }
 #endif
+        break;
+    case EnterViaJumpWithoutSavedTags:
+        jit.move(JSInterfaceJIT::framePointerRegister, JSInterfaceJIT::stackPointerRegister);
+        break;
+    }
 
     jit.emitPutToCallFrameHeader(0, JSStack::CodeBlock);
     jit.storePtr(JSInterfaceJIT::callFrameRegister, &vm->topCallFrame);
@@ -374,7 +381,7 @@ static MacroAssemblerCodeRef nativeForGenerator(VM* vm, CodeSpecializationKind k
     jit.jumpToExceptionHandler();
 
     LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
-    return FINALIZE_CODE(patchBuffer, ("native %s%s trampoline", entryType == EnterViaJump ? "Tail " : "", toCString(kind).data()));
+    return FINALIZE_CODE(patchBuffer, ("native %s%s trampoline", entryType == EnterViaJumpWithSavedTags ? "Tail With Saved Tags " : entryType == EnterViaJumpWithoutSavedTags ? "Tail Without Saved Tags " : "", toCString(kind).data()));
 }
 
 MacroAssemblerCodeRef nativeCallGenerator(VM* vm)
@@ -384,7 +391,12 @@ MacroAssemblerCodeRef nativeCallGenerator(VM* vm)
 
 MacroAssemblerCodeRef nativeTailCallGenerator(VM* vm)
 {
-    return nativeForGenerator(vm, CodeForCall, EnterViaJump);
+    return nativeForGenerator(vm, CodeForCall, EnterViaJumpWithSavedTags);
+}
+
+MacroAssemblerCodeRef nativeTailCallWithoutSavedTagsGenerator(VM* vm)
+{
+    return nativeForGenerator(vm, CodeForCall, EnterViaJumpWithoutSavedTags);
 }
 
 MacroAssemblerCodeRef nativeConstructGenerator(VM* vm)
@@ -1079,6 +1091,97 @@ MacroAssemblerCodeRef randomThunkGenerator(VM* vm)
 #endif
 }
 
+MacroAssemblerCodeRef boundThisNoArgsFunctionCallGenerator(VM* vm)
+{
+    CCallHelpers jit(vm);
+    
+    jit.emitFunctionPrologue();
+    
+    // Set up our call frame.
+    jit.storePtr(CCallHelpers::TrustedImmPtr(nullptr), CCallHelpers::addressFor(JSStack::CodeBlock));
+    jit.store32(CCallHelpers::TrustedImm32(0), CCallHelpers::tagFor(JSStack::ArgumentCount));
+
+    unsigned extraStackNeeded = 0;
+    if (unsigned stackMisalignment = sizeof(CallerFrameAndPC) % stackAlignmentBytes())
+        extraStackNeeded = stackAlignmentBytes() - stackMisalignment;
+    
+    // We need to forward all of the arguments that we were passed. We aren't allowed to do a tail
+    // call here as far as I can tell. At least not so long as the generic path doesn't do a tail
+    // call, since that would be way too weird.
+    
+    // The formula for the number of stack bytes needed given some number of parameters (including
+    // this) is:
+    //
+    //     stackAlign((numParams + CallFrameHeaderSize) * sizeof(Register) - sizeof(CallerFrameAndPC))
+    //
+    // Probably we want to write this as:
+    //
+    //     stackAlign((numParams + (CallFrameHeaderSize - CallerFrameAndPCSize)) * sizeof(Register))
+    //
+    // That's really all there is to this. We have all the registers we need to do it.
+    
+    jit.load32(CCallHelpers::payloadFor(JSStack::ArgumentCount), GPRInfo::regT1);
+    jit.add32(CCallHelpers::TrustedImm32(JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize), GPRInfo::regT1, GPRInfo::regT2);
+    jit.lshift32(CCallHelpers::TrustedImm32(3), GPRInfo::regT2);
+    jit.add32(CCallHelpers::TrustedImm32(stackAlignmentBytes() - 1), GPRInfo::regT2);
+    jit.and32(CCallHelpers::TrustedImm32(-stackAlignmentBytes()), GPRInfo::regT2);
+    
+    if (extraStackNeeded)
+        jit.add32(CCallHelpers::TrustedImm32(extraStackNeeded), GPRInfo::regT2);
+    
+    // At this point regT1 has the actual argument count and regT2 has the amount of stack we will
+    // need.
+    
+    jit.subPtr(GPRInfo::regT2, CCallHelpers::stackPointerRegister);
+
+    // Do basic callee frame setup, including 'this'.
+    
+    jit.loadCell(CCallHelpers::addressFor(JSStack::Callee), GPRInfo::regT3);
+
+    jit.store32(GPRInfo::regT1, CCallHelpers::calleeFramePayloadSlot(JSStack::ArgumentCount));
+    
+    JSValueRegs valueRegs = JSValueRegs::withTwoAvailableRegs(GPRInfo::regT0, GPRInfo::regT2);
+    jit.loadValue(CCallHelpers::Address(GPRInfo::regT3, JSBoundFunction::offsetOfBoundThis()), valueRegs);
+    jit.storeValue(valueRegs, CCallHelpers::calleeArgumentSlot(0));
+
+    jit.loadPtr(CCallHelpers::Address(GPRInfo::regT3, JSBoundFunction::offsetOfTargetFunction()), GPRInfo::regT3);
+    jit.storeCell(GPRInfo::regT3, CCallHelpers::calleeFrameSlot(JSStack::Callee));
+    
+    // OK, now we can start copying. This is a simple matter of copying parameters from the caller's
+    // frame to the callee's frame. Note that we know that regT1 (the argument count) must be at
+    // least 1.
+    jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1);
+    CCallHelpers::Jump done = jit.branchTest32(CCallHelpers::Zero, GPRInfo::regT1);
+    
+    CCallHelpers::Label loop = jit.label();
+    jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1);
+    jit.loadValue(CCallHelpers::addressFor(virtualRegisterForArgument(1)).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight), valueRegs);
+    jit.storeValue(valueRegs, CCallHelpers::calleeArgumentSlot(1).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight));
+    jit.branchTest32(CCallHelpers::NonZero, GPRInfo::regT1).linkTo(loop, &jit);
+    
+    done.link(&jit);
+    
+    jit.loadPtr(
+        CCallHelpers::Address(GPRInfo::regT3, JSFunction::offsetOfExecutable()),
+        GPRInfo::regT0);
+    jit.loadPtr(
+        CCallHelpers::Address(
+            GPRInfo::regT0, ExecutableBase::offsetOfJITCodeWithArityCheckFor(CodeForCall)),
+        GPRInfo::regT0);
+    CCallHelpers::Jump noCode = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT0);
+    
+    emitPointerValidation(jit, GPRInfo::regT0);
+    jit.call(GPRInfo::regT0);
+    
+    jit.emitFunctionEpilogue();
+    jit.ret();
+    
+    LinkBuffer linkBuffer(*vm, jit, GLOBAL_THUNK_ID);
+    linkBuffer.link(noCode, CodeLocationLabel(vm->jitStubs->ctiNativeTailCallWithoutSavedTags(vm)));
+    return FINALIZE_CODE(
+        linkBuffer, ("Specialized thunk for bound function calls with no arguments"));
 }
 
+} // namespace JSC
+
 #endif // ENABLE(JIT)
index 82d47fa..e768c51 100644 (file)
@@ -46,6 +46,7 @@ MacroAssemblerCodeRef virtualThunkFor(VM*, CallLinkInfo&);
 MacroAssemblerCodeRef nativeCallGenerator(VM*);
 MacroAssemblerCodeRef nativeConstructGenerator(VM*);
 MacroAssemblerCodeRef nativeTailCallGenerator(VM*);
+MacroAssemblerCodeRef nativeTailCallWithoutSavedTagsGenerator(VM*);
 MacroAssemblerCodeRef arityFixupGenerator(VM*);
 MacroAssemblerCodeRef unreachableGenerator(VM*);
 
@@ -65,6 +66,8 @@ MacroAssemblerCodeRef imulThunkGenerator(VM*);
 MacroAssemblerCodeRef randomThunkGenerator(VM*);
 MacroAssemblerCodeRef truncThunkGenerator(VM*);
 
+MacroAssemblerCodeRef boundThisNoArgsFunctionCallGenerator(VM* vm);
+
 }
 #endif // ENABLE(JIT)
 
index e97e366..22c8452 100644 (file)
@@ -103,11 +103,41 @@ void ExecutableBase::clearCode()
 
 const ClassInfo NativeExecutable::s_info = { "NativeExecutable", &ExecutableBase::s_info, 0, CREATE_METHOD_TABLE(NativeExecutable) };
 
+NativeExecutable* NativeExecutable::create(VM& vm, PassRefPtr<JITCode> callThunk, NativeFunction function, PassRefPtr<JITCode> constructThunk, NativeFunction constructor, Intrinsic intrinsic, const String& name)
+{
+    NativeExecutable* executable;
+    executable = new (NotNull, allocateCell<NativeExecutable>(vm.heap)) NativeExecutable(vm, function, constructor, intrinsic);
+    executable->finishCreation(vm, callThunk, constructThunk, name);
+    return executable;
+}
+
 void NativeExecutable::destroy(JSCell* cell)
 {
     static_cast<NativeExecutable*>(cell)->NativeExecutable::~NativeExecutable();
 }
 
+Structure* NativeExecutable::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
+{
+    return Structure::create(vm, globalObject, proto, TypeInfo(CellType, StructureFlags), info());
+}
+
+void NativeExecutable::finishCreation(VM& vm, PassRefPtr<JITCode> callThunk, PassRefPtr<JITCode> constructThunk, const String& name)
+{
+    Base::finishCreation(vm);
+    m_jitCodeForCall = callThunk;
+    m_jitCodeForConstruct = constructThunk;
+    m_jitCodeForCallWithArityCheck = m_jitCodeForCall->addressForCall(MustCheckArity);
+    m_jitCodeForConstructWithArityCheck = m_jitCodeForConstruct->addressForCall(MustCheckArity);
+    m_name = name;
+}
+
+NativeExecutable::NativeExecutable(VM& vm, NativeFunction function, NativeFunction constructor, Intrinsic intrinsic)
+    : ExecutableBase(vm, vm.nativeExecutableStructure.get(), NUM_PARAMETERS_IS_HOST, intrinsic)
+    , m_function(function)
+    , m_constructor(constructor)
+{
+}
+
 const ClassInfo ScriptExecutable::s_info = { "ScriptExecutable", &ExecutableBase::s_info, 0, CREATE_METHOD_TABLE(ScriptExecutable) };
 
 ScriptExecutable::ScriptExecutable(Structure* structure, VM& vm, const SourceCode& source, bool isInStrictContext, DerivedContextType derivedContextType, bool isInArrowFunctionContext, EvalContextType evalContextType, Intrinsic intrinsic)
index 8018e36..016f72a 100644 (file)
@@ -258,13 +258,7 @@ public:
     typedef ExecutableBase Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    static NativeExecutable* create(VM& vm, PassRefPtr<JITCode> callThunk, NativeFunction function, PassRefPtr<JITCode> constructThunk, NativeFunction constructor, Intrinsic intrinsic, const String& name)
-    {
-        NativeExecutable* executable;
-        executable = new (NotNull, allocateCell<NativeExecutable>(vm.heap)) NativeExecutable(vm, function, constructor, intrinsic);
-        executable->finishCreation(vm, callThunk, constructThunk, name);
-        return executable;
-    }
+    static NativeExecutable* create(VM& vm, PassRefPtr<JITCode> callThunk, NativeFunction function, PassRefPtr<JITCode> constructThunk, NativeFunction constructor, Intrinsic intrinsic, const String& name);
 
     static void destroy(JSCell*);
 
@@ -289,7 +283,7 @@ public:
         return OBJECT_OFFSETOF(NativeExecutable, m_constructor);
     }
 
-    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(vm, globalObject, proto, TypeInfo(CellType, StructureFlags), info()); }
+    static Structure* createStructure(VM&, JSGlobalObject*, JSValue proto);
         
     DECLARE_INFO;
 
@@ -298,23 +292,12 @@ public:
     const String& name() const { return m_name; }
 
 protected:
-    void finishCreation(VM& vm, PassRefPtr<JITCode> callThunk, PassRefPtr<JITCode> constructThunk, const String& name)
-    {
-        Base::finishCreation(vm);
-        m_jitCodeForCall = callThunk;
-        m_jitCodeForConstruct = constructThunk;
-        m_name = name;
-    }
+    void finishCreation(VM&, PassRefPtr<JITCode> callThunk, PassRefPtr<JITCode> constructThunk, const String& name);
 
 private:
     friend class ExecutableBase;
 
-    NativeExecutable(VM& vm, NativeFunction function, NativeFunction constructor, Intrinsic intrinsic)
-        : ExecutableBase(vm, vm.nativeExecutableStructure.get(), NUM_PARAMETERS_IS_HOST, intrinsic)
-        , m_function(function)
-        , m_constructor(constructor)
-    {
-    }
+    NativeExecutable(VM&, NativeFunction function, NativeFunction constructor, Intrinsic);
 
     NativeFunction m_function;
     NativeFunction m_constructor;
index bd087ed..e4416a2 100644 (file)
@@ -141,12 +141,16 @@ EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec)
 
     // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
     size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0;
-    JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), numBoundArgs);
-    if (!boundArgs)
-        return JSValue::encode(throwOutOfMemoryError(exec));
-
-    for (size_t i = 0; i < numBoundArgs; ++i)
-        boundArgs->initializeIndex(vm, i, exec->argument(i + 1));
+    JSArray* boundArgs;
+    if (numBoundArgs) {
+        boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), numBoundArgs);
+        if (!boundArgs)
+            return JSValue::encode(throwOutOfMemoryError(exec));
+        
+        for (size_t i = 0; i < numBoundArgs; ++i)
+            boundArgs->initializeIndex(vm, i, exec->argument(i + 1));
+    } else
+        boundArgs = nullptr;
 
     // If the [[Class]] internal property of Target is "Function", then ...
     // Else set the length own property of F to 0.
index 03d98de..2265f6c 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com)
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -124,12 +125,9 @@ EncodedJSValue JSC_HOST_CALL IntlCollatorPrototypeGetterCompare(ExecState* state
         // a. Let F be a new built-in function object as defined in 11.3.4.
         // b. The value of F’s length property is 2.
         JSFunction* targetObject = JSFunction::create(vm, globalObject, 2, ASCIILiteral("compare"), IntlCollatorFuncCompare, NoIntrinsic);
-        JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0);
-        if (!boundArgs)
-            return JSValue::encode(throwOutOfMemoryError(state));
 
         // c. Let bc be BoundFunctionCreate(F, «this value»).
-        boundCompare = JSBoundFunction::create(vm, state, globalObject, targetObject, collator, boundArgs, 2, ASCIILiteral("compare"));
+        boundCompare = JSBoundFunction::create(vm, state, globalObject, targetObject, collator, nullptr, 2, ASCIILiteral("compare"));
         if (vm.exception())
             return JSValue::encode(JSValue());
         // d. Set collator.[[boundCompare]] to bc.
index 9194df9..bd7dae2 100644 (file)
@@ -61,6 +61,7 @@ enum JS_EXPORT_PRIVATE Intrinsic {
     IsArrayConstructorIntrinsic,
     IsJSArrayIntrinsic,
     IsRegExpObjectIntrinsic,
+    BoundThisNoArgsFunctionCallIntrinsic,
 
     // Getter intrinsics.
     TypedArrayLengthIntrinsic,
index 420e661..69d80ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -34,16 +34,37 @@ namespace JSC {
 
 const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(JSBoundFunction) };
 
+EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionCall(ExecState* exec)
+{
+    JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee());
+
+    MarkedArgumentBuffer args;
+    for (unsigned i = 0; i < exec->argumentCount(); ++i)
+        args.append(exec->uncheckedArgument(i));
+
+    JSFunction* targetFunction = jsCast<JSFunction*>(boundFunction->targetFunction());
+    ExecutableBase* executable = targetFunction->executable();
+    if (executable->hasJITCodeForCall()) {
+        // Force the executable to cache its arity entrypoint.
+        executable->entrypointFor(CodeForCall, MustCheckArity);
+    }
+    CallData callData;
+    CallType callType = getCallData(targetFunction, callData);
+    ASSERT(callType != CallType::None);
+    return JSValue::encode(call(exec, targetFunction, callType, callData, boundFunction->boundThis(), args));
+}
+
 EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec)
 {
     JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee());
 
-    ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true!
-    JSArray* boundArgs = asArray(boundFunction->boundArgs());
+    JSArray* boundArgs = boundFunction->boundArgs();
 
     MarkedArgumentBuffer args;
-    for (unsigned i = 0; i < boundArgs->length(); ++i)
-        args.append(boundArgs->getIndexQuickly(i));
+    if (boundArgs) {
+        for (unsigned i = 0; i < boundArgs->length(); ++i)
+            args.append(boundArgs->getIndexQuickly(i));
+    }
     for (unsigned i = 0; i < exec->argumentCount(); ++i)
         args.append(exec->uncheckedArgument(i));
 
@@ -54,16 +75,32 @@ EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec)
     return JSValue::encode(call(exec, targetFunction, callType, callData, boundFunction->boundThis(), args));
 }
 
+EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionConstruct(ExecState* exec)
+{
+    JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee());
+
+    MarkedArgumentBuffer args;
+    for (unsigned i = 0; i < exec->argumentCount(); ++i)
+        args.append(exec->uncheckedArgument(i));
+
+    JSFunction* targetFunction = jsCast<JSFunction*>(boundFunction->targetFunction());
+    ConstructData constructData;
+    ConstructType constructType = getConstructData(targetFunction, constructData);
+    ASSERT(constructType != ConstructType::None);
+    return JSValue::encode(construct(exec, targetFunction, constructType, constructData, args));
+}
+
 EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState* exec)
 {
     JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee());
 
-    ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true!
-    JSArray* boundArgs = asArray(boundFunction->boundArgs());
+    JSArray* boundArgs = boundFunction->boundArgs();
 
     MarkedArgumentBuffer args;
-    for (unsigned i = 0; i < boundArgs->length(); ++i)
-        args.append(boundArgs->getIndexQuickly(i));
+    if (boundArgs) {
+        for (unsigned i = 0; i < boundArgs->length(); ++i)
+            args.append(boundArgs->getIndexQuickly(i));
+    }
     for (unsigned i = 0; i < exec->argumentCount(); ++i)
         args.append(exec->uncheckedArgument(i));
 
@@ -119,12 +156,19 @@ inline Structure* getBoundFunctionStructure(VM& vm, ExecState* exec, JSGlobalObj
     return result;
 }
 
-JSBoundFunction* JSBoundFunction::create(VM& vm, ExecState* exec, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int length, const String& name)
+JSBoundFunction* JSBoundFunction::create(VM& vm, ExecState* exec, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs, int length, const String& name)
 {
     ConstructData constructData;
     ConstructType constructType = JSC::getConstructData(targetFunction, constructData);
     bool canConstruct = constructType != ConstructType::None;
-    NativeExecutable* executable = vm.getHostFunction(boundFunctionCall, canConstruct ? boundFunctionConstruct : callHostFunctionAsConstructor, name);
+    
+    bool slowCase = boundArgs || !getJSFunction(targetFunction);
+    
+    NativeExecutable* executable = vm.getHostFunction(
+        slowCase ? boundFunctionCall : boundThisNoArgsFunctionCall,
+        slowCase ? NoIntrinsic : BoundThisNoArgsFunctionCallIntrinsic,
+        canConstruct ? (slowCase ? boundFunctionConstruct : boundThisNoArgsFunctionConstruct) : callHostFunctionAsConstructor,
+        name);
     Structure* structure = getBoundFunctionStructure(vm, exec, globalObject, targetFunction);
     if (UNLIKELY(vm.exception()))
         return nullptr;
@@ -139,11 +183,11 @@ bool JSBoundFunction::customHasInstance(JSObject* object, ExecState* exec, JSVal
     return jsCast<JSBoundFunction*>(object)->m_targetFunction->hasInstance(exec, value);
 }
 
-JSBoundFunction::JSBoundFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs)
+JSBoundFunction::JSBoundFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs)
     : Base(vm, globalObject, structure)
     , m_targetFunction(vm, this, targetFunction)
     , m_boundThis(vm, this, boundThis)
-    , m_boundArgs(vm, this, boundArgs)
+    , m_boundArgs(vm, this, boundArgs, WriteBarrier<JSArray>::MayBeNull)
 {
 }
 
index 2023926..59af3a5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,7 +30,9 @@
 
 namespace JSC {
 
+EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionCall(ExecState*);
 EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState*);
+EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionConstruct(ExecState*);
 EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState*);
 EncodedJSValue JSC_HOST_CALL isBoundFunction(ExecState*);
 EncodedJSValue JSC_HOST_CALL hasInstanceBoundFunction(ExecState*);
@@ -40,19 +42,22 @@ public:
     typedef JSFunction Base;
     const static unsigned StructureFlags = ~ImplementsDefaultHasInstance & Base::StructureFlags;
 
-    static JSBoundFunction* create(VM&, ExecState*, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int, const String& name);
+    static JSBoundFunction* create(VM&, ExecState*, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs, int, const String& name);
     
     static bool customHasInstance(JSObject*, ExecState*, JSValue);
 
     JSObject* targetFunction() { return m_targetFunction.get(); }
     JSValue boundThis() { return m_boundThis.get(); }
-    JSValue boundArgs() { return m_boundArgs.get(); }
+    JSArray* boundArgs() { return m_boundArgs.get(); }
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
         ASSERT(globalObject);
         return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info()); 
     }
+    
+    static ptrdiff_t offsetOfTargetFunction() { return OBJECT_OFFSETOF(JSBoundFunction, m_targetFunction); }
+    static ptrdiff_t offsetOfBoundThis() { return OBJECT_OFFSETOF(JSBoundFunction, m_boundThis); }
 
     DECLARE_INFO;
 
@@ -60,13 +65,13 @@ protected:
     static void visitChildren(JSCell*, SlotVisitor&);
 
 private:
-    JSBoundFunction(VM&, JSGlobalObject*, Structure*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs);
+    JSBoundFunction(VM&, JSGlobalObject*, Structure*, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs);
     
     void finishCreation(VM&, NativeExecutable*, int length, const String& name);
 
     WriteBarrier<JSObject> m_targetFunction;
     WriteBarrier<Unknown> m_boundThis;
-    WriteBarrier<Unknown> m_boundArgs;
+    WriteBarrier<JSArray> m_boundArgs;
 };
 
 } // namespace JSC
index defb5b6..2c7c2e8 100644 (file)
@@ -84,15 +84,7 @@ JSFunction* JSFunction::create(VM& vm, WebAssemblyExecutable* executable, JSScop
 
 NativeExecutable* JSFunction::lookUpOrCreateNativeExecutable(VM& vm, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor, const String& name)
 {
-#if !ENABLE(JIT)
-    UNUSED_PARAM(intrinsic);
-#else
-    if (intrinsic != NoIntrinsic && vm.canUseJIT()) {
-        ASSERT(nativeConstructor == callHostFunctionAsConstructor);
-        return vm.getHostFunction(nativeFunction, intrinsic, name);
-    }
-#endif
-    return vm.getHostFunction(nativeFunction, nativeConstructor, name);
+    return vm.getHostFunction(nativeFunction, intrinsic, nativeConstructor, name);
 }
 
 JSFunction* JSFunction::create(VM& vm, JSGlobalObject* globalObject, int length, const String& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor)
index 0a2279c..14b3598 100644 (file)
@@ -495,33 +495,34 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic)
         return imulThunkGenerator;
     case RandomIntrinsic:
         return randomThunkGenerator;
+    case BoundThisNoArgsFunctionCallIntrinsic:
+        return boundThisNoArgsFunctionCallGenerator;
     default:
-        return 0;
+        return nullptr;
     }
 }
 
+#endif // !ENABLE(JIT)
+
 NativeExecutable* VM::getHostFunction(NativeFunction function, NativeFunction constructor, const String& name)
 {
-    return jitStubs->hostFunctionStub(this, function, constructor, name);
+    return getHostFunction(function, NoIntrinsic, constructor, name);
 }
-NativeExecutable* VM::getHostFunction(NativeFunction function, Intrinsic intrinsic, const String& name)
-{
-    ASSERT(canUseJIT());
-    return jitStubs->hostFunctionStub(this, function, intrinsic != NoIntrinsic ? thunkGeneratorForIntrinsic(intrinsic) : 0, intrinsic, name);
-}
-
-#else // !ENABLE(JIT)
 
-NativeExecutable* VM::getHostFunction(NativeFunction function, NativeFunction constructor, const String& name)
+NativeExecutable* VM::getHostFunction(NativeFunction function, Intrinsic intrinsic, NativeFunction constructor, const String& name)
 {
+    if (canUseJIT()) {
+        return jitStubs->hostFunctionStub(
+            this, function, constructor,
+            intrinsic != NoIntrinsic ? thunkGeneratorForIntrinsic(intrinsic) : 0,
+            intrinsic, name);
+    }
     return NativeExecutable::create(*this,
         adoptRef(new NativeJITCode(MacroAssemblerCodeRef::createLLIntCodeRef(llint_native_call_trampoline), JITCode::HostCallThunk)), function,
         adoptRef(new NativeJITCode(MacroAssemblerCodeRef::createLLIntCodeRef(llint_native_construct_trampoline), JITCode::HostCallThunk)), constructor,
         NoIntrinsic, name);
 }
 
-#endif // !ENABLE(JIT)
-
 VM::ClientData::~ClientData()
 {
 }
index 64f8bb2..bd0a8de 100644 (file)
@@ -399,7 +399,6 @@ public:
     {
         return jitStubs->ctiStub(this, generator);
     }
-    NativeExecutable* getHostFunction(NativeFunction, Intrinsic, const String& name);
     
     std::unique_ptr<RegisterAtOffsetList> allCalleeSaveRegisterOffsets;
     
@@ -411,6 +410,7 @@ public:
     std::unique_ptr<FTL::Thunks> ftlThunks;
 #endif
     NativeExecutable* getHostFunction(NativeFunction, NativeFunction constructor, const String& name);
+    NativeExecutable* getHostFunction(NativeFunction, Intrinsic intrinsic, NativeFunction constructor, const String& name);
 
     static ptrdiff_t exceptionOffset()
     {