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/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;