FTL B3 getById() should do exceptions
https://bugs.webkit.org/show_bug.cgi?id=152810
Reviewed by Saam Barati.
This adds abstractions for doing exceptions from patchpoints, and uses them to implement
exceptions from GetById. This covers all of the following ways that a GetById might throw an
exceptions:
- Throw without try/catch from the vmCall() in a GetById(Untyped:)
- Throw with try/catch from the vmCall() in a GetById(Untyped:)
- Throw without try/catch from the callOperation() in the patchpoint of a GetById
- Throw with try/catch from the callOperation() in the patchpoint of a GetById
- Throw without try/catch from the Call IC generated in the patchpoint of a GetById
- Throw with try/catch from the Call IC generated in the patchpoint of a GetById
This requires having a default exception target in FTL-generated code, and ensuring that this
target is generated regardless of whether we have branches to the B3 basic block of the
default exception target. This also requires adding some extra arguments to a
PatchpointValue, and then knowing that the arguments are used for OSR exit and not anything
else. This also requires associating the CallSiteIndex of the patchpoint with the register
set used for exit and with the OSR exit label for the unwind exit.
All of the stuff that you have to worry about when wiring a patchpoint to exception handling
is covered by the new PatchpointExceptionHandle object. You create one by calling
preparePatchpointForExceptions(). This sets up the B3 IR representation of the patchpoint
with stackmap arguments for the exceptional exit, and creates a PatchpointExceptionHandle
object that can be used to create zero or more actual OSR exits. It can create both OSR exits
for operation calls and OSR exits for unwind. You call the
PatchpointExceptionHandle::scheduleExitCreationXXX() methods from the generator callback to
actually get OSR exits.
This API makes heavy use of Box<>, late paths, and link tasks. For example, you can use the
PatchpointExceptionHandle to get a Box<JumpList> that you can append exception jumps to. When
you use this API, it automatically registers a link task that will link the JumpList to the
actual OSR exit label.
This API is very flexible about how you get to the label of the OSR exit. You are encouraged
to use the Box<JumpList> approach, but if you really just need the label, you can also get
a RefPtr<ExceptionTarget> and rely on the fact that the ExceptionTarget object will be able
to vend you the OSR exit label at link-time.
This reduces the number of JSC test failures with FTL B3 from 186 to 133. It also adds a
bunch of new tests specifically for all of the ways you might throw from GetById, and B3
passes all of these new tests. Note that I'm not counting the new tests as part of the
previous 186 test failures (FTL B3 failed all of the new tests prior to this change).
After this change, it should be easy to make all of the other patchpoints also handle
exceptions by just following the preparePatchpointForExceptions() idiom.
* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* b3/B3StackmapValue.h:
* b3/B3ValueRep.cpp:
(JSC::B3::ValueRep::addUsedRegistersTo):
(JSC::B3::ValueRep::usedRegisters):
(JSC::B3::ValueRep::dump):
* b3/B3ValueRep.h:
(JSC::B3::ValueRep::doubleValue):
(JSC::B3::ValueRep::withOffset):
(JSC::B3::ValueRep::usedRegisters):
* ftl/FTLB3Compile.cpp:
(JSC::FTL::compile):
* ftl/FTLB3Output.h:
(JSC::FTL::Output::unreachable):
(JSC::FTL::Output::speculate):
* ftl/FTLExceptionTarget.cpp: Added.
(JSC::FTL::ExceptionTarget::~ExceptionTarget):
(JSC::FTL::ExceptionTarget::label):
(JSC::FTL::ExceptionTarget::jumps):
(JSC::FTL::ExceptionTarget::ExceptionTarget):
* ftl/FTLExceptionTarget.h: Added.
* ftl/FTLJITCode.cpp:
(JSC::FTL::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::lower):
(JSC::FTL::DFG::LowerDFGToLLVM::compileArithAddOrSub):
(JSC::FTL::DFG::LowerDFGToLLVM::compileArithMul):
(JSC::FTL::DFG::LowerDFGToLLVM::compilePutById):
(JSC::FTL::DFG::LowerDFGToLLVM::compileMakeRope):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCallOrConstructVarargs):
(JSC::FTL::DFG::LowerDFGToLLVM::compileIn):
(JSC::FTL::DFG::LowerDFGToLLVM::getById):
(JSC::FTL::DFG::LowerDFGToLLVM::emitBinarySnippet):
(JSC::FTL::DFG::LowerDFGToLLVM::emitBinaryBitOpSnippet):
(JSC::FTL::DFG::LowerDFGToLLVM::emitRightShiftSnippet):
(JSC::FTL::DFG::LowerDFGToLLVM::callCheck):
(JSC::FTL::DFG::LowerDFGToLLVM::preparePatchpointForExceptions):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitArgumentsForPatchpointIfWillCatchException):
(JSC::FTL::DFG::LowerDFGToLLVM::lowBlock):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
(JSC::FTL::DFG::LowerDFGToLLVM::blessSpeculation):
* ftl/FTLPatchpointExceptionHandle.cpp: Added.
(JSC::FTL::PatchpointExceptionHandle::create):
(JSC::FTL::PatchpointExceptionHandle::defaultHandle):
(JSC::FTL::PatchpointExceptionHandle::~PatchpointExceptionHandle):
(JSC::FTL::PatchpointExceptionHandle::scheduleExitCreation):
(JSC::FTL::PatchpointExceptionHandle::scheduleExitCreationForUnwind):
(JSC::FTL::PatchpointExceptionHandle::PatchpointExceptionHandle):
(JSC::FTL::PatchpointExceptionHandle::createHandle):
* ftl/FTLPatchpointExceptionHandle.h: Added.
* ftl/FTLState.cpp:
* ftl/FTLState.h:
(JSC::FTL::verboseCompilationEnabled):
* tests/stress/ftl-get-by-id-getter-exception-interesting-live-state.js: Added.
* tests/stress/ftl-get-by-id-getter-exception-no-catch.js: Added.
* tests/stress/ftl-get-by-id-getter-exception.js: Added.
* tests/stress/ftl-get-by-id-slow-exception-interesting-live-state.js: Added.
* tests/stress/ftl-get-by-id-slow-exception-no-catch.js: Added.
* tests/stress/ftl-get-by-id-slow-exception.js: Added.
* tests/stress/ftl-operation-exception-interesting-live-state.js: Added.
* tests/stress/ftl-operation-exception-no-catch.js: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@194716 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 67d3383..de41a39 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -1023,6 +1023,7 @@
ftl/FTLDWARFRegister.cpp
ftl/FTLDataSection.cpp
ftl/FTLExceptionHandlerManager.cpp
+ ftl/FTLExceptionTarget.cpp
ftl/FTLExitArgument.cpp
ftl/FTLExitArgumentForOperand.cpp
ftl/FTLExitPropertyValue.cpp
@@ -1049,6 +1050,7 @@
ftl/FTLOSRExitHandle.cpp
ftl/FTLOperations.cpp
ftl/FTLOutput.cpp
+ ftl/FTLPatchpointExceptionHandle.cpp
ftl/FTLRecoveryOpcode.cpp
ftl/FTLSaveRestore.cpp
ftl/FTLSlowPathCall.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index ca15387..d1c51b3 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,118 @@
+2016-01-07 Filip Pizlo <fpizlo@apple.com>
+
+ FTL B3 getById() should do exceptions
+ https://bugs.webkit.org/show_bug.cgi?id=152810
+
+ Reviewed by Saam Barati.
+
+ This adds abstractions for doing exceptions from patchpoints, and uses them to implement
+ exceptions from GetById. This covers all of the following ways that a GetById might throw an
+ exceptions:
+
+ - Throw without try/catch from the vmCall() in a GetById(Untyped:)
+ - Throw with try/catch from the vmCall() in a GetById(Untyped:)
+ - Throw without try/catch from the callOperation() in the patchpoint of a GetById
+ - Throw with try/catch from the callOperation() in the patchpoint of a GetById
+ - Throw without try/catch from the Call IC generated in the patchpoint of a GetById
+ - Throw with try/catch from the Call IC generated in the patchpoint of a GetById
+
+ This requires having a default exception target in FTL-generated code, and ensuring that this
+ target is generated regardless of whether we have branches to the B3 basic block of the
+ default exception target. This also requires adding some extra arguments to a
+ PatchpointValue, and then knowing that the arguments are used for OSR exit and not anything
+ else. This also requires associating the CallSiteIndex of the patchpoint with the register
+ set used for exit and with the OSR exit label for the unwind exit.
+
+ All of the stuff that you have to worry about when wiring a patchpoint to exception handling
+ is covered by the new PatchpointExceptionHandle object. You create one by calling
+ preparePatchpointForExceptions(). This sets up the B3 IR representation of the patchpoint
+ with stackmap arguments for the exceptional exit, and creates a PatchpointExceptionHandle
+ object that can be used to create zero or more actual OSR exits. It can create both OSR exits
+ for operation calls and OSR exits for unwind. You call the
+ PatchpointExceptionHandle::scheduleExitCreationXXX() methods from the generator callback to
+ actually get OSR exits.
+
+ This API makes heavy use of Box<>, late paths, and link tasks. For example, you can use the
+ PatchpointExceptionHandle to get a Box<JumpList> that you can append exception jumps to. When
+ you use this API, it automatically registers a link task that will link the JumpList to the
+ actual OSR exit label.
+
+ This API is very flexible about how you get to the label of the OSR exit. You are encouraged
+ to use the Box<JumpList> approach, but if you really just need the label, you can also get
+ a RefPtr<ExceptionTarget> and rely on the fact that the ExceptionTarget object will be able
+ to vend you the OSR exit label at link-time.
+
+ This reduces the number of JSC test failures with FTL B3 from 186 to 133. It also adds a
+ bunch of new tests specifically for all of the ways you might throw from GetById, and B3
+ passes all of these new tests. Note that I'm not counting the new tests as part of the
+ previous 186 test failures (FTL B3 failed all of the new tests prior to this change).
+
+ After this change, it should be easy to make all of the other patchpoints also handle
+ exceptions by just following the preparePatchpointForExceptions() idiom.
+
+ * CMakeLists.txt:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * b3/B3StackmapValue.h:
+ * b3/B3ValueRep.cpp:
+ (JSC::B3::ValueRep::addUsedRegistersTo):
+ (JSC::B3::ValueRep::usedRegisters):
+ (JSC::B3::ValueRep::dump):
+ * b3/B3ValueRep.h:
+ (JSC::B3::ValueRep::doubleValue):
+ (JSC::B3::ValueRep::withOffset):
+ (JSC::B3::ValueRep::usedRegisters):
+ * ftl/FTLB3Compile.cpp:
+ (JSC::FTL::compile):
+ * ftl/FTLB3Output.h:
+ (JSC::FTL::Output::unreachable):
+ (JSC::FTL::Output::speculate):
+ * ftl/FTLExceptionTarget.cpp: Added.
+ (JSC::FTL::ExceptionTarget::~ExceptionTarget):
+ (JSC::FTL::ExceptionTarget::label):
+ (JSC::FTL::ExceptionTarget::jumps):
+ (JSC::FTL::ExceptionTarget::ExceptionTarget):
+ * ftl/FTLExceptionTarget.h: Added.
+ * ftl/FTLJITCode.cpp:
+ (JSC::FTL::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::DFG::LowerDFGToLLVM::lower):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileArithAddOrSub):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileArithMul):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compilePutById):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileMakeRope):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileCallOrConstructVarargs):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileIn):
+ (JSC::FTL::DFG::LowerDFGToLLVM::getById):
+ (JSC::FTL::DFG::LowerDFGToLLVM::emitBinarySnippet):
+ (JSC::FTL::DFG::LowerDFGToLLVM::emitBinaryBitOpSnippet):
+ (JSC::FTL::DFG::LowerDFGToLLVM::emitRightShiftSnippet):
+ (JSC::FTL::DFG::LowerDFGToLLVM::callCheck):
+ (JSC::FTL::DFG::LowerDFGToLLVM::preparePatchpointForExceptions):
+ (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitArgumentsForPatchpointIfWillCatchException):
+ (JSC::FTL::DFG::LowerDFGToLLVM::lowBlock):
+ (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
+ (JSC::FTL::DFG::LowerDFGToLLVM::blessSpeculation):
+ * ftl/FTLPatchpointExceptionHandle.cpp: Added.
+ (JSC::FTL::PatchpointExceptionHandle::create):
+ (JSC::FTL::PatchpointExceptionHandle::defaultHandle):
+ (JSC::FTL::PatchpointExceptionHandle::~PatchpointExceptionHandle):
+ (JSC::FTL::PatchpointExceptionHandle::scheduleExitCreation):
+ (JSC::FTL::PatchpointExceptionHandle::scheduleExitCreationForUnwind):
+ (JSC::FTL::PatchpointExceptionHandle::PatchpointExceptionHandle):
+ (JSC::FTL::PatchpointExceptionHandle::createHandle):
+ * ftl/FTLPatchpointExceptionHandle.h: Added.
+ * ftl/FTLState.cpp:
+ * ftl/FTLState.h:
+ (JSC::FTL::verboseCompilationEnabled):
+ * tests/stress/ftl-get-by-id-getter-exception-interesting-live-state.js: Added.
+ * tests/stress/ftl-get-by-id-getter-exception-no-catch.js: Added.
+ * tests/stress/ftl-get-by-id-getter-exception.js: Added.
+ * tests/stress/ftl-get-by-id-slow-exception-interesting-live-state.js: Added.
+ * tests/stress/ftl-get-by-id-slow-exception-no-catch.js: Added.
+ * tests/stress/ftl-get-by-id-slow-exception.js: Added.
+ * tests/stress/ftl-operation-exception-interesting-live-state.js: Added.
+ * tests/stress/ftl-operation-exception-no-catch.js: Added.
+
2016-01-07 Konstantin Tokarev <annulen@yandex.ru>
[mips] Implemented missing branch patching methods.
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index dc65274..1eee988 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -518,6 +518,10 @@
0F9D339B1803ADB70073C2BC /* FTLStackMaps.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9D33991803ADB70073C2BC /* FTLStackMaps.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F9D36941AE9CC33000D4DFB /* DFGCleanUpPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9D36921AE9CC33000D4DFB /* DFGCleanUpPhase.cpp */; };
0F9D36951AE9CC33000D4DFB /* DFGCleanUpPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9D36931AE9CC33000D4DFB /* DFGCleanUpPhase.h */; };
+ 0F9D4C0C1C3E1C11006CD984 /* FTLExceptionTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9D4C0A1C3E1C11006CD984 /* FTLExceptionTarget.cpp */; };
+ 0F9D4C0D1C3E1C11006CD984 /* FTLExceptionTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9D4C0B1C3E1C11006CD984 /* FTLExceptionTarget.h */; };
+ 0F9D4C101C3E2C74006CD984 /* FTLPatchpointExceptionHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9D4C0E1C3E2C74006CD984 /* FTLPatchpointExceptionHandle.cpp */; };
+ 0F9D4C111C3E2C74006CD984 /* FTLPatchpointExceptionHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9D4C0F1C3E2C74006CD984 /* FTLPatchpointExceptionHandle.h */; };
0F9E32631B05AB0400801ED5 /* DFGStoreBarrierInsertionPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9E32611B05AB0400801ED5 /* DFGStoreBarrierInsertionPhase.cpp */; };
0F9E32641B05AB0400801ED5 /* DFGStoreBarrierInsertionPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9E32621B05AB0400801ED5 /* DFGStoreBarrierInsertionPhase.h */; };
0F9FB4F417FCB91700CB67F8 /* DFGStackLayoutPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9FB4F217FCB91700CB67F8 /* DFGStackLayoutPhase.cpp */; };
@@ -2650,6 +2654,10 @@
0F9D33991803ADB70073C2BC /* FTLStackMaps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLStackMaps.h; path = ftl/FTLStackMaps.h; sourceTree = "<group>"; };
0F9D36921AE9CC33000D4DFB /* DFGCleanUpPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGCleanUpPhase.cpp; path = dfg/DFGCleanUpPhase.cpp; sourceTree = "<group>"; };
0F9D36931AE9CC33000D4DFB /* DFGCleanUpPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCleanUpPhase.h; path = dfg/DFGCleanUpPhase.h; sourceTree = "<group>"; };
+ 0F9D4C0A1C3E1C11006CD984 /* FTLExceptionTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLExceptionTarget.cpp; path = ftl/FTLExceptionTarget.cpp; sourceTree = "<group>"; };
+ 0F9D4C0B1C3E1C11006CD984 /* FTLExceptionTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLExceptionTarget.h; path = ftl/FTLExceptionTarget.h; sourceTree = "<group>"; };
+ 0F9D4C0E1C3E2C74006CD984 /* FTLPatchpointExceptionHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLPatchpointExceptionHandle.cpp; path = ftl/FTLPatchpointExceptionHandle.cpp; sourceTree = "<group>"; };
+ 0F9D4C0F1C3E2C74006CD984 /* FTLPatchpointExceptionHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLPatchpointExceptionHandle.h; path = ftl/FTLPatchpointExceptionHandle.h; sourceTree = "<group>"; };
0F9E32611B05AB0400801ED5 /* DFGStoreBarrierInsertionPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGStoreBarrierInsertionPhase.cpp; path = dfg/DFGStoreBarrierInsertionPhase.cpp; sourceTree = "<group>"; };
0F9E32621B05AB0400801ED5 /* DFGStoreBarrierInsertionPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGStoreBarrierInsertionPhase.h; path = dfg/DFGStoreBarrierInsertionPhase.h; sourceTree = "<group>"; };
0F9FB4F217FCB91700CB67F8 /* DFGStackLayoutPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGStackLayoutPhase.cpp; path = dfg/DFGStackLayoutPhase.cpp; sourceTree = "<group>"; };
@@ -4544,6 +4552,8 @@
0F9C5E5D18E35F5E00D431C3 /* FTLDWARFRegister.h */,
79DF66AE1BF26A570001CF11 /* FTLExceptionHandlerManager.cpp */,
79DF66AF1BF26A570001CF11 /* FTLExceptionHandlerManager.h */,
+ 0F9D4C0A1C3E1C11006CD984 /* FTLExceptionTarget.cpp */,
+ 0F9D4C0B1C3E1C11006CD984 /* FTLExceptionTarget.h */,
0F235BBD17178E1C00690C7F /* FTLExitArgument.cpp */,
0F235BBE17178E1C00690C7F /* FTLExitArgument.h */,
0F235BBF17178E1C00690C7F /* FTLExitArgumentForOperand.cpp */,
@@ -4602,6 +4612,8 @@
0F9B1DB61C0E42BD00E5BFD2 /* FTLOSRExitHandle.h */,
0FEA0A291709629600BB722C /* FTLOutput.cpp */,
0FEA0A06170513DB00BB722C /* FTLOutput.h */,
+ 0F9D4C0E1C3E2C74006CD984 /* FTLPatchpointExceptionHandle.cpp */,
+ 0F9D4C0F1C3E2C74006CD984 /* FTLPatchpointExceptionHandle.h */,
0F485325187DFDEC0083B687 /* FTLRecoveryOpcode.cpp */,
0F485326187DFDEC0083B687 /* FTLRecoveryOpcode.h */,
0FCEFAA91804C13E00472CE4 /* FTLSaveRestore.cpp */,
@@ -7127,6 +7139,7 @@
0F7B294D14C3CD4C007C3DB1 /* DFGCommon.h in Headers */,
0FEA0A32170D40BF00BB722C /* DFGCommonData.h in Headers */,
0F38B01817CFE75500B144D3 /* DFGCompilationKey.h in Headers */,
+ 0F9D4C111C3E2C74006CD984 /* FTLPatchpointExceptionHandle.h in Headers */,
0F38B01A17CFE75500B144D3 /* DFGCompilationMode.h in Headers */,
0F3B3A1B153E68F4003ED0FF /* DFGConstantFoldingPhase.h in Headers */,
0FED67BA1B26256D0066CE15 /* DFGConstantHoistingPhase.h in Headers */,
@@ -7827,6 +7840,7 @@
E33637A61B63220200EE0840 /* ReflectObject.h in Headers */,
996B73231BDA08EF00331B84 /* ReflectObject.lut.h in Headers */,
0FA7A8EC18B413C80052371D /* Reg.h in Headers */,
+ 0F9D4C0D1C3E1C11006CD984 /* FTLExceptionTarget.h in Headers */,
BC18C45A0E16F5CD00B34460 /* RegExp.h in Headers */,
A1712B3F11C7B228007A5315 /* RegExpCache.h in Headers */,
BCD202C20E1706A7002C7E82 /* RegExpConstructor.h in Headers */,
@@ -9072,6 +9086,7 @@
1428083A107EC0750013E7B2 /* JSStack.cpp in Sources */,
147F39D5107EC37600427A48 /* JSString.cpp in Sources */,
70EC0EC21AA0D7DA00B6AAFA /* JSStringIterator.cpp in Sources */,
+ 0F9D4C101C3E2C74006CD984 /* FTLPatchpointExceptionHandle.cpp in Sources */,
2600B5A6152BAAA70091EE5F /* JSStringJoiner.cpp in Sources */,
1482B74E0A43032800517CFC /* JSStringRef.cpp in Sources */,
146AAB380B66A94400E55F16 /* JSStringRefCF.cpp in Sources */,
@@ -9176,6 +9191,7 @@
0F190CAC189D82F6000AE5F0 /* ProfilerJettisonReason.cpp in Sources */,
0FF729B3166AD35C000F5BA3 /* ProfilerOrigin.cpp in Sources */,
0FF729B4166AD35C000F5BA3 /* ProfilerOriginStack.cpp in Sources */,
+ 0F9D4C0C1C3E1C11006CD984 /* FTLExceptionTarget.cpp in Sources */,
0FB1058B1675483100F8AB6E /* ProfilerOSRExit.cpp in Sources */,
0FB1058D1675483700F8AB6E /* ProfilerOSRExitSite.cpp in Sources */,
0F13912B16771C3A009CCB07 /* ProfilerProfiledBytecodes.cpp in Sources */,
diff --git a/Source/JavaScriptCore/b3/B3StackmapValue.h b/Source/JavaScriptCore/b3/B3StackmapValue.h
index 009fdb4..ee5999e 100644
--- a/Source/JavaScriptCore/b3/B3StackmapValue.h
+++ b/Source/JavaScriptCore/b3/B3StackmapValue.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -77,12 +77,19 @@
append(value);
}
+ // Helper for appending a bunch of values with some ValueRep.
+ template<typename VectorType>
+ void appendVectorWithRep(const VectorType& vector, const ValueRep& rep)
+ {
+ for (Value* value : vector)
+ append(value, rep);
+ }
+
// Helper for appending cold any's. This often used by clients to implement OSR.
template<typename VectorType>
void appendColdAnys(const VectorType& vector)
{
- for (Value* value : vector)
- append(ConstrainedValue(value, ValueRep::ColdAny));
+ appendVectorWithRep(vector, ValueRep::ColdAny);
}
// This is a helper for something you might do a lot of: append a value that should be constrained
diff --git a/Source/JavaScriptCore/b3/B3ValueRep.cpp b/Source/JavaScriptCore/b3/B3ValueRep.cpp
index 6fbdc4a..c55b3ba 100644
--- a/Source/JavaScriptCore/b3/B3ValueRep.cpp
+++ b/Source/JavaScriptCore/b3/B3ValueRep.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -33,6 +33,34 @@
namespace JSC { namespace B3 {
+void ValueRep::addUsedRegistersTo(RegisterSet& set) const
+{
+ switch (m_kind) {
+ case WarmAny:
+ case ColdAny:
+ case LateColdAny:
+ case SomeRegister:
+ case Constant:
+ return;
+ case Register:
+ set.set(reg());
+ return;
+ case Stack:
+ case StackArgument:
+ set.set(MacroAssembler::stackPointerRegister);
+ set.set(GPRInfo::callFrameRegister);
+ return;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+RegisterSet ValueRep::usedRegisters() const
+{
+ RegisterSet result;
+ addUsedRegistersTo(result);
+ return result;
+}
+
void ValueRep::dump(PrintStream& out) const
{
out.print(m_kind);
diff --git a/Source/JavaScriptCore/b3/B3ValueRep.h b/Source/JavaScriptCore/b3/B3ValueRep.h
index 0d02d40..4c4b499 100644
--- a/Source/JavaScriptCore/b3/B3ValueRep.h
+++ b/Source/JavaScriptCore/b3/B3ValueRep.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -32,6 +32,7 @@
#include "GPRInfo.h"
#include "JSCJSValue.h"
#include "Reg.h"
+#include "RegisterSet.h"
#include "ValueRecovery.h"
#include <wtf/PrintStream.h>
@@ -207,7 +208,7 @@
return bitwise_cast<double>(value());
}
- ValueRep withOffset(intptr_t offset)
+ ValueRep withOffset(intptr_t offset) const
{
switch (kind()) {
case Stack:
@@ -219,6 +220,20 @@
}
}
+ void addUsedRegistersTo(RegisterSet&) const;
+
+ RegisterSet usedRegisters() const;
+
+ // Get the used registers for a vector of ValueReps.
+ template<typename VectorType>
+ static RegisterSet usedRegisters(const VectorType& vector)
+ {
+ RegisterSet result;
+ for (const ValueRep& value : vector)
+ value.addUsedRegistersTo(result);
+ return result;
+ }
+
JS_EXPORT_PRIVATE void dump(PrintStream&) const;
// This has a simple contract: it emits code to restore the value into the given register. This
diff --git a/Source/JavaScriptCore/ftl/FTLB3Compile.cpp b/Source/JavaScriptCore/ftl/FTLB3Compile.cpp
index db9d9a3..1b1678e 100644
--- a/Source/JavaScriptCore/ftl/FTLB3Compile.cpp
+++ b/Source/JavaScriptCore/ftl/FTLB3Compile.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -112,6 +112,18 @@
CCallHelpers jit(&vm, codeBlock);
B3::generate(*state.proc, jit);
+ // Emit the exception handler.
+ *state.exceptionHandler = jit.label();
+ jit.copyCalleeSavesToVMCalleeSavesBuffer();
+ jit.move(MacroAssembler::TrustedImmPtr(jit.vm()), GPRInfo::argumentGPR0);
+ jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
+ CCallHelpers::Call call = jit.call();
+ jit.jumpToExceptionHandler();
+ jit.addLinkTask(
+ [=] (LinkBuffer& linkBuffer) {
+ linkBuffer.link(call, FunctionPtr(lookupExceptionHandler));
+ });
+
state.finalizer->b3CodeLinkBuffer = std::make_unique<LinkBuffer>(
vm, jit, codeBlock, JITCompilationCanFail);
if (state.finalizer->b3CodeLinkBuffer->didFailToAllocate()) {
diff --git a/Source/JavaScriptCore/ftl/FTLB3Output.h b/Source/JavaScriptCore/ftl/FTLB3Output.h
index a5499eb..de4bcdf 100644
--- a/Source/JavaScriptCore/ftl/FTLB3Output.h
+++ b/Source/JavaScriptCore/ftl/FTLB3Output.h
@@ -410,21 +410,6 @@
void unreachable() { m_block->appendNew<B3::ControlValue>(m_proc, B3::Oops, origin()); }
- template<typename Functor>
- void speculate(LValue value, const StackmapArgumentList& arguments, const Functor& functor)
- {
- B3::CheckValue* check = speculate(value, arguments);
- check->setGenerator(functor);
- }
-
- B3::CheckValue* speculate(LValue value, const StackmapArgumentList& arguments)
- {
- B3::CheckValue* check = speculate(value);
- for (LValue value : arguments)
- check->append(B3::ConstrainedValue(value));
- return check;
- }
-
B3::CheckValue* speculate(LValue value)
{
return m_block->appendNew<B3::CheckValue>(m_proc, B3::Check, origin(), value);
diff --git a/Source/JavaScriptCore/ftl/FTLExceptionTarget.cpp b/Source/JavaScriptCore/ftl/FTLExceptionTarget.cpp
new file mode 100644
index 0000000..0644f0d
--- /dev/null
+++ b/Source/JavaScriptCore/ftl/FTLExceptionTarget.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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
+ * 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 "FTLExceptionTarget.h"
+
+#if ENABLE(FTL_JIT) && FTL_USES_B3
+
+#include "FTLState.h"
+
+namespace JSC { namespace FTL {
+
+ExceptionTarget::~ExceptionTarget()
+{
+}
+
+CodeLocationLabel ExceptionTarget::label(LinkBuffer& linkBuffer)
+{
+ if (m_isDefaultHandler)
+ return linkBuffer.locationOf(*m_defaultHandler);
+ return linkBuffer.locationOf(m_handle->label);
+}
+
+Box<CCallHelpers::JumpList> ExceptionTarget::jumps(CCallHelpers& jit)
+{
+ Box<CCallHelpers::JumpList> result = Box<CCallHelpers::JumpList>::create();
+ if (m_isDefaultHandler) {
+ Box<CCallHelpers::Label> defaultHandler = m_defaultHandler;
+ jit.addLinkTask(
+ [=] (LinkBuffer& linkBuffer) {
+ linkBuffer.link(*result, linkBuffer.locationOf(*defaultHandler));
+ });
+ } else {
+ RefPtr<OSRExitHandle> handle = m_handle;
+ jit.addLinkTask(
+ [=] (LinkBuffer& linkBuffer) {
+ linkBuffer.link(*result, linkBuffer.locationOf(handle->label));
+ });
+ }
+ return result;
+}
+
+ExceptionTarget::ExceptionTarget(
+ bool isDefaultHandler, Box<CCallHelpers::Label> defaultHandler, RefPtr<OSRExitHandle> handle)
+ : m_isDefaultHandler(isDefaultHandler)
+ , m_defaultHandler(defaultHandler)
+ , m_handle(handle)
+{
+}
+
+} } // namespace JSC::FTL
+
+#endif // ENABLE(FTL_JIT) && FTL_USES_B3
+
diff --git a/Source/JavaScriptCore/ftl/FTLExceptionTarget.h b/Source/JavaScriptCore/ftl/FTLExceptionTarget.h
new file mode 100644
index 0000000..2dce74f
--- /dev/null
+++ b/Source/JavaScriptCore/ftl/FTLExceptionTarget.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ * 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 FTLExceptionTarget_h
+#define FTLExceptionTarget_h
+
+#include "DFGCommon.h"
+
+#if ENABLE(FTL_JIT) && FTL_USES_B3
+
+#include "CCallHelpers.h"
+#include "FTLOSRExitHandle.h"
+#include <wtf/Box.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace JSC { namespace FTL {
+
+class State;
+
+class ExceptionTarget : public ThreadSafeRefCounted<ExceptionTarget> {
+public:
+ ~ExceptionTarget();
+
+ // It's OK to call this during linking, but not any sooner.
+ CodeLocationLabel label(LinkBuffer&);
+
+ // Or, you can get a JumpList at any time. Anything you add to this JumpList will be linked to
+ // the target's label.
+ Box<CCallHelpers::JumpList> jumps(CCallHelpers&);
+
+private:
+ friend class PatchpointExceptionHandle;
+
+ ExceptionTarget(bool isDefaultHandler, Box<CCallHelpers::Label>, RefPtr<OSRExitHandle>);
+
+ bool m_isDefaultHandler;
+ Box<CCallHelpers::Label> m_defaultHandler;
+ RefPtr<OSRExitHandle> m_handle;
+};
+
+} } // namespace JSC::FTL
+
+#endif // ENABLE(FTL_JIT) && FTL_USES_B3
+
+#endif // FTLExceptionTarget_h
+
diff --git a/Source/JavaScriptCore/ftl/FTLJITCode.cpp b/Source/JavaScriptCore/ftl/FTLJITCode.cpp
index 9af238e..13fb0e6 100644
--- a/Source/JavaScriptCore/ftl/FTLJITCode.cpp
+++ b/Source/JavaScriptCore/ftl/FTLJITCode.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -168,7 +168,13 @@
RegisterSet JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite(CodeBlock*, CallSiteIndex callSiteIndex)
{
#if FTL_USES_B3
- UNUSED_PARAM(callSiteIndex);
+ for (OSRExit& exit : osrExit) {
+ if (exit.m_exceptionHandlerCallSiteIndex.bits() == callSiteIndex.bits()) {
+ RELEASE_ASSERT(exit.m_isExceptionHandler);
+ RELEASE_ASSERT(exit.m_isUnwindHandler);
+ return ValueRep::usedRegisters(exit.m_valueReps);
+ }
+ }
#else // FTL_USES_B3
for (OSRExit& exit : osrExit) {
if (exit.m_exceptionHandlerCallSiteIndex.bits() == callSiteIndex.bits()) {
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index ccaabc3..53798a6 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -42,6 +42,7 @@
#include "FTLAbstractHeapRepository.h"
#include "FTLAvailableRecovery.h"
#include "FTLB3Output.h"
+#include "FTLExceptionTarget.h"
#include "FTLForOSREntryJITCode.h"
#include "FTLFormattedValue.h"
#include "FTLInlineCacheSize.h"
@@ -49,6 +50,7 @@
#include "FTLLoweredNodeValue.h"
#include "FTLOperations.h"
#include "FTLOutput.h"
+#include "FTLPatchpointExceptionHandle.h"
#include "FTLThunks.h"
#include "FTLWeightedTarget.h"
#include "JITAddGenerator.h"
@@ -138,6 +140,8 @@
void lower()
{
+ State* state = &m_ftlState;
+
CString name;
if (verboseCompilationEnabled()) {
name = toCString(
@@ -149,16 +153,16 @@
m_graph.ensureDominators();
#if !FTL_USES_B3
- m_ftlState.module =
- moduleCreateWithNameInContext(name.data(), m_ftlState.context);
+ state->module =
+ moduleCreateWithNameInContext(name.data(), state->context);
- m_ftlState.function = addFunction(
- m_ftlState.module, name.data(), functionType(m_out.int64));
- setFunctionCallingConv(m_ftlState.function, LLVMCCallConv);
+ state->function = addFunction(
+ state->module, name.data(), functionType(m_out.int64));
+ setFunctionCallingConv(state->function, LLVMCCallConv);
if (isX86() && Options::llvmDisallowAVX()) {
// AVX makes V8/raytrace 80% slower. It makes Kraken/audio-oscillator 4.5x
// slower. It should be disabled.
- addTargetDependentFunctionAttr(m_ftlState.function, "target-features", "-avx");
+ addTargetDependentFunctionAttr(state->function, "target-features", "-avx");
}
#endif // !FTL_USES_B3
@@ -168,7 +172,7 @@
#if FTL_USES_B3
m_out.initialize(m_heaps);
#else
- m_out.initialize(m_ftlState.module, m_ftlState.function, m_heaps);
+ m_out.initialize(state->module, state->function, m_heaps);
#endif
// We use prologue frequency for all of the initialization code.
@@ -201,7 +205,7 @@
size_t sizeOfCaptured = sizeof(JSValue) * m_graph.m_nextMachineLocal;
B3::StackSlotValue* capturedBase = m_out.lockedStackSlot(sizeOfCaptured);
m_captured = m_out.add(capturedBase, m_out.constIntPtr(sizeOfCaptured));
- m_ftlState.capturedValue = capturedBase;
+ state->capturedValue = capturedBase;
#else // FTL_USES_B3
LValue capturedAlloca = m_out.alloca(arrayType(m_out.int64, m_graph.m_nextMachineLocal));
@@ -209,9 +213,9 @@
m_out.ptrToInt(capturedAlloca, m_out.intPtr),
m_out.constIntPtr(m_graph.m_nextMachineLocal * sizeof(Register)));
- m_ftlState.capturedStackmapID = m_stackmapIDs++;
+ state->capturedStackmapID = m_stackmapIDs++;
m_out.call(
- m_out.voidType, m_out.stackmapIntrinsic(), m_out.constInt64(m_ftlState.capturedStackmapID),
+ m_out.voidType, m_out.stackmapIntrinsic(), m_out.constInt64(state->capturedStackmapID),
m_out.int32Zero, capturedAlloca);
#endif // FTL_USE_B3
@@ -286,10 +290,10 @@
if (hasVarargs) {
LValue varargsSpillSlots = m_out.alloca(
arrayType(m_out.int64, JSCallVarargs::numSpillSlotsNeeded()));
- m_ftlState.varargsSpillSlotsStackmapID = m_stackmapIDs++;
+ state->varargsSpillSlotsStackmapID = m_stackmapIDs++;
m_out.call(
m_out.voidType, m_out.stackmapIntrinsic(),
- m_out.constInt64(m_ftlState.varargsSpillSlotsStackmapID),
+ m_out.constInt64(state->varargsSpillSlotsStackmapID),
m_out.int32Zero, varargsSpillSlots);
}
@@ -300,10 +304,10 @@
LValue exceptionHandlingVolatileRegistersSpillSlots = m_out.alloca(
arrayType(m_out.int64, maxNumberOfCatchSpills));
- m_ftlState.exceptionHandlingSpillSlotStackmapID = m_stackmapIDs++;
+ state->exceptionHandlingSpillSlotStackmapID = m_stackmapIDs++;
m_out.call(
m_out.voidType, m_out.stackmapIntrinsic(),
- m_out.constInt64(m_ftlState.exceptionHandlingSpillSlotStackmapID),
+ m_out.constInt64(state->exceptionHandlingSpillSlotStackmapID),
m_out.int32Zero, exceptionHandlingVolatileRegistersSpillSlots);
}
#endif // !FTL_USES_B3
@@ -337,21 +341,30 @@
#if FTL_USES_B3
// FIXME
#else
- m_ftlState.handleStackOverflowExceptionStackmapID = m_stackmapIDs++;
+ state->handleStackOverflowExceptionStackmapID = m_stackmapIDs++;
m_out.call(
m_out.voidType, m_out.stackmapIntrinsic(),
- m_out.constInt64(m_ftlState.handleStackOverflowExceptionStackmapID),
+ m_out.constInt64(state->handleStackOverflowExceptionStackmapID),
m_out.constInt32(MacroAssembler::maxJumpReplacementSize()));
#endif
m_out.unreachable();
m_out.appendTo(m_handleExceptions, checkArguments);
#if FTL_USES_B3
- // FIXME
+ PatchpointValue* patchpoint = m_out.patchpoint(Void);
+ Box<CCallHelpers::Label> exceptionHandler = state->exceptionHandler;
+ patchpoint->setGenerator(
+ [=] (CCallHelpers& jit, const StackmapGenerationParams&) {
+ CCallHelpers::Jump jump = jit.jump();
+ jit.addLinkTask(
+ [=] (LinkBuffer& linkBuffer) {
+ linkBuffer.link(jump, linkBuffer.locationOf(*exceptionHandler));
+ });
+ });
#else
- m_ftlState.handleExceptionStackmapID = m_stackmapIDs++;
+ state->handleExceptionStackmapID = m_stackmapIDs++;
m_out.call(
- m_out.voidType, m_out.stackmapIntrinsic(), m_out.constInt64(m_ftlState.handleExceptionStackmapID),
+ m_out.voidType, m_out.stackmapIntrinsic(), m_out.constInt64(state->handleExceptionStackmapID),
m_out.constInt32(MacroAssembler::maxJumpReplacementSize()));
#endif
m_out.unreachable();
@@ -418,11 +431,11 @@
#if !FTL_USES_B3
if (Options::dumpLLVMIR())
- dumpModule(m_ftlState.module);
+ dumpModule(state->module);
if (verboseCompilationEnabled())
- m_ftlState.dumpState("after lowering");
+ state->dumpState("after lowering");
if (validationEnabled())
- verifyModule(m_ftlState.module);
+ verifyModule(state->module);
#endif
}
@@ -1656,7 +1669,7 @@
}
#if FTL_USES_B3
- B3::CheckValue* result =
+ CheckValue* result =
isSub ? m_out.speculateSub(left, right) : m_out.speculateAdd(left, right);
blessSpeculation(result, Overflow, noValue(), nullptr, m_origin);
setInt32(result);
@@ -1712,7 +1725,7 @@
LValue left = lowInt52(m_node->child1());
LValue right = lowInt52(m_node->child2());
#if FTL_USES_B3
- B3::CheckValue* result =
+ CheckValue* result =
isSub ? m_out.speculateSub(left, right) : m_out.speculateAdd(left, right);
blessSpeculation(result, Overflow, noValue(), nullptr, m_origin);
setInt52(result);
@@ -1803,7 +1816,7 @@
result = m_out.mul(left, right);
else {
#if FTL_USES_B3
- B3::CheckValue* speculation = m_out.speculateMul(left, right);
+ CheckValue* speculation = m_out.speculateMul(left, right);
blessSpeculation(speculation, Overflow, noValue(), nullptr, m_origin);
result = speculation;
#else // FTL_USES_B3
@@ -1837,7 +1850,7 @@
LValue right = lowInt52(m_node->child2(), opposite(kind));
#if FTL_USES_B3
- B3::CheckValue* result = m_out.speculateMul(left, right);
+ CheckValue* result = m_out.speculateMul(left, right);
blessSpeculation(result, Overflow, noValue(), nullptr, m_origin);
#else // FTL_USES_B3
LValue overflowResult = m_out.mulWithOverflow64(left, right);
@@ -2643,8 +2656,8 @@
// https://bugs.webkit.org/show_bug.cgi?id=151686
B3::PatchpointValue* patchpoint = m_out.patchpoint(Void);
- patchpoint->append(base, ValueRep::SomeRegister);
- patchpoint->append(value, ValueRep::SomeRegister);
+ patchpoint->appendSomeRegister(base);
+ patchpoint->appendSomeRegister(value);
patchpoint->clobber(RegisterSet::macroScratchRegisters());
State* state = &m_ftlState;
@@ -4291,7 +4304,7 @@
for (unsigned i = 1; i < numKids; ++i) {
flags = m_out.bitAnd(flags, m_out.load32(kids[i], m_heaps.JSString_flags));
#if FTL_USES_B3
- B3::CheckValue* lengthCheck = m_out.speculateAdd(
+ CheckValue* lengthCheck = m_out.speculateAdd(
length, m_out.load32(kids[i], m_heaps.JSString_length));
blessSpeculation(lengthCheck, Uncountable, noValue(), nullptr, m_origin);
length = lengthCheck;
@@ -5268,8 +5281,8 @@
// Append the forms of the arguments that we will use before any clobbering happens.
patchpoint->append(jsCallee, ValueRep::reg(GPRInfo::regT0));
if (jsArguments)
- patchpoint->append(jsArguments, ValueRep::SomeRegister);
- patchpoint->append(thisArg, ValueRep::SomeRegister);
+ patchpoint->appendSomeRegister(jsArguments);
+ patchpoint->appendSomeRegister(thisArg);
if (!forwarding) {
// Now append them again for after clobbering. Note that the compiler may ask us to use a
@@ -6126,7 +6139,7 @@
UniquedStringImpl* str = bitwise_cast<UniquedStringImpl*>(string->tryGetValueImpl());
#if FTL_USES_B3
B3::PatchpointValue* patchpoint = m_out.patchpoint(Int64);
- patchpoint->append(cell, ValueRep::SomeRegister);
+ patchpoint->appendSomeRegister(cell);
patchpoint->clobber(RegisterSet::macroScratchRegisters());
State* state = &m_ftlState;
@@ -7205,21 +7218,38 @@
UniquedStringImpl* uid = m_graph.identifiers()[node->identifierNumber()];
#if FTL_USES_B3
- // FIXME: Make this do exceptions.
- // https://bugs.webkit.org/show_bug.cgi?id=151686
-
B3::PatchpointValue* patchpoint = m_out.patchpoint(Int64);
- patchpoint->append(base, ValueRep::SomeRegister);
+ patchpoint->appendSomeRegister(base);
+
+ // FIXME: If this is a GetByIdFlush, we might get some performance boost if we claim that it
+ // clobbers volatile registers late. It's not necessary for correctness, though, since the
+ // IC code is super smart about saving registers.
+ // https://bugs.webkit.org/show_bug.cgi?id=152848
+
patchpoint->clobber(RegisterSet::macroScratchRegisters());
+ RefPtr<PatchpointExceptionHandle> exceptionHandle =
+ preparePatchpointForExceptions(patchpoint);
+
State* state = &m_ftlState;
patchpoint->setGenerator(
[=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
+ CallSiteIndex callSiteIndex =
+ state->jitCode->common.addUniqueCallSiteIndex(node->origin.semantic);
+
+ // This is the direct exit target for operation calls.
+ Box<CCallHelpers::JumpList> exceptions =
+ exceptionHandle->scheduleExitCreation(params)->jumps(jit);
+
+ // This is the exit for call IC's created by the getById for getters. We don't have
+ // to do anything weird other than call this, since it will associate the exit with
+ // the callsite index.
+ exceptionHandle->scheduleExitCreationForUnwind(params, callSiteIndex);
+
auto generator = Box<JITGetByIdGenerator>::create(
- jit.codeBlock(), node->origin.semantic,
- state->jitCode->common.addUniqueCallSiteIndex(node->origin.semantic),
+ jit.codeBlock(), node->origin.semantic, callSiteIndex,
params.unavailableRegisters(), JSValueRegs(params[1].gpr()),
JSValueRegs(params[0].gpr()));
@@ -7229,15 +7259,12 @@
params.addLatePath(
[=] (CCallHelpers& jit) {
AllowMacroScratchRegisterUsage allowScratch(jit);
-
- // FIXME: Make this do something.
- CCallHelpers::JumpList exceptions;
generator->slowPathJump().link(&jit);
CCallHelpers::Label slowPathBegin = jit.label();
CCallHelpers::Call slowPathCall = callOperation(
*state, params.unavailableRegisters(), jit, node->origin.semantic,
- &exceptions, operationGetByIdOptimize, params[0].gpr(),
+ exceptions.get(), operationGetByIdOptimize, params[0].gpr(),
CCallHelpers::TrustedImmPtr(generator->stubInfo()), params[1].gpr(),
CCallHelpers::TrustedImmPtr(uid)).call();
jit.jump().linkTo(done, &jit);
@@ -7524,8 +7551,8 @@
SnippetOperand rightOperand(m_state.forNode(node->child2()).resultType());
PatchpointValue* patchpoint = m_out.patchpoint(Int64);
- patchpoint->append(left, ValueRep::SomeRegister);
- patchpoint->append(right, ValueRep::SomeRegister);
+ patchpoint->appendSomeRegister(left);
+ patchpoint->appendSomeRegister(right);
patchpoint->append(m_tagMask, ValueRep::reg(GPRInfo::tagMaskRegister));
patchpoint->append(m_tagTypeNumber, ValueRep::reg(GPRInfo::tagTypeNumberRegister));
patchpoint->numGPScratchRegisters = 1;
@@ -7582,8 +7609,8 @@
SnippetOperand rightOperand(m_state.forNode(node->child2()).resultType());
PatchpointValue* patchpoint = m_out.patchpoint(Int64);
- patchpoint->append(left, ValueRep::SomeRegister);
- patchpoint->append(right, ValueRep::SomeRegister);
+ patchpoint->appendSomeRegister(left);
+ patchpoint->appendSomeRegister(right);
patchpoint->append(m_tagMask, ValueRep::reg(GPRInfo::tagMaskRegister));
patchpoint->append(m_tagTypeNumber, ValueRep::reg(GPRInfo::tagTypeNumberRegister));
patchpoint->numGPScratchRegisters = 1;
@@ -7634,8 +7661,8 @@
SnippetOperand rightOperand(m_state.forNode(node->child2()).resultType());
PatchpointValue* patchpoint = m_out.patchpoint(Int64);
- patchpoint->append(left, ValueRep::SomeRegister);
- patchpoint->append(right, ValueRep::SomeRegister);
+ patchpoint->appendSomeRegister(left);
+ patchpoint->appendSomeRegister(right);
patchpoint->append(m_tagMask, ValueRep::reg(GPRInfo::tagMaskRegister));
patchpoint->append(m_tagTypeNumber, ValueRep::reg(GPRInfo::tagTypeNumberRegister));
patchpoint->numGPScratchRegisters = 1;
@@ -10132,7 +10159,43 @@
m_out.appendTo(continuation);
}
-#if !FTL_USES_B3
+#if FTL_USES_B3
+ RefPtr<PatchpointExceptionHandle> preparePatchpointForExceptions(PatchpointValue* value)
+ {
+ CodeOrigin opCatchOrigin;
+ HandlerInfo* exceptionHandler;
+ bool willCatchException = m_graph.willCatchExceptionInMachineFrame(m_origin.forExit, opCatchOrigin, exceptionHandler);
+ if (!willCatchException)
+ return PatchpointExceptionHandle::defaultHandle(m_ftlState);
+
+ if (verboseCompilationEnabled()) {
+ dataLog(" Patchpoint exception OSR exit #", m_ftlState.jitCode->osrExitDescriptors.size(), " with availability: ", availabilityMap(), "\n");
+ if (!m_availableRecoveries.isEmpty())
+ dataLog(" Available recoveries: ", listDump(m_availableRecoveries), "\n");
+ }
+
+ bool exitOK = true;
+ NodeOrigin origin = m_origin.withForExitAndExitOK(opCatchOrigin, exitOK);
+
+ OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(noValue(), nullptr);
+
+ // Compute the offset into the StackmapGenerationParams where we will find the exit arguments
+ // we are about to append. We need to account for both the children we've already added, and
+ // for the possibility of a result value if the patchpoint is not void.
+ unsigned offset = value->numChildren();
+ if (value->type() != Void)
+ offset++;
+
+ // Use LateColdAny to ensure that the stackmap arguments interfere with the patchpoint's
+ // result and with any late-clobbered registers.
+ value->appendVectorWithRep(
+ buildExitArguments(exitDescriptor, opCatchOrigin, noValue()),
+ ValueRep::LateColdAny);
+
+ return PatchpointExceptionHandle::create(
+ m_ftlState, exitDescriptor, origin, offset, *exceptionHandler);
+ }
+#else // FTL_USES_B3
void appendOSRExitArgumentsForPatchpointIfWillCatchException(StackmapArgumentList& arguments, ExceptionType exceptionType, unsigned offsetOfExitArguments)
{
CodeOrigin opCatchOrigin;
@@ -10153,7 +10216,7 @@
buildExitArguments(exitDescriptor, exitDescriptorImpl.m_codeOrigin, noValue(), offsetOfExitArguments);
arguments.appendVector(freshList);
}
-#endif // !FTL_USES_B3
+#endif // FTL_USES_B3
LBasicBlock lowBlock(DFG::BasicBlock* block)
{
@@ -10245,7 +10308,7 @@
}
#if FTL_USES_B3
- void blessSpeculation(B3::CheckValue* value, ExitKind kind, FormattedValue lowValue, Node* highValue, NodeOrigin origin, bool isExceptionHandler = false)
+ void blessSpeculation(CheckValue* value, ExitKind kind, FormattedValue lowValue, Node* highValue, NodeOrigin origin, bool isExceptionHandler = false)
{
OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(lowValue, highValue);
diff --git a/Source/JavaScriptCore/ftl/FTLPatchpointExceptionHandle.cpp b/Source/JavaScriptCore/ftl/FTLPatchpointExceptionHandle.cpp
new file mode 100644
index 0000000..4ca014e
--- /dev/null
+++ b/Source/JavaScriptCore/ftl/FTLPatchpointExceptionHandle.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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
+ * 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 "FTLPatchpointExceptionHandle.h"
+
+#if ENABLE(FTL_JIT) && FTL_USES_B3
+
+#include "B3StackmapGenerationParams.h"
+#include "FTLExceptionTarget.h"
+#include "FTLOSRExit.h"
+#include "FTLOSRExitHandle.h"
+#include "FTLState.h"
+
+namespace JSC { namespace FTL {
+
+using namespace DFG;
+
+RefPtr<PatchpointExceptionHandle> PatchpointExceptionHandle::create(
+ State& state, OSRExitDescriptor* descriptor, NodeOrigin origin, unsigned offset,
+ const HandlerInfo& handler)
+{
+ return adoptRef(new PatchpointExceptionHandle(state, descriptor, origin, offset, handler));
+}
+
+RefPtr<PatchpointExceptionHandle> PatchpointExceptionHandle::defaultHandle(State& state)
+{
+ if (!state.defaultExceptionHandle) {
+ state.defaultExceptionHandle = adoptRef(
+ new PatchpointExceptionHandle(state, nullptr, NodeOrigin(), 0, HandlerInfo()));
+ }
+ return state.defaultExceptionHandle;
+}
+
+PatchpointExceptionHandle::~PatchpointExceptionHandle()
+{
+}
+
+RefPtr<ExceptionTarget> PatchpointExceptionHandle::scheduleExitCreation(
+ const B3::StackmapGenerationParams& params)
+{
+ if (!m_descriptor) {
+ // NOTE: This object could be a singleton, however usually we toss the ExceptionHandler
+ // object shortly after creation.
+ bool isDefaultHandler = true;
+ return adoptRef(
+ new ExceptionTarget(isDefaultHandler, m_state.exceptionHandler, nullptr));
+ }
+ bool isDefaultHandler = false;
+ return adoptRef(new ExceptionTarget(isDefaultHandler, { }, createHandle(params)));
+}
+
+void PatchpointExceptionHandle::scheduleExitCreationForUnwind(
+ const B3::StackmapGenerationParams& params, CallSiteIndex callSiteIndex)
+{
+ if (!m_descriptor)
+ return;
+
+ RefPtr<OSRExitHandle> handle = createHandle(params);
+
+ handle->exit.m_isUnwindHandler = true;
+ handle->exit.m_exceptionHandlerCallSiteIndex = callSiteIndex;
+
+ HandlerInfo handler = m_handler;
+ params.addLatePath(
+ [handle, handler, callSiteIndex] (CCallHelpers& jit) {
+ CodeBlock* codeBlock = jit.codeBlock();
+ jit.addLinkTask(
+ [=] (LinkBuffer& linkBuffer) {
+ HandlerInfo newHandler = handler;
+ newHandler.start = callSiteIndex.bits();
+ newHandler.end = callSiteIndex.bits() + 1;
+ newHandler.nativeCode = linkBuffer.locationOf(handle->label);
+ codeBlock->appendExceptionHandler(newHandler);
+ });
+ });
+}
+
+PatchpointExceptionHandle::PatchpointExceptionHandle(
+ State& state, OSRExitDescriptor* descriptor, NodeOrigin origin, unsigned offset,
+ const HandlerInfo& handler)
+ : m_state(state)
+ , m_descriptor(descriptor)
+ , m_origin(origin)
+ , m_offset(offset)
+ , m_handler(handler)
+{
+}
+
+RefPtr<OSRExitHandle> PatchpointExceptionHandle::createHandle(
+ const B3::StackmapGenerationParams& params)
+{
+ bool isExceptionHandler = true;
+ return m_descriptor->emitOSRExitLater(
+ m_state, Uncountable, m_origin, params, m_offset, isExceptionHandler);
+}
+
+} } // namespace JSC::FTL
+
+#endif // ENABLE(FTL_JIT) && FTL_USES_B3
+
diff --git a/Source/JavaScriptCore/ftl/FTLPatchpointExceptionHandle.h b/Source/JavaScriptCore/ftl/FTLPatchpointExceptionHandle.h
new file mode 100644
index 0000000..f9f0aef
--- /dev/null
+++ b/Source/JavaScriptCore/ftl/FTLPatchpointExceptionHandle.h
@@ -0,0 +1,105 @@
+/*
+ * 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
+ * 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 FTLPatchpointExceptionHandle_h
+#define FTLPatchpointExceptionHandle_h
+
+#include "DFGCommon.h"
+
+#if ENABLE(FTL_JIT) && FTL_USES_B3
+
+#include "DFGNodeOrigin.h"
+#include "HandlerInfo.h"
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace JSC {
+
+namespace B3 {
+class StackmapGenerationParams;
+} // namespace B3
+
+namespace FTL {
+
+class ExceptionTarget;
+class State;
+struct OSRExitDescriptor;
+struct OSRExitHandle;
+
+class PatchpointExceptionHandle : public ThreadSafeRefCounted<PatchpointExceptionHandle> {
+public:
+ static RefPtr<PatchpointExceptionHandle> create(
+ State&, OSRExitDescriptor*, DFG::NodeOrigin, unsigned offset, const HandlerInfo&);
+
+ static RefPtr<PatchpointExceptionHandle> defaultHandle(State&);
+
+ ~PatchpointExceptionHandle();
+
+ // Note that you can use this handle to schedule any number of exits. This capability is here for
+ // two reasons:
+ //
+ // - B3 code duplication. B3 could take a patchpoint and turn it into multiple patchpoints if it
+ // duplicates code. Duplicating code is legal since you can do it without changing the behavior
+ // of the program. One example is tail duplication. Another is jump threading. Yet another is
+ // path specialization. You will have one PatchpointExceptionHandle per patchpoint you create
+ // during DFG->B3 lowering, and that patchpoint will have a generator that calls
+ // handle->scheduleBlah(). That generator will be called multiple times if your patchpoint got
+ // duplicated.
+ //
+ // - Combination of unwind and non-unwind exception handlers inside one patchpoint. A GetById may
+ // need both an exception handler that serves as an unwind target and an exception handler that
+ // is branched to directly for operation calls emitted inside the patchpoint. In that case,
+ // you'll call both scheduleExitCreation() and scheduleExitCreationForUnwind() on the same
+ // handle.
+
+ // Schedules the creation of an OSR exit jump destination. You don't know when this will be
+ // created, but it will happen before linking. You can link jumps to it during link time. That's
+ // why this returns an ExceptionTarget. That will contain the jump destination (target->label())
+ // at link time.
+ RefPtr<ExceptionTarget> scheduleExitCreation(const B3::StackmapGenerationParams&);
+
+ // Schedules the creation of an OSR exit jump destination, and ensures that it gets associated
+ // with the handler for some callsite index.
+ void scheduleExitCreationForUnwind(const B3::StackmapGenerationParams&, CallSiteIndex);
+
+private:
+ PatchpointExceptionHandle(
+ State&, OSRExitDescriptor*, DFG::NodeOrigin, unsigned offset, const HandlerInfo&);
+
+ RefPtr<OSRExitHandle> createHandle(const B3::StackmapGenerationParams&);
+
+ State& m_state;
+ OSRExitDescriptor* m_descriptor;
+ DFG::NodeOrigin m_origin;
+ unsigned m_offset;
+ HandlerInfo m_handler;
+};
+
+} } // namespace JSC::FTL
+
+#endif // ENABLE(FTL_JIT) && FTL_USES_B3
+
+#endif // FTLPatchpointExceptionHandle_h
+
diff --git a/Source/JavaScriptCore/ftl/FTLState.cpp b/Source/JavaScriptCore/ftl/FTLState.cpp
index fc0eceb..8176735 100644
--- a/Source/JavaScriptCore/ftl/FTLState.cpp
+++ b/Source/JavaScriptCore/ftl/FTLState.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -33,6 +33,7 @@
#include "FTLForOSREntryJITCode.h"
#include "FTLJITCode.h"
#include "FTLJITFinalizer.h"
+#include "FTLPatchpointExceptionHandle.h"
#include <llvm/InitializeLLVM.h>
#include <stdio.h>
diff --git a/Source/JavaScriptCore/ftl/FTLState.h b/Source/JavaScriptCore/ftl/FTLState.h
index c7d46dd..852fb92e 100644
--- a/Source/JavaScriptCore/ftl/FTLState.h
+++ b/Source/JavaScriptCore/ftl/FTLState.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -40,6 +40,7 @@
#include "FTLJSCallVarargs.h"
#include "FTLJSTailCall.h"
#include "FTLStackMaps.h"
+#include <wtf/Box.h>
#include <wtf/Noncopyable.h>
namespace JSC {
@@ -51,6 +52,8 @@
namespace FTL {
+class PatchpointExceptionHandle;
+
inline bool verboseCompilationEnabled()
{
return DFG::verboseCompilationEnabled(DFG::FTLMode);
@@ -82,8 +85,10 @@
GeneratedFunction generatedFunction;
JITFinalizer* finalizer;
#if FTL_USES_B3
- B3::PatchpointValue* handleStackOverflowExceptionValue { nullptr };
- B3::PatchpointValue* handleExceptionValue { nullptr };
+ // Top-level exception handler. Jump here if you know that you have to genericUnwind() and there
+ // are no applicable catch blocks anywhere in the Graph.
+ RefPtr<PatchpointExceptionHandle> defaultExceptionHandle;
+ Box<CCallHelpers::Label> exceptionHandler { Box<CCallHelpers::Label>::create() };
B3::StackSlotValue* capturedValue { nullptr };
#else // FTL_USES_B3
unsigned handleStackOverflowExceptionStackmapID { UINT_MAX };
diff --git a/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception-interesting-live-state.js b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception-interesting-live-state.js
new file mode 100644
index 0000000..a7320ab
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception-interesting-live-state.js
@@ -0,0 +1,61 @@
+function foo(o, p) {
+ var x = 100;
+ var result = 101;
+ var pf = p.g;
+ try {
+ x = 102;
+ pf++;
+ result = o.f;
+ o = 104;
+ pf++;
+ x = 106;
+ } catch (e) {
+ return {outcome: "exception", values: [o, pf, x, result]};
+ }
+ return {outcome: "return", values: [o, pf, x, result]};
+}
+
+noInline(foo);
+
+// Warm up foo() with polymorphic objects and getters.
+for (var i = 0; i < 100000; ++i) {
+ var o = {};
+ o.__defineGetter__("f", function() {
+ return 107;
+ });
+ if (i & 1)
+ o["i" + i] = i; // Make it polymorphic.
+ var result = foo(o, {g:200});
+ if (result.outcome !== "return")
+ throw "Error in loop: bad outcome: " + result.outcome;
+ if (result.values.length !== 4)
+ throw "Error in loop: bad number of values: " + result.values.length;
+ if (result.values[0] !== 104)
+ throw "Error in loop: bad values[0]: " + result.values[0];
+ if (result.values[1] !== 202)
+ throw "Error in loop: bad values[1]: " + result.values[1];
+ if (result.values[2] !== 106)
+ throw "Error in loop: bad values[2]: " + result.values[2];
+ if (result.values[3] !== 107)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+}
+
+// Now throw an exception.
+var o = {};
+o.__defineGetter__("f", function() {
+ throw "Error42";
+});
+var result = foo(o, {g:300});
+if (result.outcome !== "exception")
+ throw "Error at end: bad outcome: " + result.outcome;
+if (result.values.length !== 4)
+ throw "Error at end: bad number of values: " + result.values.length;
+if (result.values[0] !== o)
+ throw "Error at end: bad values[0]: " + result.values[0];
+if (result.values[1] !== 301)
+ throw "Error at end: bad values[1]: " + result.values[1];
+if (result.values[2] !== 102)
+ throw "Error at end: bad values[2]: " + result.values[2];
+if (result.values[3] !== 101)
+ throw "Error at end: bad values[3]: " + result.values[3];
+
diff --git a/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception-no-catch.js b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception-no-catch.js
new file mode 100644
index 0000000..85f357a
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception-no-catch.js
@@ -0,0 +1,53 @@
+function foo(o, p) {
+ var x = 100;
+ var result = 101;
+ x = 102;
+ p = 103;
+ result = o.f;
+ o = 104;
+ p = 105;
+ x = 106;
+ return {outcome: "return", values: [o, p, x, result]};
+}
+
+noInline(foo);
+
+// Warm up foo() with polymorphic objects and getters.
+for (var i = 0; i < 100000; ++i) {
+ var o = {};
+ o.__defineGetter__("f", function() {
+ return 107;
+ });
+ if (i & 1)
+ o["i" + i] = i; // Make it polymorphic.
+ var result = foo(o);
+ if (result.outcome !== "return")
+ throw "Error in loop: bad outcome: " + result.outcome;
+ if (result.values.length !== 4)
+ throw "Error in loop: bad number of values: " + result.values.length;
+ if (result.values[0] !== 104)
+ throw "Error in loop: bad values[0]: " + result.values[0];
+ if (result.values[1] !== 105)
+ throw "Error in loop: bad values[1]: " + result.values[1];
+ if (result.values[2] !== 106)
+ throw "Error in loop: bad values[2]: " + result.values[2];
+ if (result.values[3] !== 107)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+}
+
+// Now throw an exception.
+var result;
+try {
+ var o = {};
+ o.__defineGetter__("f", function() {
+ throw "Error42";
+ });
+ result = foo(o, 108);
+} catch (e) {
+ if (e != "Error42")
+ throw "Error at end: bad exception: " + e;
+ result = {outcome: "exception"};
+}
+if (result.outcome !== "exception")
+ throw "Error at end: bad outcome: " + result.outcome;
+
diff --git a/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception.js b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception.js
new file mode 100644
index 0000000..38e732d
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-getter-exception.js
@@ -0,0 +1,60 @@
+function foo(o, p) {
+ var x = 100;
+ var result = 101;
+ try {
+ x = 102;
+ p = 103;
+ result = o.f;
+ o = 104;
+ p = 105;
+ x = 106;
+ } catch (e) {
+ return {outcome: "exception", values: [o, p, x, result]};
+ }
+ return {outcome: "return", values: [o, p, x, result]};
+}
+
+noInline(foo);
+
+// Warm up foo() with polymorphic objects and getters.
+for (var i = 0; i < 100000; ++i) {
+ var o = {};
+ o.__defineGetter__("f", function() {
+ return 107;
+ });
+ if (i & 1)
+ o["i" + i] = i; // Make it polymorphic.
+ var result = foo(o);
+ if (result.outcome !== "return")
+ throw "Error in loop: bad outcome: " + result.outcome;
+ if (result.values.length !== 4)
+ throw "Error in loop: bad number of values: " + result.values.length;
+ if (result.values[0] !== 104)
+ throw "Error in loop: bad values[0]: " + result.values[0];
+ if (result.values[1] !== 105)
+ throw "Error in loop: bad values[1]: " + result.values[1];
+ if (result.values[2] !== 106)
+ throw "Error in loop: bad values[2]: " + result.values[2];
+ if (result.values[3] !== 107)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+}
+
+// Now throw an exception.
+var o = {};
+o.__defineGetter__("f", function() {
+ throw "Error42";
+});
+var result = foo(o, 108);
+if (result.outcome !== "exception")
+ throw "Error at end: bad outcome: " + result.outcome;
+if (result.values.length !== 4)
+ throw "Error at end: bad number of values: " + result.values.length;
+if (result.values[0] !== o)
+ throw "Error at end: bad values[0]: " + result.values[0];
+if (result.values[1] !== 103)
+ throw "Error at end: bad values[1]: " + result.values[1];
+if (result.values[2] !== 102)
+ throw "Error at end: bad values[2]: " + result.values[2];
+if (result.values[3] !== 101)
+ throw "Error at end: bad values[3]: " + result.values[3];
+
diff --git a/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception-interesting-live-state.js b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception-interesting-live-state.js
new file mode 100644
index 0000000..c8c14bc
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception-interesting-live-state.js
@@ -0,0 +1,58 @@
+function foo(o, p) {
+ var x = 100;
+ var result = 101;
+ var pf = p.g;
+ try {
+ x = 102;
+ pf++;
+ result = o.f;
+ o = 104;
+ pf++;
+ x = 106;
+ } catch (e) {
+ return {outcome: "exception", values: [o, pf, x, result]};
+ }
+ return {outcome: "return", values: [o, pf, x, result]};
+}
+
+noInline(foo);
+
+// Warm up foo() with polymorphic objects.
+for (var i = 0; i < 100000; ++i) {
+ var o;
+ o = {f:107};
+ o["i" + i] = i; // Make it polymorphic.
+ var result = foo(o, {g:200});
+ if (result.outcome !== "return")
+ throw "Error in loop: bad outcome: " + result.outcome;
+ if (result.values.length !== 4)
+ throw "Error in loop: bad number of values: " + result.values.length;
+ if (result.values[0] !== 104)
+ throw "Error in loop: bad values[0]: " + result.values[0];
+ if (result.values[1] !== 202)
+ throw "Error in loop: bad values[1]: " + result.values[1];
+ if (result.values[2] !== 106)
+ throw "Error in loop: bad values[2]: " + result.values[2];
+ if (result.values[3] !== 107)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+}
+
+// Now throw an exception.
+var o = {};
+o.__defineGetter__("f", function() {
+ throw "Error42";
+});
+var result = foo(o, {g:300});
+if (result.outcome !== "exception")
+ throw "Error at end: bad outcome: " + result.outcome;
+if (result.values.length !== 4)
+ throw "Error at end: bad number of values: " + result.values.length;
+if (result.values[0] !== o)
+ throw "Error at end: bad values[0]: " + result.values[0];
+if (result.values[1] !== 301)
+ throw "Error at end: bad values[1]: " + result.values[1];
+if (result.values[2] !== 102)
+ throw "Error at end: bad values[2]: " + result.values[2];
+if (result.values[3] !== 101)
+ throw "Error at end: bad values[3]: " + result.values[3];
+
diff --git a/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception-no-catch.js b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception-no-catch.js
new file mode 100644
index 0000000..d19ab2e
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception-no-catch.js
@@ -0,0 +1,49 @@
+function foo(o, p) {
+ var x = 100;
+ var result = 101;
+ x = 102;
+ p = 103;
+ result = o.f;
+ o = 104;
+ p = 105;
+ x = 106;
+ return {outcome: "return", values: [o, p, x, result]};
+}
+
+noInline(foo);
+
+// Warm up foo() with polymorphic objects.
+for (var i = 0; i < 100000; ++i) {
+ var o;
+ o = {f:107};
+ o["i" + i] = i; // Make it polymorphic.
+ var result = foo(o);
+ if (result.outcome !== "return")
+ throw "Error in loop: bad outcome: " + result.outcome;
+ if (result.values.length !== 4)
+ throw "Error in loop: bad number of values: " + result.values.length;
+ if (result.values[0] !== 104)
+ throw "Error in loop: bad values[0]: " + result.values[0];
+ if (result.values[1] !== 105)
+ throw "Error in loop: bad values[1]: " + result.values[1];
+ if (result.values[2] !== 106)
+ throw "Error in loop: bad values[2]: " + result.values[2];
+ if (result.values[3] !== 107)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+}
+
+// Now throw an exception.
+var result;
+try {
+ var o = {};
+ o.__defineGetter__("f", function() {
+ throw "Error42";
+ });
+ result = foo(o, 108);
+} catch (e) {
+ if (e != "Error42")
+ throw "Error at end: bad exception: " + e;
+ result = {outcome: "exception"};
+}
+if (result.outcome !== "exception")
+ throw "Error at end: bad outcome: " + result.outcome;
diff --git a/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception.js b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception.js
new file mode 100644
index 0000000..8dbfe6d
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/ftl-get-by-id-slow-exception.js
@@ -0,0 +1,57 @@
+function foo(o, p) {
+ var x = 100;
+ var result = 101;
+ try {
+ x = 102;
+ p = 103;
+ result = o.f;
+ o = 104;
+ p = 105;
+ x = 106;
+ } catch (e) {
+ return {outcome: "exception", values: [o, p, x, result]};
+ }
+ return {outcome: "return", values: [o, p, x, result]};
+}
+
+noInline(foo);
+
+// Warm up foo() with polymorphic objects.
+for (var i = 0; i < 100000; ++i) {
+ var o;
+ o = {f:107};
+ o["i" + i] = i; // Make it polymorphic.
+ var result = foo(o);
+ if (result.outcome !== "return")
+ throw "Error in loop: bad outcome: " + result.outcome;
+ if (result.values.length !== 4)
+ throw "Error in loop: bad number of values: " + result.values.length;
+ if (result.values[0] !== 104)
+ throw "Error in loop: bad values[0]: " + result.values[0];
+ if (result.values[1] !== 105)
+ throw "Error in loop: bad values[1]: " + result.values[1];
+ if (result.values[2] !== 106)
+ throw "Error in loop: bad values[2]: " + result.values[2];
+ if (result.values[3] !== 107)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+}
+
+// Now throw an exception.
+var o = {};
+o.__defineGetter__("f", function() {
+ throw "Error42";
+});
+var result = foo(o, 108);
+if (result.outcome !== "exception")
+ throw "Error at end: bad outcome: " + result.outcome;
+if (result.values.length !== 4)
+ throw "Error at end: bad number of values: " + result.values.length;
+if (result.values[0] !== o)
+ throw "Error at end: bad values[0]: " + result.values[0];
+if (result.values[1] !== 103)
+ throw "Error at end: bad values[1]: " + result.values[1];
+if (result.values[2] !== 102)
+ throw "Error at end: bad values[2]: " + result.values[2];
+if (result.values[3] !== 101)
+ throw "Error at end: bad values[3]: " + result.values[3];
+
diff --git a/Source/JavaScriptCore/tests/stress/ftl-operation-exception-interesting-live-state.js b/Source/JavaScriptCore/tests/stress/ftl-operation-exception-interesting-live-state.js
new file mode 100644
index 0000000..6f4ef2c
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/ftl-operation-exception-interesting-live-state.js
@@ -0,0 +1,63 @@
+function foo(o, p) {
+ var x = 100;
+ var result = 101;
+ var pf = p.g;
+ try {
+ x = 102;
+ pf++;
+ result = o.f;
+ o = 104;
+ pf++;
+ x = 106;
+ } catch (e) {
+ return {outcome: "exception", values: [o, pf, x, result]};
+ }
+ return {outcome: "return", values: [o, pf, x, result]};
+}
+
+noInline(foo);
+
+// Warm up foo() with polymorphic objects and non-object types.
+for (var i = 0; i < 100000; ++i) {
+ var o;
+ var isObject = i & 1;
+ if (isObject) {
+ o = {f:107};
+ o["i" + i] = i; // Make it polymorphic.
+ } else
+ o = 42;
+ var result = foo(o, {g:200});
+ if (result.outcome !== "return")
+ throw "Error in loop: bad outcome: " + result.outcome;
+ if (result.values.length !== 4)
+ throw "Error in loop: bad number of values: " + result.values.length;
+ if (result.values[0] !== 104)
+ throw "Error in loop: bad values[0]: " + result.values[0];
+ if (result.values[1] !== 202)
+ throw "Error in loop: bad values[1]: " + result.values[1];
+ if (result.values[2] !== 106)
+ throw "Error in loop: bad values[2]: " + result.values[2];
+ if (isObject) {
+ if (result.values[3] !== 107)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+ } else {
+ if (result.values[3] !== void 0)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+ }
+}
+
+// Now throw an exception.
+var result = foo(null, {g:300});
+if (result.outcome !== "exception")
+ throw "Error at end: bad outcome: " + result.outcome;
+if (result.values.length !== 4)
+ throw "Error at end: bad number of values: " + result.values.length;
+if (result.values[0] !== null)
+ throw "Error at end: bad values[0]: " + result.values[0];
+if (result.values[1] !== 301)
+ throw "Error at end: bad values[1]: " + result.values[1];
+if (result.values[2] !== 102)
+ throw "Error at end: bad values[2]: " + result.values[2];
+if (result.values[3] !== 101)
+ throw "Error at end: bad values[3]: " + result.values[3];
+
diff --git a/Source/JavaScriptCore/tests/stress/ftl-operation-exception-no-catch.js b/Source/JavaScriptCore/tests/stress/ftl-operation-exception-no-catch.js
new file mode 100644
index 0000000..50ec2e4
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/ftl-operation-exception-no-catch.js
@@ -0,0 +1,52 @@
+function foo(o, p) {
+ var x = 100;
+ var result = 101;
+ x = 102;
+ p = 103;
+ result = o.f;
+ o = 104;
+ p = 105;
+ x = 106;
+ return {outcome: "return", values: [o, p, x, result]};
+}
+
+noInline(foo);
+
+// Warm up foo() with polymorphic objects and non-object types.
+for (var i = 0; i < 100000; ++i) {
+ var o;
+ var isObject = i & 1;
+ if (isObject) {
+ o = {f:107};
+ o["i" + i] = i; // Make it polymorphic.
+ } else
+ o = 42;
+ var result = foo(o);
+ if (result.outcome !== "return")
+ throw "Error in loop: bad outcome: " + result.outcome;
+ if (result.values.length !== 4)
+ throw "Error in loop: bad number of values: " + result.values.length;
+ if (result.values[0] !== 104)
+ throw "Error in loop: bad values[0]: " + result.values[0];
+ if (result.values[1] !== 105)
+ throw "Error in loop: bad values[1]: " + result.values[1];
+ if (result.values[2] !== 106)
+ throw "Error in loop: bad values[2]: " + result.values[2];
+ if (isObject) {
+ if (result.values[3] !== 107)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+ } else {
+ if (result.values[3] !== void 0)
+ throw "Error in loop: bad values[3]: " + result.values[3];
+ }
+}
+
+// Now throw an exception.
+var result;
+try {
+ result = foo(null, 108);
+} catch (e) {
+ result = {outcome: "exception"};
+}
+if (result.outcome !== "exception")
+ throw "Error at end: bad outcome: " + result.outcome;