Use JITCompilationCanFail in more places, and make the fail path of JITCompilationMus...
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Mar 2015 01:26:56 +0000 (01:26 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Mar 2015 01:26:56 +0000 (01:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142993

Source/JavaScriptCore:

Reviewed by Geoffrey Garen and Mark Lam.

This changes the most commonly invoked paths that relied on JITCompilationMustSucceed
into using JITCompilationCanFail and having a legit fallback path. This mostly involves
having the FTL JIT do the same trick as the DFG JIT in case of any memory allocation
failure, but also involves adding the same kind of thing to the stub generators in
Repatch.

Because of that change, there are relatively few uses of JITCompilationMustSucceed. Most
of those uses cannot handle a GC, and so cannot do releaseExecutableMemory(). Only a few,
like host call stub generation, could handle a GC, but those get invoked very rarely. So,
this patch changes the releaseExecutableMemory() call into a crash with some diagnostic
printout.

Also add a way of inducing executable allocation failure, so that we can test this.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::compile):
(JSC::DFG::JITCompiler::compileFunction):
(JSC::DFG::JITCompiler::link): Deleted.
(JSC::DFG::JITCompiler::linkFunction): Deleted.
* dfg/DFGJITCompiler.h:
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::compileInThreadImpl):
* ftl/FTLCompile.cpp:
(JSC::FTL::mmAllocateCodeSection):
(JSC::FTL::mmAllocateDataSection):
* ftl/FTLLink.cpp:
(JSC::FTL::link):
* ftl/FTLState.h:
* jit/ArityCheckFailReturnThunks.cpp:
(JSC::ArityCheckFailReturnThunks::returnPCsFor):
* jit/ExecutableAllocationFuzz.cpp: Added.
(JSC::numberOfExecutableAllocationFuzzChecks):
(JSC::doExecutableAllocationFuzzing):
* jit/ExecutableAllocationFuzz.h: Added.
(JSC::doExecutableAllocationFuzzingIfEnabled):
* jit/ExecutableAllocatorFixedVMPool.cpp:
(JSC::ExecutableAllocator::allocate):
* jit/JIT.cpp:
(JSC::JIT::privateCompile):
* jit/JITCompilationEffort.h:
* jit/Repatch.cpp:
(JSC::generateByIdStub):
(JSC::tryCacheGetByID):
(JSC::tryBuildGetByIDList):
(JSC::emitPutReplaceStub):
(JSC::emitPutTransitionStubAndGetOldStructure):
(JSC::tryCachePutByID):
(JSC::tryBuildPutByIdList):
(JSC::tryRepatchIn):
(JSC::linkPolymorphicCall):
* jsc.cpp:
(jscmain):
* runtime/Options.h:
* runtime/TestRunnerUtils.h:
* runtime/VM.cpp:
* tests/executableAllocationFuzz: Added.
* tests/executableAllocationFuzz.yaml: Added.
* tests/executableAllocationFuzz/v8-raytrace.js: Added.

Tools:

Reviewed by Mark Lam.

Bunch of support for testing executable allocation failure.

* Scripts/jsc-stress-test-helpers/js-executable-allocation-fuzz: Added.
(fail):
* Scripts/run-javascriptcore-tests:
(runJSCStressTests):
* Scripts/run-jsc-stress-tests:

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

27 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
Source/JavaScriptCore/dfg/DFGJITCompiler.h
Source/JavaScriptCore/dfg/DFGPlan.cpp
Source/JavaScriptCore/ftl/FTLCompile.cpp
Source/JavaScriptCore/ftl/FTLLink.cpp
Source/JavaScriptCore/ftl/FTLState.h
Source/JavaScriptCore/jit/ArityCheckFailReturnThunks.cpp
Source/JavaScriptCore/jit/ExecutableAllocationFuzz.cpp [new file with mode: 0644]
Source/JavaScriptCore/jit/ExecutableAllocationFuzz.h [new file with mode: 0644]
Source/JavaScriptCore/jit/ExecutableAllocatorFixedVMPool.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JITCompilationEffort.h
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/runtime/TestRunnerUtils.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/tests/executableAllocationFuzz.yaml [new file with mode: 0644]
Source/JavaScriptCore/tests/executableAllocationFuzz/v8-raytrace.js [new file with mode: 0644]
Tools/ChangeLog
Tools/Scripts/jsc-stress-test-helpers/js-executable-allocation-fuzz [new file with mode: 0755]
Tools/Scripts/run-javascriptcore-tests
Tools/Scripts/run-jsc-stress-tests

index b046736..4a15039 100644 (file)
@@ -326,6 +326,7 @@ set(JavaScriptCore_SOURCES
     jit/AssemblyHelpers.cpp
     jit/ArityCheckFailReturnThunks.cpp
     jit/BinarySwitch.cpp
+    jit/ExecutableAllocationFuzz.cpp
     jit/ExecutableAllocator.cpp
     jit/ExecutableAllocatorFixedVMPool.cpp
     jit/GCAwareJITStubRoutine.cpp
index 047135b..3172e64 100644 (file)
@@ -1,3 +1,72 @@
+2015-03-25  Filip Pizlo  <fpizlo@apple.com>
+
+        Use JITCompilationCanFail in more places, and make the fail path of JITCompilationMustSucceed a crash instead of attempting GC
+        https://bugs.webkit.org/show_bug.cgi?id=142993
+
+        Reviewed by Geoffrey Garen and Mark Lam.
+        
+        This changes the most commonly invoked paths that relied on JITCompilationMustSucceed
+        into using JITCompilationCanFail and having a legit fallback path. This mostly involves
+        having the FTL JIT do the same trick as the DFG JIT in case of any memory allocation
+        failure, but also involves adding the same kind of thing to the stub generators in
+        Repatch.
+        
+        Because of that change, there are relatively few uses of JITCompilationMustSucceed. Most
+        of those uses cannot handle a GC, and so cannot do releaseExecutableMemory(). Only a few,
+        like host call stub generation, could handle a GC, but those get invoked very rarely. So,
+        this patch changes the releaseExecutableMemory() call into a crash with some diagnostic
+        printout.
+        
+        Also add a way of inducing executable allocation failure, so that we can test this.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * dfg/DFGJITCompiler.cpp:
+        (JSC::DFG::JITCompiler::compile):
+        (JSC::DFG::JITCompiler::compileFunction):
+        (JSC::DFG::JITCompiler::link): Deleted.
+        (JSC::DFG::JITCompiler::linkFunction): Deleted.
+        * dfg/DFGJITCompiler.h:
+        * dfg/DFGPlan.cpp:
+        (JSC::DFG::Plan::compileInThreadImpl):
+        * ftl/FTLCompile.cpp:
+        (JSC::FTL::mmAllocateCodeSection):
+        (JSC::FTL::mmAllocateDataSection):
+        * ftl/FTLLink.cpp:
+        (JSC::FTL::link):
+        * ftl/FTLState.h:
+        * jit/ArityCheckFailReturnThunks.cpp:
+        (JSC::ArityCheckFailReturnThunks::returnPCsFor):
+        * jit/ExecutableAllocationFuzz.cpp: Added.
+        (JSC::numberOfExecutableAllocationFuzzChecks):
+        (JSC::doExecutableAllocationFuzzing):
+        * jit/ExecutableAllocationFuzz.h: Added.
+        (JSC::doExecutableAllocationFuzzingIfEnabled):
+        * jit/ExecutableAllocatorFixedVMPool.cpp:
+        (JSC::ExecutableAllocator::allocate):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompile):
+        * jit/JITCompilationEffort.h:
+        * jit/Repatch.cpp:
+        (JSC::generateByIdStub):
+        (JSC::tryCacheGetByID):
+        (JSC::tryBuildGetByIDList):
+        (JSC::emitPutReplaceStub):
+        (JSC::emitPutTransitionStubAndGetOldStructure):
+        (JSC::tryCachePutByID):
+        (JSC::tryBuildPutByIdList):
+        (JSC::tryRepatchIn):
+        (JSC::linkPolymorphicCall):
+        * jsc.cpp:
+        (jscmain):
+        * runtime/Options.h:
+        * runtime/TestRunnerUtils.h:
+        * runtime/VM.cpp:
+        * tests/executableAllocationFuzz: Added.
+        * tests/executableAllocationFuzz.yaml: Added.
+        * tests/executableAllocationFuzz/v8-raytrace.js: Added.
+
 2015-03-25  Mark Lam  <mark.lam@apple.com>
 
         REGRESSION(169139): LLINT intermittently fails JSC testapi tests.
index 68984f8..6545361 100644 (file)
     <ClCompile Include="..\jit\ArityCheckFailReturnThunks.cpp" />
     <ClCompile Include="..\jit\AssemblyHelpers.cpp" />
     <ClCompile Include="..\jit\BinarySwitch.cpp" />
+    <ClCompile Include="..\jit\ExecutableAllocationFuzz.cpp" />
     <ClCompile Include="..\jit\ExecutableAllocator.cpp" />
     <ClCompile Include="..\jit\ExecutableAllocatorFixedVMPool.cpp" />
     <ClCompile Include="..\jit\GCAwareJITStubRoutine.cpp" />
     <ClInclude Include="..\jit\BinarySwitch.h" />
     <ClInclude Include="..\jit\CCallHelpers.h" />
     <ClInclude Include="..\jit\CompactJITCodeMap.h" />
+    <ClInclude Include="..\jit\ExecutableAllocationFuzz.h" />
     <ClInclude Include="..\jit\ExecutableAllocator.h" />
     <ClInclude Include="..\jit\FPRInfo.h" />
     <ClInclude Include="..\jit\GCAwareJITStubRoutine.h" />
index 0a77bea..edfaba7 100644 (file)
                0FEE98431A89227500754E93 /* SetupVarargsFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEE98421A89227500754E93 /* SetupVarargsFrame.cpp */; };
                0FEFC9AA1681A3B300567F53 /* DFGOSRExitJumpPlaceholder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEFC9A71681A3B000567F53 /* DFGOSRExitJumpPlaceholder.cpp */; };
                0FEFC9AB1681A3B600567F53 /* DFGOSRExitJumpPlaceholder.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEFC9A81681A3B000567F53 /* DFGOSRExitJumpPlaceholder.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0FF054F91AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF054F71AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp */; };
+               0FF054FA1AC35B4400E5BE57 /* ExecutableAllocationFuzz.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF054F81AC35B4400E5BE57 /* ExecutableAllocationFuzz.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FF0F19916B729F6005DF95B /* DFGLongLivedState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB4B51C16B62772003F696B /* DFGLongLivedState.cpp */; };
                0FF0F19B16B729FA005DF95B /* DFGLongLivedState.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB4B51D16B62772003F696B /* DFGLongLivedState.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FF0F19C16B72A03005DF95B /* DFGNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB4B51E16B62772003F696B /* DFGNode.cpp */; };
                0FEE98421A89227500754E93 /* SetupVarargsFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SetupVarargsFrame.cpp; sourceTree = "<group>"; };
                0FEFC9A71681A3B000567F53 /* DFGOSRExitJumpPlaceholder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGOSRExitJumpPlaceholder.cpp; path = dfg/DFGOSRExitJumpPlaceholder.cpp; sourceTree = "<group>"; };
                0FEFC9A81681A3B000567F53 /* DFGOSRExitJumpPlaceholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGOSRExitJumpPlaceholder.h; path = dfg/DFGOSRExitJumpPlaceholder.h; sourceTree = "<group>"; };
+               0FF054F71AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutableAllocationFuzz.cpp; sourceTree = "<group>"; };
+               0FF054F81AC35B4400E5BE57 /* ExecutableAllocationFuzz.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutableAllocationFuzz.h; sourceTree = "<group>"; };
                0FF4272F158EBD44004CB9FF /* Disassembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Disassembler.h; path = disassembler/Disassembler.h; sourceTree = "<group>"; };
                0FF42730158EBD44004CB9FF /* UDis86Disassembler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UDis86Disassembler.cpp; path = disassembler/UDis86Disassembler.cpp; sourceTree = "<group>"; };
                0FF42734158EBD94004CB9FF /* udis86_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = udis86_decode.c; path = disassembler/udis86/udis86_decode.c; sourceTree = "<group>"; };
                1429D92C0ED22D7000B89619 /* jit */ = {
                        isa = PBXGroup;
                        children = (
+                               0FF054F71AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp */,
+                               0FF054F81AC35B4400E5BE57 /* ExecutableAllocationFuzz.h */,
                                0F7576D018E1FEE9002EF4CD /* AccessorCallJITStubRoutine.cpp */,
                                0F7576D118E1FEE9002EF4CD /* AccessorCallJITStubRoutine.h */,
                                0F6B1CC718641DF800845D97 /* ArityCheckFailReturnThunks.cpp */,
                                A704D90317A0BAA8006BA554 /* DFGAbstractInterpreter.h in Headers */,
                                A704D90417A0BAA8006BA554 /* DFGAbstractInterpreterInlines.h in Headers */,
                                0F620177143FCD3F0068B77C /* DFGAbstractValue.h in Headers */,
+                               0FF054FA1AC35B4400E5BE57 /* ExecutableAllocationFuzz.h in Headers */,
                                0F66E16B14DF3F1600B7B2E4 /* DFGAdjacencyList.h in Headers */,
                                0FFB921816D02EB20055A5DB /* DFGAllocator.h in Headers */,
                                A737810C1799EA2E00817533 /* DFGAnalysis.h in Headers */,
                                0F2B66FA17B6B5AB00A7AE3F /* JSTypedArrayConstructors.cpp in Sources */,
                                0F2B66FC17B6B5AB00A7AE3F /* JSTypedArrayPrototypes.cpp in Sources */,
                                0F2B66FE17B6B5AB00A7AE3F /* JSTypedArrays.cpp in Sources */,
+                               0FF054F91AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp in Sources */,
                                86E3C61A167BABEE006D760A /* JSValue.mm in Sources */,
                                14BD5A320A3E91F600BAF59C /* JSValueRef.cpp in Sources */,
                                147F39D7107EC37600427A48 /* JSEnvironmentRecord.cpp in Sources */,
index f20bb48..8c2bc31 100644 (file)
@@ -325,10 +325,7 @@ void JITCompiler::compile()
     // Create OSR entry trampolines if necessary.
     m_speculative->createOSREntries();
     setEndOfCode();
-}
 
-void JITCompiler::link()
-{
     auto linkBuffer = std::make_unique<LinkBuffer>(*m_vm, *this, m_codeBlock, JITCompilationCanFail);
     if (linkBuffer->didFailToAllocate()) {
         m_graph.m_plan.finalizer = std::make_unique<FailedFinalizer>(m_graph.m_plan);
@@ -412,7 +409,9 @@ void JITCompiler::compileFunction()
 #else
     thunkReg = GPRInfo::regT5;
 #endif
-    move(TrustedImmPtr(m_vm->arityCheckFailReturnThunks->returnPCsFor(*m_vm, m_codeBlock->numParameters())), thunkReg);
+    CodeLocationLabel* arityThunkLabels =
+        m_vm->arityCheckFailReturnThunks->returnPCsFor(*m_vm, m_codeBlock->numParameters());
+    move(TrustedImmPtr(arityThunkLabels), thunkReg);
     loadPtr(BaseIndex(thunkReg, GPRInfo::regT0, timesPtr()), thunkReg);
     m_callArityFixup = call();
     jump(fromArityCheck);
@@ -426,10 +425,7 @@ void JITCompiler::compileFunction()
     // Create OSR entry trampolines if necessary.
     m_speculative->createOSREntries();
     setEndOfCode();
-}
 
-void JITCompiler::linkFunction()
-{
     // === Link ===
     auto linkBuffer = std::make_unique<LinkBuffer>(*m_vm, *this, m_codeBlock, JITCompilationCanFail);
     if (linkBuffer->didFailToAllocate()) {
index 9784f28..b10be8c 100644 (file)
@@ -111,9 +111,6 @@ public:
     void compile();
     void compileFunction();
     
-    void link();
-    void linkFunction();
-
     // Accessors for properties.
     Graph& graph() { return m_graph; }
     
index fbc1ae2..a3f33ab 100644 (file)
@@ -299,13 +299,10 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
         dumpAndVerifyGraph(dfg, "Graph after optimization:");
         
         JITCompiler dataFlowJIT(dfg);
-        if (codeBlock->codeType() == FunctionCode) {
+        if (codeBlock->codeType() == FunctionCode)
             dataFlowJIT.compileFunction();
-            dataFlowJIT.linkFunction();
-        } else {
+        else
             dataFlowJIT.compile();
-            dataFlowJIT.link();
-        }
         
         return DFGPath;
     }
@@ -404,6 +401,11 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
             FTL::fail(state);
             return FTLPath;
         }
+        
+        if (state.allocationFailed) {
+            FTL::fail(state);
+            return FTLPath;
+        }
 
         if (state.jitCode->stackmaps.stackSize() > Options::llvmMaxStackSize()) {
             FTL::fail(state);
@@ -411,6 +413,12 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
         }
 
         FTL::link(state);
+        
+        if (state.allocationFailed) {
+            FTL::fail(state);
+            return FTLPath;
+        }
+        
         return FTLPath;
 #else
         RELEASE_ASSERT_NOT_REACHED();
index c21de47..4b41b89 100644 (file)
@@ -59,7 +59,17 @@ static uint8_t* mmAllocateCodeSection(
     
     RefPtr<ExecutableMemoryHandle> result =
         state.graph.m_vm.executableAllocator.allocate(
-            state.graph.m_vm, size, state.graph.m_codeBlock, JITCompilationMustSucceed);
+            state.graph.m_vm, size, state.graph.m_codeBlock, JITCompilationCanFail);
+    
+    if (!result) {
+        // Signal failure. This compilation will get tossed.
+        state.allocationFailed = true;
+        
+        // Fake an allocation, since LLVM cannot handle failures in the memory manager.
+        RefPtr<DataSection> fakeSection = adoptRef(new DataSection(size, jitAllocationGranule));
+        state.jitCode->addDataSection(fakeSection);
+        return bitwise_cast<uint8_t*>(fakeSection->base());
+    }
     
     // LLVM used to put __compact_unwind in a code section. We keep this here defensively,
     // for clients that use older LLVMs.
@@ -344,7 +354,11 @@ static void fixFunctionBasedOnStackMaps(
         checkJIT.jumpToExceptionHandler();
 
         auto linkBuffer = std::make_unique<LinkBuffer>(
-            vm, checkJIT, codeBlock, JITCompilationMustSucceed);
+            vm, checkJIT, codeBlock, JITCompilationCanFail);
+        if (linkBuffer->didFailToAllocate()) {
+            state.allocationFailed = true;
+            return;
+        }
         linkBuffer->link(callLookupExceptionHandler, FunctionPtr(lookupExceptionHandler));
         linkBuffer->link(callLookupExceptionHandlerFromCallerFrame, FunctionPtr(lookupExceptionHandlerFromCallerFrame));
 
@@ -358,7 +372,11 @@ static void fixFunctionBasedOnStackMaps(
         RELEASE_ASSERT(didSeeUnwindInfo);
         
         auto linkBuffer = std::make_unique<LinkBuffer>(
-            vm, exitThunkGenerator, codeBlock, JITCompilationMustSucceed);
+            vm, exitThunkGenerator, codeBlock, JITCompilationCanFail);
+        if (linkBuffer->didFailToAllocate()) {
+            state.allocationFailed = true;
+            return;
+        }
         
         RELEASE_ASSERT(state.finalizer->osrExit.size() == state.jitCode->osrExit.size());
         
@@ -516,7 +534,11 @@ static void fixFunctionBasedOnStackMaps(
         exceptionTarget.link(&slowPathJIT);
         MacroAssembler::Jump exceptionJump = slowPathJIT.jump();
         
-        state.finalizer->sideCodeLinkBuffer = std::make_unique<LinkBuffer>(vm, slowPathJIT, codeBlock, JITCompilationMustSucceed);
+        state.finalizer->sideCodeLinkBuffer = std::make_unique<LinkBuffer>(vm, slowPathJIT, codeBlock, JITCompilationCanFail);
+        if (state.finalizer->sideCodeLinkBuffer->didFailToAllocate()) {
+            state.allocationFailed = true;
+            return;
+        }
         state.finalizer->sideCodeLinkBuffer->link(
             exceptionJump, state.finalizer->handleExceptionsLinkBuffer->entrypoint());
         
@@ -675,13 +697,17 @@ void compile(State& state, Safepoint::Result& safepointResult)
             dataLog("FATAL: Could not create LLVM execution engine: ", error, "\n");
             CRASH();
         }
+        
+        // At this point we no longer own the module.
+        LModule module = state.module;
+        state.module = nullptr;
 
         // The data layout also has to be set in the module. Get the data layout from the MCJIT and apply
         // it to the module.
         LLVMTargetMachineRef targetMachine = llvm->GetExecutionEngineTargetMachine(engine);
         LLVMTargetDataRef targetData = llvm->GetExecutionEngineTargetData(engine);
         char* stringRepOfTargetData = llvm->CopyStringRepOfTargetData(targetData);
-        llvm->SetDataLayout(state.module, stringRepOfTargetData);
+        llvm->SetDataLayout(module, stringRepOfTargetData);
         free(stringRepOfTargetData);
 
         LLVMPassManagerRef functionPasses = 0;
@@ -707,14 +733,14 @@ void compile(State& state, Safepoint::Result& safepointResult)
             llvm->AddCFGSimplificationPass(modulePasses);
             llvm->AddDeadStoreEliminationPass(modulePasses);
 
-            llvm->RunPassManager(modulePasses, state.module);
+            llvm->RunPassManager(modulePasses, module);
         } else {
             LLVMPassManagerBuilderRef passBuilder = llvm->PassManagerBuilderCreate();
             llvm->PassManagerBuilderSetOptLevel(passBuilder, Options::llvmOptimizationLevel());
             llvm->PassManagerBuilderUseInlinerWithThreshold(passBuilder, 275);
             llvm->PassManagerBuilderSetSizeLevel(passBuilder, Options::llvmSizeLevel());
         
-            functionPasses = llvm->CreateFunctionPassManagerForModule(state.module);
+            functionPasses = llvm->CreateFunctionPassManagerForModule(module);
             modulePasses = llvm->CreatePassManager();
         
             llvm->AddTargetData(llvm->GetExecutionEngineTargetData(engine), modulePasses);
@@ -725,11 +751,11 @@ void compile(State& state, Safepoint::Result& safepointResult)
             llvm->PassManagerBuilderDispose(passBuilder);
         
             llvm->InitializeFunctionPassManager(functionPasses);
-            for (LValue function = llvm->GetFirstFunction(state.module); function; function = llvm->GetNextFunction(function))
+            for (LValue function = llvm->GetFirstFunction(module); function; function = llvm->GetNextFunction(function))
                 llvm->RunFunctionPassManager(functionPasses, function);
             llvm->FinalizeFunctionPassManager(functionPasses);
         
-            llvm->RunPassManager(modulePasses, state.module);
+            llvm->RunPassManager(modulePasses, module);
         }
 
         if (shouldShowDisassembly() || verboseCompilationEnabled())
@@ -743,10 +769,14 @@ void compile(State& state, Safepoint::Result& safepointResult)
         llvm->DisposePassManager(modulePasses);
         llvm->DisposeExecutionEngine(engine);
     }
+
     if (safepointResult.didGetCancelled())
         return;
     RELEASE_ASSERT(!state.graph.m_vm.heap.isCollecting());
     
+    if (state.allocationFailed)
+        return;
+    
     if (shouldShowDisassembly()) {
         for (unsigned i = 0; i < state.jitCode->handles().size(); ++i) {
             ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get();
@@ -802,6 +832,8 @@ void compile(State& state, Safepoint::Result& safepointResult)
         fixFunctionBasedOnStackMaps(
             state, state.graph.m_codeBlock, state.jitCode.get(), state.generatedFunction,
             recordMap, didSeeUnwindInfo);
+        if (state.allocationFailed)
+            return;
         
         if (shouldShowDisassembly() || Options::asyncDisassembly()) {
             for (unsigned i = 0; i < state.jitCode->handles().size(); ++i) {
@@ -830,8 +862,6 @@ void compile(State& state, Safepoint::Result& safepointResult)
             }
         }
     }
-    
-    state.module = 0; // We no longer own the module.
 }
 
 } } // namespace JSC::FTL
index 357004a..c1fb929 100644 (file)
@@ -171,13 +171,19 @@ void link(State& state)
         jit.emitFunctionEpilogue();
         mainPathJumps.append(jit.branchTest32(CCallHelpers::Zero, GPRInfo::regT0));
         jit.emitFunctionPrologue();
-        jit.move(CCallHelpers::TrustedImmPtr(vm.arityCheckFailReturnThunks->returnPCsFor(vm, codeBlock->numParameters())), GPRInfo::regT7);
+        CodeLocationLabel* arityThunkLabels =
+            vm.arityCheckFailReturnThunks->returnPCsFor(vm, codeBlock->numParameters());
+        jit.move(CCallHelpers::TrustedImmPtr(arityThunkLabels), GPRInfo::regT7);
         jit.loadPtr(CCallHelpers::BaseIndex(GPRInfo::regT7, GPRInfo::regT0, CCallHelpers::timesPtr()), GPRInfo::regT7);
         CCallHelpers::Call callArityFixup = jit.call();
         jit.emitFunctionEpilogue();
         mainPathJumps.append(jit.jump());
 
-        linkBuffer = std::make_unique<LinkBuffer>(vm, jit, codeBlock, JITCompilationMustSucceed);
+        linkBuffer = std::make_unique<LinkBuffer>(vm, jit, codeBlock, JITCompilationCanFail);
+        if (linkBuffer->didFailToAllocate()) {
+            state.allocationFailed = true;
+            return;
+        }
         linkBuffer->link(callArityCheck, codeBlock->m_isConstructor ? operationConstructArityCheck : operationCallArityCheck);
         linkBuffer->link(callArityFixup, FunctionPtr((vm.getCTIStub(arityFixupGenerator)).code().executableAddress()));
         linkBuffer->link(mainPathJumps, CodeLocationLabel(bitwise_cast<void*>(state.generatedFunction)));
@@ -195,7 +201,11 @@ void link(State& state)
         jit.emitFunctionEpilogue();
         CCallHelpers::Jump mainPathJump = jit.jump();
         
-        linkBuffer = std::make_unique<LinkBuffer>(vm, jit, codeBlock, JITCompilationMustSucceed);
+        linkBuffer = std::make_unique<LinkBuffer>(vm, jit, codeBlock, JITCompilationCanFail);
+        if (linkBuffer->didFailToAllocate()) {
+            state.allocationFailed = true;
+            return;
+        }
         linkBuffer->link(mainPathJump, CodeLocationLabel(bitwise_cast<void*>(state.generatedFunction)));
 
         state.jitCode->initializeAddressForCall(linkBuffer->locationOf(start));
index 56e17a3..2e2947a 100644 (file)
@@ -66,6 +66,7 @@ public:
     LContext context;
     LModule module;
     LValue function;
+    bool allocationFailed { false }; // Throw out the compilation once LLVM returns.
     RefPtr<JITCode> jitCode;
     GeneratedFunction generatedFunction;
     JITFinalizer* finalizer;
index 7e9b77d..d522b81 100644 (file)
@@ -97,7 +97,8 @@ CodeLocationLabel* ArityCheckFailReturnThunks::returnPCsFor(
         jit.jump(GPRInfo::regT2);
     }
     
-    LinkBuffer linkBuffer(vm, jit, GLOBAL_THUNK_ID);
+    // Sadly, we cannot fail here because the LLInt may need us.
+    LinkBuffer linkBuffer(vm, jit, GLOBAL_THUNK_ID, JITCompilationMustSucceed);
     
     unsigned returnPCsSize = numExpectedArgumentsIncludingThis / stackAlignmentRegisters() + 1;
     std::unique_ptr<CodeLocationLabel[]> returnPCs =
diff --git a/Source/JavaScriptCore/jit/ExecutableAllocationFuzz.cpp b/Source/JavaScriptCore/jit/ExecutableAllocationFuzz.cpp
new file mode 100644 (file)
index 0000000..9cee5d9
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "ExecutableAllocationFuzz.h"
+
+#include "TestRunnerUtils.h"
+#include <wtf/Atomics.h>
+#include <wtf/DataLog.h>
+
+namespace JSC {
+
+static Atomic<unsigned> s_numberOfExecutableAllocationFuzzChecks;
+unsigned numberOfExecutableAllocationFuzzChecks()
+{
+    return s_numberOfExecutableAllocationFuzzChecks.load();
+}
+
+ExecutableAllocationFuzzResult doExecutableAllocationFuzzing()
+{
+    ASSERT(Options::enableExecutableAllocationFuzz());
+    
+    unsigned oldValue;
+    unsigned newValue;
+    do {
+        oldValue = s_numberOfExecutableAllocationFuzzChecks.load();
+        newValue = oldValue + 1;
+    } while (!s_numberOfExecutableAllocationFuzzChecks.compareExchangeWeak(oldValue, newValue));
+    
+    if (newValue == Options::fireExecutableAllocationFuzzAt()) {
+        if (Options::verboseExecutableAllocationFuzz()) {
+            dataLog("Will pretend to fail executable allocation.\n");
+            WTFReportBacktrace();
+        }
+        return PretendToFailExecutableAllocation;
+    }
+    
+    if (Options::fireExecutableAllocationFuzzAtOrAfter()
+        && newValue >= Options::fireExecutableAllocationFuzzAtOrAfter()) {
+        if (Options::verboseExecutableAllocationFuzz()) {
+            dataLog("Will pretend to fail executable allocation.\n");
+            WTFReportBacktrace();
+        }
+        return PretendToFailExecutableAllocation;
+    }
+    
+    return AllowNormalExecutableAllocation;
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/jit/ExecutableAllocationFuzz.h b/Source/JavaScriptCore/jit/ExecutableAllocationFuzz.h
new file mode 100644 (file)
index 0000000..4997d21
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef ExecutableAllocationFuzz_h
+#define ExecutableAllocationFuzz_h
+
+#include "Options.h"
+
+namespace JSC {
+
+enum ExecutableAllocationFuzzResult {
+    AllowNormalExecutableAllocation,
+    PretendToFailExecutableAllocation
+};
+
+ExecutableAllocationFuzzResult doExecutableAllocationFuzzing();
+
+inline ExecutableAllocationFuzzResult doExecutableAllocationFuzzingIfEnabled()
+{
+    if (LIKELY(!Options::enableExecutableAllocationFuzz()))
+        return AllowNormalExecutableAllocation;
+    
+    return doExecutableAllocationFuzzing();
+}
+
+} // namespace JSC
+
+#endif // ExecutableAllocationFuzz_h
+
index e42606e..3bfa368 100644 (file)
@@ -31,6 +31,7 @@
 #if ENABLE(EXECUTABLE_ALLOCATOR_FIXED)
 
 #include "CodeProfiling.h"
+#include "ExecutableAllocationFuzz.h"
 #include <errno.h>
 #if !PLATFORM(WIN)
 #include <unistd.h>
@@ -158,15 +159,24 @@ double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage)
     return result;
 }
 
-PassRefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(VM& vm, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort)
+PassRefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(VM&, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort)
 {
+    if (effort != JITCompilationCanFail && Options::reportMustSucceedExecutableAllocations()) {
+        dataLog("Allocating ", sizeInBytes, " bytes of executable memory with JITCompilationMustSucceed.\n");
+        WTFReportBacktrace();
+    }
+    
+    if (effort == JITCompilationCanFail
+        && doExecutableAllocationFuzzingIfEnabled() == PretendToFailExecutableAllocation)
+        return nullptr;
+    
     RefPtr<ExecutableMemoryHandle> result = allocator->allocate(sizeInBytes, ownerUID);
     if (!result) {
-        if (effort == JITCompilationCanFail)
-            return result;
-        releaseExecutableMemory(vm);
-        result = allocator->allocate(sizeInBytes, ownerUID);
-        RELEASE_ASSERT(result);
+        if (effort != JITCompilationCanFail) {
+            dataLog("Ran out of executable memory while allocating ", sizeInBytes, " bytes.\n");
+            CRASH();
+        }
+        return nullptr;
     }
     return result.release();
 }
index b6469a1..c1f1746 100644 (file)
@@ -585,7 +585,9 @@ CompilationResult JIT::privateCompile(JITCompilationEffort effort)
 #else
         thunkReg = GPRInfo::regT5;
 #endif
-        move(TrustedImmPtr(m_vm->arityCheckFailReturnThunks->returnPCsFor(*m_vm, m_codeBlock->numParameters())), thunkReg);
+        CodeLocationLabel* failThunkLabels =
+            m_vm->arityCheckFailReturnThunks->returnPCsFor(*m_vm, m_codeBlock->numParameters());
+        move(TrustedImmPtr(failThunkLabels), thunkReg);
         loadPtr(BaseIndex(thunkReg, regT0, timesPtr()), thunkReg);
         emitNakedCall(m_vm->getCTIStub(arityFixupGenerator).code());
 
index 5eb6801..29e9542 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
index 67e654a..ca03a47 100644 (file)
@@ -293,7 +293,7 @@ static FunctionPtr customFor(const PutPropertySlot& slot)
     return FunctionPtr(slot.customSetter());
 }
 
-static void generateByIdStub(
+static bool generateByIdStub(
     ExecState* exec, ByIdStubKind kind, const Identifier& propertyName,
     FunctionPtr custom, StructureStubInfo& stubInfo, StructureChain* chain, size_t count,
     PropertyOffset offset, Structure* structure, bool loadTargetFromProxy, WatchpointSet* watchpointSet,
@@ -574,7 +574,9 @@ static void generateByIdStub(
     }
     emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
     
-    LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock());
+    LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
+    if (patchBuffer.didFailToAllocate())
+        return false;
     
     linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
     if (kind == CallCustomGetter || kind == CallCustomSetter) {
@@ -601,6 +603,8 @@ static void generateByIdStub(
         stubRoutine = adoptRef(new AccessorCallJITStubRoutine(code, *vm, WTF::move(callLinkInfo)));
     else
         stubRoutine = createJITStubRoutine(code, *vm, codeBlock->ownerExecutable(), true);
+    
+    return true;
 }
 
 enum InlineCacheAction {
@@ -687,7 +691,9 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
 
             emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
             
-            LinkBuffer patchBuffer(*vm, stubJit, codeBlock);
+            LinkBuffer patchBuffer(*vm, stubJit, codeBlock, JITCompilationCanFail);
+            if (patchBuffer.didFailToAllocate())
+                return GiveUpOnCache;
 
             linkRestoreScratch(patchBuffer, needToRestoreScratch, stubInfo, success, fail, failureCases);
 
@@ -717,8 +723,10 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
 
         MacroAssembler::Jump success = stubJit.jump();
 
-        LinkBuffer patchBuffer(*vm, stubJit, codeBlock);
-
+        LinkBuffer patchBuffer(*vm, stubJit, codeBlock, JITCompilationCanFail);
+        if (patchBuffer.didFailToAllocate())
+            return GiveUpOnCache;
+        
         patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
         patchBuffer.link(failure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
 
@@ -844,11 +852,13 @@ static InlineCacheAction tryBuildGetByIDList(ExecState* exec, JSValue baseValue,
     }
     
     RefPtr<JITStubRoutine> stubRoutine;
-    generateByIdStub(
+    bool result = generateByIdStub(
         exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count, offset, 
         structure, loadTargetFromProxy, slot.watchpointSet(), 
         stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
         CodeLocationLabel(list->currentSlowPathTarget(stubInfo)), stubRoutine);
+    if (!result)
+        return GiveUpOnCache;
     
     GetByIdAccess::AccessType accessType;
     if (slot.isCacheableValue())
@@ -901,7 +911,7 @@ static V_JITOperation_ESsiJJI appropriateListBuildingPutByIdFunction(const PutPr
     return operationPutByIdNonStrictBuildList;
 }
 
-static void emitPutReplaceStub(
+static bool emitPutReplaceStub(
     ExecState* exec,
     const Identifier&,
     const PutPropertySlot& slot,
@@ -968,7 +978,10 @@ static void emitPutReplaceStub(
         failure = badStructure;
     }
     
-    LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock());
+    LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
+    if (patchBuffer.didFailToAllocate())
+        return false;
+    
     patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
     patchBuffer.link(failure, failureLabel);
             
@@ -977,6 +990,8 @@ static void emitPutReplaceStub(
         ("PutById replace stub for %s, return point %p",
             toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
                 stubInfo.patch.deltaCallToDone).executableAddress()));
+    
+    return true;
 }
 
 static Structure* emitPutTransitionStubAndGetOldStructure(ExecState* exec, VM* vm, Structure*& structure, const Identifier& ident, 
@@ -1213,7 +1228,10 @@ static Structure* emitPutTransitionStubAndGetOldStructure(ExecState* exec, VM* v
         successInSlowPath = stubJit.jump();
     }
     
-    LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock());
+    LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
+    if (patchBuffer.didFailToAllocate())
+        return nullptr;
+    
     patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
     if (allocator.didReuseRegisters())
         patchBuffer.link(failure, failureLabel);
@@ -1308,13 +1326,15 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
         PolymorphicPutByIdList* list;
         list = PolymorphicPutByIdList::from(putKind, stubInfo);
 
-        generateByIdStub(
+        bool result = generateByIdStub(
             exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count,
             offset, structure, false, nullptr,
             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
             stubRoutine);
-
+        if (!result)
+            return GiveUpOnCache;
+        
         list->addAccess(PutByIdAccess::setter(
             *vm, codeBlock->ownerExecutable(),
             slot.isCacheableSetter() ? PutByIdAccess::Setter : PutByIdAccess::CustomSetter,
@@ -1383,10 +1403,12 @@ static InlineCacheAction tryBuildPutByIdList(ExecState* exec, JSValue baseValue,
             structure->didCachePropertyReplacement(*vm, slot.cachedOffset());
             
             // We're now committed to creating the stub. Mogrify the meta-data accordingly.
-            emitPutReplaceStub(
+            bool result = emitPutReplaceStub(
                 exec, propertyName, slot, stubInfo, 
                 structure, CodeLocationLabel(list->currentSlowPathTarget()), stubRoutine);
-
+            if (!result)
+                return GiveUpOnCache;
+            
             list->addAccess(
                 PutByIdAccess::replace(
                     *vm, codeBlock->ownerExecutable(),
@@ -1416,13 +1438,15 @@ static InlineCacheAction tryBuildPutByIdList(ExecState* exec, JSValue baseValue,
         PolymorphicPutByIdList* list;
         list = PolymorphicPutByIdList::from(putKind, stubInfo);
 
-        generateByIdStub(
+        bool result = generateByIdStub(
             exec, kindFor(slot), propertyName, customFor(slot), stubInfo, prototypeChain, count,
             offset, structure, false, nullptr,
             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
             CodeLocationLabel(list->currentSlowPathTarget()),
             stubRoutine);
-
+        if (!result)
+            return GiveUpOnCache;
+        
         list->addAccess(PutByIdAccess::setter(
             *vm, codeBlock->ownerExecutable(),
             slot.isCacheableSetter() ? PutByIdAccess::Setter : PutByIdAccess::CustomSetter,
@@ -1548,8 +1572,10 @@ static InlineCacheAction tryRepatchIn(
         
         emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
         
-        LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock());
-
+        LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
+        if (patchBuffer.didFailToAllocate())
+            return GiveUpOnCache;
+        
         linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
         
         stubRoutine = FINALIZE_CODE_FOR_STUB(
@@ -1851,7 +1877,11 @@ void linkPolymorphicCall(
     stubJit.restoreReturnAddressBeforeReturn(GPRInfo::regT4);
     AssemblyHelpers::Jump slow = stubJit.jump();
         
-    LinkBuffer patchBuffer(*vm, stubJit, callerCodeBlock);
+    LinkBuffer patchBuffer(*vm, stubJit, callerCodeBlock, JITCompilationCanFail);
+    if (patchBuffer.didFailToAllocate()) {
+        linkVirtualFor(exec, callLinkInfo, CodeForCall, registers);
+        return;
+    }
     
     RELEASE_ASSERT(callCases.size() == calls.size());
     for (CallToCodePtr callToCodePtr : calls) {
index ec55581..2b2d1b7 100644 (file)
@@ -1498,6 +1498,10 @@ int jscmain(int argc, char** argv)
 #if ENABLE(JIT)
         if (Options::enableExceptionFuzz())
             printf("JSC EXCEPTION FUZZ: encountered %u checks.\n", numberOfExceptionFuzzChecks());
+        bool fireAtEnabled =
+            Options::fireExecutableAllocationFuzzAt() || Options::fireExecutableAllocationFuzzAtOrAfter();
+        if (Options::enableExecutableAllocationFuzz() && (!fireAtEnabled || Options::verboseExecutableAllocationFuzz()))
+            printf("JSC EXECUTABLE ALLOCATION FUZZ: encountered %u checks.\n", numberOfExecutableAllocationFuzzChecks());
 #endif
     }
     
index f26fcb3..e72bbd9 100644 (file)
@@ -97,6 +97,8 @@ typedef const char* optionString;
     v(bool, useDFGJIT, true) \
     v(bool, useRegExpJIT, true) \
     \
+    v(bool, reportMustSucceedExecutableAllocations, false) \
+    \
     v(unsigned, maxPerThreadStackUsage, 4 * MB) \
     v(unsigned, reservedZoneSize, 128 * KB) \
     v(unsigned, errorModeReservedZoneSize, 64 * KB) \
@@ -291,7 +293,12 @@ typedef const char* optionString;
     v(unsigned, numberOfGCCyclesToRecordForVerification, 3) \
     \
     v(bool, enableExceptionFuzz, false) \
-    v(unsigned, fireExceptionFuzzAt, 0)
+    v(unsigned, fireExceptionFuzzAt, 0) \
+    \
+    v(bool, enableExecutableAllocationFuzz, false) \
+    v(unsigned, fireExecutableAllocationFuzzAt, 0) \
+    v(unsigned, fireExecutableAllocationFuzzAtOrAfter, 0) \
+    v(bool, verboseExecutableAllocationFuzz, false)
 
 class Options {
 public:
index 45e3385..4ca16b5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -45,6 +45,7 @@ JS_EXPORT_PRIVATE JSValue setNeverInline(ExecState*);
 JS_EXPORT_PRIVATE JSValue optimizeNextInvocation(ExecState*);
 
 JS_EXPORT_PRIVATE unsigned numberOfExceptionFuzzChecks();
+JS_EXPORT_PRIVATE unsigned numberOfExecutableAllocationFuzzChecks();
 
 } // namespace JSC
 
index 264360a..07ea468 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2011, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2011, 2013-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
diff --git a/Source/JavaScriptCore/tests/executableAllocationFuzz.yaml b/Source/JavaScriptCore/tests/executableAllocationFuzz.yaml
new file mode 100644 (file)
index 0000000..66e651c
--- /dev/null
@@ -0,0 +1,31 @@
+# Copyright (C) 2015 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer. 
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution. 
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+- path: executableAllocationFuzz
+  cmd: |
+      if ($hostOS == "windows")
+          skip
+      else
+          runExecutableAllocationFuzz("default")
+          runExecutableAllocationFuzz("no-cjit", "--enableConcurrentJIT=false")
+      end
diff --git a/Source/JavaScriptCore/tests/executableAllocationFuzz/v8-raytrace.js b/Source/JavaScriptCore/tests/executableAllocationFuzz/v8-raytrace.js
new file mode 100644 (file)
index 0000000..1805ae5
--- /dev/null
@@ -0,0 +1,902 @@
+// The ray tracer code in this file is written by Adam Burmister. It
+// is available in its original form from:
+//
+//   http://labs.flog.nz.co/raytracer/
+//
+// It has been modified slightly by Google to work as a standalone
+// benchmark, but the all the computational code remains
+// untouched. This file also contains a copy of parts of the Prototype
+// JavaScript framework which is used by the ray tracer.
+
+// Variable used to hold a number that can be used to verify that
+// the scene was ray traced correctly.
+var checkNumber;
+
+
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+
+// The following is a copy of parts of the Prototype JavaScript library:
+
+// Prototype JavaScript framework, version 1.5.0
+// (c) 2005-2007 Sam Stephenson
+//
+// Prototype is freely distributable under the terms of an MIT-style license.
+// For details, see the Prototype web site: http://prototype.conio.net/
+
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+};
+
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+};
+
+
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+
+// The rest of this file is the actual ray tracer written by Adam
+// Burmister. It's a concatenation of the following files:
+//
+//   flog/color.js
+//   flog/light.js
+//   flog/vector.js
+//   flog/ray.js
+//   flog/scene.js
+//   flog/material/basematerial.js
+//   flog/material/solid.js
+//   flog/material/chessboard.js
+//   flog/shape/baseshape.js
+//   flog/shape/sphere.js
+//   flog/shape/plane.js
+//   flog/intersectioninfo.js
+//   flog/camera.js
+//   flog/background.js
+//   flog/engine.js
+
+
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Color = Class.create();
+
+Flog.RayTracer.Color.prototype = {
+    red : 0.0,
+    green : 0.0,
+    blue : 0.0,
+
+    initialize : function(r, g, b) {
+        if(!r) r = 0.0;
+        if(!g) g = 0.0;
+        if(!b) b = 0.0;
+
+        this.red = r;
+        this.green = g;
+        this.blue = b;
+    },
+
+    add : function(c1, c2){
+        var result = new Flog.RayTracer.Color(0,0,0);
+
+        result.red = c1.red + c2.red;
+        result.green = c1.green + c2.green;
+        result.blue = c1.blue + c2.blue;
+
+        return result;
+    },
+
+    addScalar: function(c1, s){
+        var result = new Flog.RayTracer.Color(0,0,0);
+
+        result.red = c1.red + s;
+        result.green = c1.green + s;
+        result.blue = c1.blue + s;
+
+        result.limit();
+
+        return result;
+    },
+
+    subtract: function(c1, c2){
+        var result = new Flog.RayTracer.Color(0,0,0);
+
+        result.red = c1.red - c2.red;
+        result.green = c1.green - c2.green;
+        result.blue = c1.blue - c2.blue;
+
+        return result;
+    },
+
+    multiply : function(c1, c2) {
+        var result = new Flog.RayTracer.Color(0,0,0);
+
+        result.red = c1.red * c2.red;
+        result.green = c1.green * c2.green;
+        result.blue = c1.blue * c2.blue;
+
+        return result;
+    },
+
+    multiplyScalar : function(c1, f) {
+        var result = new Flog.RayTracer.Color(0,0,0);
+
+        result.red = c1.red * f;
+        result.green = c1.green * f;
+        result.blue = c1.blue * f;
+
+        return result;
+    },
+
+    divideFactor : function(c1, f) {
+        var result = new Flog.RayTracer.Color(0,0,0);
+
+        result.red = c1.red / f;
+        result.green = c1.green / f;
+        result.blue = c1.blue / f;
+
+        return result;
+    },
+
+    limit: function(){
+        this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
+        this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
+        this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
+    },
+
+    distance : function(color) {
+        var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
+        return d;
+    },
+
+    blend: function(c1, c2, w){
+        var result = new Flog.RayTracer.Color(0,0,0);
+        result = Flog.RayTracer.Color.prototype.add(
+                    Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
+                    Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
+                  );
+        return result;
+    },
+
+    brightness : function() {
+        var r = Math.floor(this.red*255);
+        var g = Math.floor(this.green*255);
+        var b = Math.floor(this.blue*255);
+        return (r * 77 + g * 150 + b * 29) >> 8;
+    },
+
+    toString : function () {
+        var r = Math.floor(this.red*255);
+        var g = Math.floor(this.green*255);
+        var b = Math.floor(this.blue*255);
+
+        return "rgb("+ r +","+ g +","+ b +")";
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Light = Class.create();
+
+Flog.RayTracer.Light.prototype = {
+    position: null,
+    color: null,
+    intensity: 10.0,
+
+    initialize : function(pos, color, intensity) {
+        this.position = pos;
+        this.color = color;
+        this.intensity = (intensity ? intensity : 10.0);
+    },
+
+    toString : function () {
+        return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Vector = Class.create();
+
+Flog.RayTracer.Vector.prototype = {
+    x : 0.0,
+    y : 0.0,
+    z : 0.0,
+
+    initialize : function(x, y, z) {
+        this.x = (x ? x : 0);
+        this.y = (y ? y : 0);
+        this.z = (z ? z : 0);
+    },
+
+    copy: function(vector){
+        this.x = vector.x;
+        this.y = vector.y;
+        this.z = vector.z;
+    },
+
+    normalize : function() {
+        var m = this.magnitude();
+        return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
+    },
+
+    magnitude : function() {
+        return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
+    },
+
+    cross : function(w) {
+        return new Flog.RayTracer.Vector(
+                                            -this.z * w.y + this.y * w.z,
+                                           this.z * w.x - this.x * w.z,
+                                          -this.y * w.x + this.x * w.y);
+    },
+
+    dot : function(w) {
+        return this.x * w.x + this.y * w.y + this.z * w.z;
+    },
+
+    add : function(v, w) {
+        return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
+    },
+
+    subtract : function(v, w) {
+        if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
+        return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
+    },
+
+    multiplyVector : function(v, w) {
+        return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
+    },
+
+    multiplyScalar : function(v, w) {
+        return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
+    },
+
+    toString : function () {
+        return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Ray = Class.create();
+
+Flog.RayTracer.Ray.prototype = {
+    position : null,
+    direction : null,
+    initialize : function(pos, dir) {
+        this.position = pos;
+        this.direction = dir;
+    },
+
+    toString : function () {
+        return 'Ray [' + this.position + ',' + this.direction + ']';
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Scene = Class.create();
+
+Flog.RayTracer.Scene.prototype = {
+    camera : null,
+    shapes : [],
+    lights : [],
+    background : null,
+
+    initialize : function() {
+        this.camera = new Flog.RayTracer.Camera(
+            new Flog.RayTracer.Vector(0,0,-5),
+            new Flog.RayTracer.Vector(0,0,1),
+            new Flog.RayTracer.Vector(0,1,0)
+        );
+        this.shapes = new Array();
+        this.lights = new Array();
+        this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
+
+Flog.RayTracer.Material.BaseMaterial = Class.create();
+
+Flog.RayTracer.Material.BaseMaterial.prototype = {
+
+    gloss: 2.0,             // [0...infinity] 0 = matt
+    transparency: 0.0,      // 0=opaque
+    reflection: 0.0,        // [0...infinity] 0 = no reflection
+    refraction: 0.50,
+    hasTexture: false,
+
+    initialize : function() {
+
+    },
+
+    getColor: function(u, v){
+
+    },
+
+    wrapUp: function(t){
+        t = t % 2.0;
+        if(t < -1) t += 2.0;
+        if(t >= 1) t -= 2.0;
+        return t;
+    },
+
+    toString : function () {
+        return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Material.Solid = Class.create();
+
+Flog.RayTracer.Material.Solid.prototype = Object.extend(
+    new Flog.RayTracer.Material.BaseMaterial(), {
+        initialize : function(color, reflection, refraction, transparency, gloss) {
+            this.color = color;
+            this.reflection = reflection;
+            this.transparency = transparency;
+            this.gloss = gloss;
+            this.hasTexture = false;
+        },
+
+        getColor: function(u, v){
+            return this.color;
+        },
+
+        toString : function () {
+            return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+        }
+    }
+);
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Material.Chessboard = Class.create();
+
+Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
+    new Flog.RayTracer.Material.BaseMaterial(), {
+        colorEven: null,
+        colorOdd: null,
+        density: 0.5,
+
+        initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
+            this.colorEven = colorEven;
+            this.colorOdd = colorOdd;
+            this.reflection = reflection;
+            this.transparency = transparency;
+            this.gloss = gloss;
+            this.density = density;
+            this.hasTexture = true;
+        },
+
+        getColor: function(u, v){
+            var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
+
+            if(t < 0.0)
+                return this.colorEven;
+            else
+                return this.colorOdd;
+        },
+
+        toString : function () {
+            return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+        }
+    }
+);
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+
+Flog.RayTracer.Shape.Sphere = Class.create();
+
+Flog.RayTracer.Shape.Sphere.prototype = {
+    initialize : function(pos, radius, material) {
+        this.radius = radius;
+        this.position = pos;
+        this.material = material;
+    },
+
+    intersect: function(ray){
+        var info = new Flog.RayTracer.IntersectionInfo();
+        info.shape = this;
+
+        var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
+
+        var B = dst.dot(ray.direction);
+        var C = dst.dot(dst) - (this.radius * this.radius);
+        var D = (B * B) - C;
+
+        if(D > 0){ // intersection!
+            info.isHit = true;
+            info.distance = (-B) - Math.sqrt(D);
+            info.position = Flog.RayTracer.Vector.prototype.add(
+                                                ray.position,
+                                                Flog.RayTracer.Vector.prototype.multiplyScalar(
+                                                    ray.direction,
+                                                    info.distance
+                                                )
+                                            );
+            info.normal = Flog.RayTracer.Vector.prototype.subtract(
+                                            info.position,
+                                            this.position
+                                        ).normalize();
+
+            info.color = this.material.getColor(0,0);
+        } else {
+            info.isHit = false;
+        }
+        return info;
+    },
+
+    toString : function () {
+        return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+
+Flog.RayTracer.Shape.Plane = Class.create();
+
+Flog.RayTracer.Shape.Plane.prototype = {
+    d: 0.0,
+
+    initialize : function(pos, d, material) {
+        this.position = pos;
+        this.d = d;
+        this.material = material;
+    },
+
+    intersect: function(ray){
+        var info = new Flog.RayTracer.IntersectionInfo();
+
+        var Vd = this.position.dot(ray.direction);
+        if(Vd == 0) return info; // no intersection
+
+        var t = -(this.position.dot(ray.position) + this.d) / Vd;
+        if(t <= 0) return info;
+
+        info.shape = this;
+        info.isHit = true;
+        info.position = Flog.RayTracer.Vector.prototype.add(
+                                            ray.position,
+                                            Flog.RayTracer.Vector.prototype.multiplyScalar(
+                                                ray.direction,
+                                                t
+                                            )
+                                        );
+        info.normal = this.position;
+        info.distance = t;
+
+        if(this.material.hasTexture){
+            var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
+            var vV = vU.cross(this.position);
+            var u = info.position.dot(vU);
+            var v = info.position.dot(vV);
+            info.color = this.material.getColor(u,v);
+        } else {
+            info.color = this.material.getColor(0,0);
+        }
+
+        return info;
+    },
+
+    toString : function () {
+        return 'Plane [' + this.position + ', d=' + this.d + ']';
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.IntersectionInfo = Class.create();
+
+Flog.RayTracer.IntersectionInfo.prototype = {
+    isHit: false,
+    hitCount: 0,
+    shape: null,
+    position: null,
+    normal: null,
+    color: null,
+    distance: null,
+
+    initialize : function() {
+        this.color = new Flog.RayTracer.Color(0,0,0);
+    },
+
+    toString : function () {
+        return 'Intersection [' + this.position + ']';
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Camera = Class.create();
+
+Flog.RayTracer.Camera.prototype = {
+    position: null,
+    lookAt: null,
+    equator: null,
+    up: null,
+    screen: null,
+
+    initialize : function(pos, lookAt, up) {
+        this.position = pos;
+        this.lookAt = lookAt;
+        this.up = up;
+        this.equator = lookAt.normalize().cross(this.up);
+        this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
+    },
+
+    getRay: function(vx, vy){
+        var pos = Flog.RayTracer.Vector.prototype.subtract(
+            this.screen,
+            Flog.RayTracer.Vector.prototype.subtract(
+                Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
+                Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
+            )
+        );
+        pos.y = pos.y * -1;
+        var dir = Flog.RayTracer.Vector.prototype.subtract(
+            pos,
+            this.position
+        );
+
+        var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
+
+        return ray;
+    },
+
+    toString : function () {
+        return 'Ray []';
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Background = Class.create();
+
+Flog.RayTracer.Background.prototype = {
+    color : null,
+    ambience : 0.0,
+
+    initialize : function(color, ambience) {
+        this.color = color;
+        this.ambience = ambience;
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Engine = Class.create();
+
+Flog.RayTracer.Engine.prototype = {
+    canvas: null, /* 2d context we can render to */
+
+    initialize: function(options){
+        this.options = Object.extend({
+                canvasHeight: 100,
+                canvasWidth: 100,
+                pixelWidth: 2,
+                pixelHeight: 2,
+                renderDiffuse: false,
+                renderShadows: false,
+                renderHighlights: false,
+                renderReflections: false,
+                rayDepth: 2
+            }, options || {});
+
+        this.options.canvasHeight /= this.options.pixelHeight;
+        this.options.canvasWidth /= this.options.pixelWidth;
+
+        /* TODO: dynamically include other scripts */
+    },
+
+    setPixel: function(x, y, color){
+        var pxW, pxH;
+        pxW = this.options.pixelWidth;
+        pxH = this.options.pixelHeight;
+
+        if (this.canvas) {
+          this.canvas.fillStyle = color.toString();
+          this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
+        } else {
+          if (x ===  y) {
+            checkNumber += color.brightness();
+          }
+          // print(x * pxW, y * pxH, pxW, pxH);
+        }
+    },
+
+    renderScene: function(scene, canvas){
+        checkNumber = 0;
+        /* Get canvas */
+        if (canvas) {
+          this.canvas = canvas.getContext("2d");
+        } else {
+          this.canvas = null;
+        }
+
+        var canvasHeight = this.options.canvasHeight;
+        var canvasWidth = this.options.canvasWidth;
+
+        for(var y=0; y < canvasHeight; y++){
+            for(var x=0; x < canvasWidth; x++){
+                var yp = y * 1.0 / canvasHeight * 2 - 1;
+                       var xp = x * 1.0 / canvasWidth * 2 - 1;
+
+                       var ray = scene.camera.getRay(xp, yp);
+
+                       var color = this.getPixelColor(ray, scene);
+
+               this.setPixel(x, y, color);
+            }
+        }
+        if (checkNumber !== 2321) {
+          throw new Error("Scene rendered incorrectly");
+        }
+    },
+
+    getPixelColor: function(ray, scene){
+        var info = this.testIntersection(ray, scene, null);
+        if(info.isHit){
+            var color = this.rayTrace(info, ray, scene, 0);
+            return color;
+        }
+        return scene.background.color;
+    },
+
+    testIntersection: function(ray, scene, exclude){
+        var hits = 0;
+        var best = new Flog.RayTracer.IntersectionInfo();
+        best.distance = 2000;
+
+        for(var i=0; i<scene.shapes.length; i++){
+            var shape = scene.shapes[i];
+
+            if(shape != exclude){
+                var info = shape.intersect(ray);
+                if(info.isHit && info.distance >= 0 && info.distance < best.distance){
+                    best = info;
+                    hits++;
+                }
+            }
+        }
+        best.hitCount = hits;
+        return best;
+    },
+
+    getReflectionRay: function(P,N,V){
+        var c1 = -N.dot(V);
+        var R1 = Flog.RayTracer.Vector.prototype.add(
+            Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
+            V
+        );
+        return new Flog.RayTracer.Ray(P, R1);
+    },
+
+    rayTrace: function(info, ray, scene, depth){
+        // Calc ambient
+        var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
+        var oldColor = color;
+        var shininess = Math.pow(10, info.shape.material.gloss + 1);
+
+        for(var i=0; i<scene.lights.length; i++){
+            var light = scene.lights[i];
+
+            // Calc diffuse lighting
+            var v = Flog.RayTracer.Vector.prototype.subtract(
+                                light.position,
+                                info.position
+                            ).normalize();
+
+            if(this.options.renderDiffuse){
+                var L = v.dot(info.normal);
+                if(L > 0.0){
+                    color = Flog.RayTracer.Color.prototype.add(
+                                        color,
+                                        Flog.RayTracer.Color.prototype.multiply(
+                                            info.color,
+                                            Flog.RayTracer.Color.prototype.multiplyScalar(
+                                                light.color,
+                                                L
+                                            )
+                                        )
+                                    );
+                }
+            }
+
+            // The greater the depth the more accurate the colours, but
+            // this is exponentially (!) expensive
+            if(depth <= this.options.rayDepth){
+          // calculate reflection ray
+          if(this.options.renderReflections && info.shape.material.reflection > 0)
+          {
+              var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
+              var refl = this.testIntersection(reflectionRay, scene, info.shape);
+
+              if (refl.isHit && refl.distance > 0){
+                  refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
+              } else {
+                  refl.color = scene.background.color;
+                        }
+
+                  color = Flog.RayTracer.Color.prototype.blend(
+                    color,
+                    refl.color,
+                    info.shape.material.reflection
+                  );
+          }
+
+                // Refraction
+                /* TODO */
+            }
+
+            /* Render shadows and highlights */
+
+            var shadowInfo = new Flog.RayTracer.IntersectionInfo();
+
+            if(this.options.renderShadows){
+                var shadowRay = new Flog.RayTracer.Ray(info.position, v);
+
+                shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
+                if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
+                    var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
+                    var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
+                    color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
+                }
+            }
+
+      // Phong specular highlights
+      if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
+        var Lv = Flog.RayTracer.Vector.prototype.subtract(
+                            info.shape.position,
+                            light.position
+                        ).normalize();
+
+        var E = Flog.RayTracer.Vector.prototype.subtract(
+                            scene.camera.position,
+                            info.shape.position
+                        ).normalize();
+
+        var H = Flog.RayTracer.Vector.prototype.subtract(
+                            E,
+                            Lv
+                        ).normalize();
+
+        var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
+        color = Flog.RayTracer.Color.prototype.add(
+                            Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
+                            color
+                        );
+      }
+        }
+        color.limit();
+        return color;
+    }
+};
+
+
+function renderScene(){
+    var scene = new Flog.RayTracer.Scene();
+
+    scene.camera = new Flog.RayTracer.Camera(
+                        new Flog.RayTracer.Vector(0, 0, -15),
+                        new Flog.RayTracer.Vector(-0.2, 0, 5),
+                        new Flog.RayTracer.Vector(0, 1, 0)
+                    );
+
+    scene.background = new Flog.RayTracer.Background(
+                                new Flog.RayTracer.Color(0.5, 0.5, 0.5),
+                                0.4
+                            );
+
+    var sphere = new Flog.RayTracer.Shape.Sphere(
+        new Flog.RayTracer.Vector(-1.5, 1.5, 2),
+        1.5,
+        new Flog.RayTracer.Material.Solid(
+            new Flog.RayTracer.Color(0,0.5,0.5),
+            0.3,
+            0.0,
+            0.0,
+            2.0
+        )
+    );
+
+    var sphere1 = new Flog.RayTracer.Shape.Sphere(
+        new Flog.RayTracer.Vector(1, 0.25, 1),
+        0.5,
+        new Flog.RayTracer.Material.Solid(
+            new Flog.RayTracer.Color(0.9,0.9,0.9),
+            0.1,
+            0.0,
+            0.0,
+            1.5
+        )
+    );
+
+    var plane = new Flog.RayTracer.Shape.Plane(
+                                new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
+                                1.2,
+                                new Flog.RayTracer.Material.Chessboard(
+                                    new Flog.RayTracer.Color(1,1,1),
+                                    new Flog.RayTracer.Color(0,0,0),
+                                    0.2,
+                                    0.0,
+                                    1.0,
+                                    0.7
+                                )
+                            );
+
+    scene.shapes.push(plane);
+    scene.shapes.push(sphere);
+    scene.shapes.push(sphere1);
+
+    var light = new Flog.RayTracer.Light(
+        new Flog.RayTracer.Vector(5, 10, -1),
+        new Flog.RayTracer.Color(0.8, 0.8, 0.8)
+    );
+
+    var light1 = new Flog.RayTracer.Light(
+        new Flog.RayTracer.Vector(-3, 5, -15),
+        new Flog.RayTracer.Color(0.8, 0.8, 0.8),
+        100
+    );
+
+    scene.lights.push(light);
+    scene.lights.push(light1);
+
+    var imageWidth = 100; // $F('imageWidth');
+    var imageHeight = 100; // $F('imageHeight');
+    var pixelSize = "5,5".split(','); //  $F('pixelSize').split(',');
+    var renderDiffuse = true; // $F('renderDiffuse');
+    var renderShadows = true; // $F('renderShadows');
+    var renderHighlights = true; // $F('renderHighlights');
+    var renderReflections = true; // $F('renderReflections');
+    var rayDepth = 2;//$F('rayDepth');
+
+    var raytracer = new Flog.RayTracer.Engine(
+        {
+            canvasWidth: imageWidth,
+            canvasHeight: imageHeight,
+            pixelWidth: pixelSize[0],
+            pixelHeight: pixelSize[1],
+            "renderDiffuse": renderDiffuse,
+            "renderHighlights": renderHighlights,
+            "renderShadows": renderShadows,
+            "renderReflections": renderReflections,
+            "rayDepth": rayDepth
+        }
+    );
+
+    raytracer.renderScene(scene, null, 0);
+}
+
+for (var i = 0; i < 6; ++i)
+  renderScene();
index 555f805..53a4cc4 100644 (file)
@@ -1,3 +1,18 @@
+2015-03-25  Filip Pizlo  <fpizlo@apple.com>
+
+        Use JITCompilationCanFail in more places, and make the fail path of JITCompilationMustSucceed a crash instead of attempting GC
+        https://bugs.webkit.org/show_bug.cgi?id=142993
+
+        Reviewed by Mark Lam.
+        
+        Bunch of support for testing executable allocation failure.
+
+        * Scripts/jsc-stress-test-helpers/js-executable-allocation-fuzz: Added.
+        (fail):
+        * Scripts/run-javascriptcore-tests:
+        (runJSCStressTests):
+        * Scripts/run-jsc-stress-tests:
+
 2015-03-25  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Fix Windows build from r181977.
diff --git a/Tools/Scripts/jsc-stress-test-helpers/js-executable-allocation-fuzz b/Tools/Scripts/jsc-stress-test-helpers/js-executable-allocation-fuzz
new file mode 100755 (executable)
index 0000000..7721a9f
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer. 
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution. 
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use strict;
+use FindBin;
+use Getopt::Long qw(:config pass_through);
+use POSIX;
+
+# We first want to run the test once to determine what the number of encountered
+# checks is. Then we want to run it again some number of times with random check
+# amounts. The test is successful if it doesn't crash.
+
+my $repeat = 20;
+my $seed = time();
+my $verbose = 0;
+
+# We allow flags to be passed via environment variables, which is rather useful for
+# running with the run-jsc-stress-tests harness.
+if (defined($ENV{JS_EAFUZZ_REPEAT})) {
+    $repeat = $ENV{JS_EAFUZZ_REPEAT};
+}
+if (defined($ENV{JS_EAFUZZ_SEED})) {
+    $seed = $ENV{JS_EAFUZZ_SEED};
+}
+if (defined($ENV{JS_EAFUZZ_VERBOSE})) {
+    $verbose = $ENV{JS_EAFUZZ_VERBOSE};
+}
+
+GetOptions(
+    'repeat=s' => \$repeat,
+    'seed=s' => \$seed,
+    'verbose' => \$verbose
+);
+
+my $commandString = shift @ARGV;
+
+my $checkCount;
+
+sub fail {
+    my $context = shift;
+    select((select(STDOUT), $ |= 1)[0]); # This is a perlism for flush. We need to do it this way to support older perls.
+    select((select(STDERR), $ |= 1)[0]);
+    die "Failure for command $commandString with seed $seed, repeat $repeat: $context";
+}
+
+if (shift @ARGV) {
+    die "Ignoring garbage arguments; only the first non-option argument is used as the command string.";
+}
+
+open (my $testInput, "$commandString --enableExecutableAllocationFuzz=true |") or fail("Cannot execute initial command when getting check count");
+while (my $inputLine = <$testInput>) {
+    chomp($inputLine);
+    my $handled = 0;
+    if ($inputLine =~ /JSC EXECUTABLE ALLOCATION FUZZ: encountered ([0-9]+) checks\./) {
+        $checkCount = $1;
+        $handled = 1;
+    }
+    if (!$handled || $verbose) {
+        print "checkCount: $inputLine\n";
+    }
+}
+close($testInput);
+
+if ($verbose) {
+    print "Check count: $checkCount\n";
+    print "Seed: $seed\n";
+}
+
+if (!$checkCount) {
+    print "Executable allocation fuzz testing not supported by jsc binary.\n";
+    exit 0;
+}
+
+# First do some tests where we have one-off failures.
+srand($seed);
+
+for (my $iteration = 0; $iteration < $repeat; ++$iteration) {
+    my $target = int(rand() * $checkCount) + 1;
+    if ($verbose) {
+        print "iteration($iteration) target($target) one-shot: Running.\n";
+    }
+    my $result = system("$commandString --enableExecutableAllocationFuzz=true --fireExecutableAllocationFuzzAt=$target");
+    if ($result != 0) {
+        fail("Cannot execute command on iteration $iteration, status $? for target $target");
+    }
+}
+
+# Then do some tests where we start failing at a particular point, and then permafail.
+srand($seed);
+
+for (my $iteration = 0; $iteration < $repeat; ++$iteration) {
+    my $target = int(rand() * $checkCount) + 1;
+    if ($verbose) {
+        print "iteration($iteration) target($target) at-or-after: Running.\n";
+    }
+    my $result = system("$commandString --enableExecutableAllocationFuzz=true --fireExecutableAllocationFuzzAtOrAfter=$target");
+    if ($result != 0) {
+        fail("Cannot execute command on iteration $iteration, status $? for target $target");
+    }
+}
+
+if ($verbose) {
+    print "Success!\n";
+}
index 5af9be1..8756276 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 
-# Copyright (C) 2005, 2013-2014 Apple Inc.  All rights reserved.
+# Copyright (C) 2005, 2013-2015 Apple Inc.  All rights reserved.
 # Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 #
 # Redistribution and use in source and binary forms, with or without
@@ -269,6 +269,7 @@ sub runJSCStressTests
         "/usr/bin/env", "ruby", "Tools/Scripts/run-jsc-stress-tests",
         "-j", jscPath($productDir), "-o", $jscStressResultsDir,
         "PerformanceTests/SunSpider/tests/sunspider-1.0",
+        "Source/JavaScriptCore/tests/executableAllocationFuzz.yaml",
         "Source/JavaScriptCore/tests/exceptionFuzz.yaml",
         "PerformanceTests/SunSpider/no-architecture-specific-optimizations.yaml",
         "PerformanceTests/SunSpider/tests/v8-v6",
index f3a97d4..dde8083 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 
-# Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+# Copyright (C) 2013-2015 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -781,6 +781,11 @@ def runExceptionFuzz
     addRunCommand("exception-fuzz", ["perl", (pathToHelpers + "js-exception-fuzz").to_s, subCommand], silentOutputHandler, simpleErrorHandler)
 end
 
+def runExecutableAllocationFuzz(name, *options)
+    subCommand = escapeAll([pathToVM.to_s, $benchmark.to_s] + options)
+    addRunCommand("executable-allocation-fuzz-" + name, ["perl", (pathToHelpers + "js-executable-allocation-fuzz").to_s, subCommand], silentOutputHandler, simpleErrorHandler)
+end
+
 def runTypeProfiler
     if $enableFTL
         run("ftl-no-cjit-type-profiler", "--enableTypeProfiler=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))