Exits from exceptions shouldn't jettison code
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 29 Jan 2016 19:46:01 +0000 (19:46 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 29 Jan 2016 19:46:01 +0000 (19:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=153564

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

We create two new exit kinds for exception-handling
OSRExits:
- ExceptionCheck: an exception check after a C call.
- GenericUnwind: an OSR exit executes because it's jumped to from genericUnwind machinery.

Having these two new exit kinds allows us to remove fields from
various OSRExit variants that store booleans indicating
if the exit is an exception handler, and if so, what kind
of exception handler. Most of this patch is just removing
those old fields and adding new equivalent functions.

This patch also implements the policy that we should never consider jettisoning
code from exits that happen from an exception check to an op_catch (it might be worth
considering a similar policy for 'throw'). We're choosing this policy because
it will almost never be more expensive, in total, to execute the OSR exit than
to execute the baseline variant of the code. When an exception is thrown, we do
really expensive work, like call through to genericUnwind, and also create an error
object with a stack trace. The cost of OSR exiting here is small in comparison to
those other operations. And penalizing a CodeBlock for OSR exiting from an exception
is silly because the basis of our implementation of exception handling in the
upper tiers is to OSR exit on a caught exception. So we used to penalize
ourselves for having an implementation that is correct w.r.t our design goals.

I've verified this hypothesis with on v8-raytrace by adding a new
benchmark that throws with very high frequency. Implementing
this policy on that benchmark results in about a 4-5% speed up.

* bytecode/ExitKind.cpp:
(JSC::exitKindToString):
(JSC::exitKindMayJettison):
(JSC::exitKindIsCountable): Deleted.
* bytecode/ExitKind.h:
* dfg/DFGJITCode.cpp:
(JSC::DFG::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::noticeOSREntry):
(JSC::DFG::JITCompiler::appendExceptionHandlingOSRExit):
(JSC::DFG::JITCompiler::exceptionCheck):
(JSC::DFG::JITCompiler::recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded):
* dfg/DFGJITCompiler.h:
* dfg/DFGOSRExit.cpp:
(JSC::DFG::OSRExit::OSRExit):
* dfg/DFGOSRExit.h:
(JSC::DFG::OSRExit::considerAddingAsFrequentExitSite):
* dfg/DFGOSRExitBase.h:
(JSC::DFG::OSRExitBase::OSRExitBase):
(JSC::DFG::OSRExitBase::isExceptionHandler):
(JSC::DFG::OSRExitBase::isGenericUnwindHandler):
(JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSite):
* dfg/DFGOSRExitCompiler.cpp:
* dfg/DFGOSRExitCompiler32_64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGOSRExitCompiler64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGOSRExitCompilerCommon.cpp:
(JSC::DFG::handleExitCounts):
(JSC::DFG::osrWriteBarrier):
(JSC::DFG::adjustAndJumpToTarget):
* dfg/DFGOSRExitCompilerCommon.h:
(JSC::DFG::adjustFrameAndStackInOSRExitCompilerThunk):
* ftl/FTLCompile.cpp:
(JSC::FTL::mmAllocateDataSection):
* ftl/FTLExitThunkGenerator.cpp:
(JSC::FTL::ExitThunkGenerator::emitThunk):
* ftl/FTLJITCode.cpp:
(JSC::FTL::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::callCheck):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitArgumentsForPatchpointIfWillCatchException):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
(JSC::FTL::DFG::LowerDFGToLLVM::blessSpeculation):
* ftl/FTLOSRExit.cpp:
(JSC::FTL::OSRExitDescriptor::emitOSRExit):
(JSC::FTL::OSRExitDescriptor::emitOSRExitLater):
(JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle):
(JSC::FTL::OSRExit::OSRExit):
(JSC::FTL::OSRExit::spillRegistersToSpillSlot):
(JSC::FTL::OSRExit::recoverRegistersFromSpillSlot):
(JSC::FTL::OSRExit::willArriveAtExitFromIndirectExceptionCheck):
(JSC::FTL::OSRExit::willArriveAtOSRExitFromCallOperation):
(JSC::FTL::exceptionTypeWillArriveAtOSRExitFromGenericUnwind): Deleted.
(JSC::FTL::OSRExit::willArriveAtOSRExitFromGenericUnwind): Deleted.
* ftl/FTLOSRExit.h:
* ftl/FTLOSRExitCompiler.cpp:
(JSC::FTL::compileStub):
(JSC::FTL::compileFTLOSRExit):
* ftl/FTLPatchpointExceptionHandle.cpp:
(JSC::FTL::PatchpointExceptionHandle::scheduleExitCreation):
(JSC::FTL::PatchpointExceptionHandle::scheduleExitCreationForUnwind):
(JSC::FTL::PatchpointExceptionHandle::PatchpointExceptionHandle):
(JSC::FTL::PatchpointExceptionHandle::createHandle):
* ftl/FTLPatchpointExceptionHandle.h:

LayoutTests:

* js/regress/script-tests/v8-raytrace-with-try-catch-high-frequency-throws.js: Added.
(randomException):
(Class.create):
(Object.extend):
(Flog.RayTracer.Color.prototype.initialize):
(Flog.RayTracer.Color.prototype.add):
(Flog.RayTracer.Color.prototype.addScalar):
(Flog.RayTracer.Color.prototype.subtract):
(Flog.RayTracer.Color.prototype.multiply):
(Flog.RayTracer.Color.prototype.multiplyScalar):
(Flog.RayTracer.Color.prototype.divideFactor):
(Flog.RayTracer.Color.prototype.limit):
(Flog.RayTracer.Color.prototype.distance):
(Flog.RayTracer.Color.prototype.blend):
(Flog.RayTracer.Color.prototype.brightness):
(Flog.RayTracer.Color.prototype.toString):
(Flog.RayTracer.Light.prototype.initialize):
(Flog.RayTracer.Light.prototype.toString):
(Flog.RayTracer.Vector.prototype.initialize):
(Flog.RayTracer.Vector.prototype.copy):
(Flog.RayTracer.Vector.prototype.normalize):
(Flog.RayTracer.Vector.prototype.magnitude):
(Flog.RayTracer.Vector.prototype.cross):
(Flog.RayTracer.Vector.prototype.dot):
(Flog.RayTracer.Vector.prototype.add):
(Flog.RayTracer.Vector.prototype.subtract):
(Flog.RayTracer.Vector.prototype.multiplyVector):
(Flog.RayTracer.Vector.prototype.multiplyScalar):
(Flog.RayTracer.Vector.prototype.toString):
(Flog.RayTracer.Ray.prototype.initialize):
(Flog.RayTracer.Ray.prototype.toString):
(Flog.RayTracer.Scene.prototype.initialize):
(Flog.RayTracer.Material.BaseMaterial.prototype.initialize):
(Flog.RayTracer.Material.BaseMaterial.prototype.getColor):
(Flog.RayTracer.Material.BaseMaterial.prototype.wrapUp):
(Flog.RayTracer.Material.BaseMaterial.prototype.toString):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
(Flog.RayTracer.Shape.Sphere.prototype.initialize):
(Flog.RayTracer.Shape.Sphere.prototype.intersect):
(Flog.RayTracer.Shape.Sphere.prototype.toString):
(Flog.RayTracer.Shape.Plane.prototype.initialize):
(Flog.RayTracer.Shape.Plane.prototype.intersect):
(Flog.RayTracer.Shape.Plane.prototype.toString):
(Flog.RayTracer.IntersectionInfo.prototype.initialize):
(Flog.RayTracer.IntersectionInfo.prototype.toString):
(Flog.RayTracer.Camera.prototype.initialize):
(Flog.RayTracer.Camera.prototype.getRay):
(Flog.RayTracer.Camera.prototype.toString):
(Flog.RayTracer.Background.prototype.initialize):
(Flog.RayTracer.Engine.prototype.initialize):
(Flog.RayTracer.Engine.prototype.setPixel):
(Flog.RayTracer.Engine.prototype.renderScene):
(Flog.RayTracer.Engine.prototype.getPixelColor):
(Flog.RayTracer.Engine.prototype.testIntersection):
(Flog.RayTracer.Engine.prototype.getReflectionRay):
(Flog.RayTracer.Engine.prototype.rayTrace):
(renderScene):
* js/regress/v8-raytrace-with-try-catch-high-frequency-throws-expected.txt: Added.
* js/regress/v8-raytrace-with-try-catch-high-frequency-throws.html: Added.

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/script-tests/v8-raytrace-with-try-catch-high-frequency-throws.js [new file with mode: 0644]
LayoutTests/js/regress/v8-raytrace-with-try-catch-high-frequency-throws-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/v8-raytrace-with-try-catch-high-frequency-throws.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/ExitKind.cpp
Source/JavaScriptCore/bytecode/ExitKind.h
Source/JavaScriptCore/dfg/DFGJITCode.cpp
Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
Source/JavaScriptCore/dfg/DFGJITCompiler.h
Source/JavaScriptCore/dfg/DFGOSRExit.cpp
Source/JavaScriptCore/dfg/DFGOSRExit.h
Source/JavaScriptCore/dfg/DFGOSRExitBase.h
Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
Source/JavaScriptCore/ftl/FTLCompile.cpp
Source/JavaScriptCore/ftl/FTLExitThunkGenerator.cpp
Source/JavaScriptCore/ftl/FTLJITCode.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/ftl/FTLOSRExit.cpp
Source/JavaScriptCore/ftl/FTLOSRExit.h
Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
Source/JavaScriptCore/ftl/FTLPatchpointExceptionHandle.cpp
Source/JavaScriptCore/ftl/FTLPatchpointExceptionHandle.h

index 6ee9366..0991971 100644 (file)
@@ -1,3 +1,77 @@
+2016-01-29  Saam barati  <sbarati@apple.com>
+
+        Exits from exceptions shouldn't jettison code
+        https://bugs.webkit.org/show_bug.cgi?id=153564
+
+        Reviewed by Geoffrey Garen.
+
+        * js/regress/script-tests/v8-raytrace-with-try-catch-high-frequency-throws.js: Added.
+        (randomException):
+        (Class.create):
+        (Object.extend):
+        (Flog.RayTracer.Color.prototype.initialize):
+        (Flog.RayTracer.Color.prototype.add):
+        (Flog.RayTracer.Color.prototype.addScalar):
+        (Flog.RayTracer.Color.prototype.subtract):
+        (Flog.RayTracer.Color.prototype.multiply):
+        (Flog.RayTracer.Color.prototype.multiplyScalar):
+        (Flog.RayTracer.Color.prototype.divideFactor):
+        (Flog.RayTracer.Color.prototype.limit):
+        (Flog.RayTracer.Color.prototype.distance):
+        (Flog.RayTracer.Color.prototype.blend):
+        (Flog.RayTracer.Color.prototype.brightness):
+        (Flog.RayTracer.Color.prototype.toString):
+        (Flog.RayTracer.Light.prototype.initialize):
+        (Flog.RayTracer.Light.prototype.toString):
+        (Flog.RayTracer.Vector.prototype.initialize):
+        (Flog.RayTracer.Vector.prototype.copy):
+        (Flog.RayTracer.Vector.prototype.normalize):
+        (Flog.RayTracer.Vector.prototype.magnitude):
+        (Flog.RayTracer.Vector.prototype.cross):
+        (Flog.RayTracer.Vector.prototype.dot):
+        (Flog.RayTracer.Vector.prototype.add):
+        (Flog.RayTracer.Vector.prototype.subtract):
+        (Flog.RayTracer.Vector.prototype.multiplyVector):
+        (Flog.RayTracer.Vector.prototype.multiplyScalar):
+        (Flog.RayTracer.Vector.prototype.toString):
+        (Flog.RayTracer.Ray.prototype.initialize):
+        (Flog.RayTracer.Ray.prototype.toString):
+        (Flog.RayTracer.Scene.prototype.initialize):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.initialize):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.getColor):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.wrapUp):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.toString):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
+        (Flog.RayTracer.Shape.Sphere.prototype.initialize):
+        (Flog.RayTracer.Shape.Sphere.prototype.intersect):
+        (Flog.RayTracer.Shape.Sphere.prototype.toString):
+        (Flog.RayTracer.Shape.Plane.prototype.initialize):
+        (Flog.RayTracer.Shape.Plane.prototype.intersect):
+        (Flog.RayTracer.Shape.Plane.prototype.toString):
+        (Flog.RayTracer.IntersectionInfo.prototype.initialize):
+        (Flog.RayTracer.IntersectionInfo.prototype.toString):
+        (Flog.RayTracer.Camera.prototype.initialize):
+        (Flog.RayTracer.Camera.prototype.getRay):
+        (Flog.RayTracer.Camera.prototype.toString):
+        (Flog.RayTracer.Background.prototype.initialize):
+        (Flog.RayTracer.Engine.prototype.initialize):
+        (Flog.RayTracer.Engine.prototype.setPixel):
+        (Flog.RayTracer.Engine.prototype.renderScene):
+        (Flog.RayTracer.Engine.prototype.getPixelColor):
+        (Flog.RayTracer.Engine.prototype.testIntersection):
+        (Flog.RayTracer.Engine.prototype.getReflectionRay):
+        (Flog.RayTracer.Engine.prototype.rayTrace):
+        (renderScene):
+        * js/regress/v8-raytrace-with-try-catch-high-frequency-throws-expected.txt: Added.
+        * js/regress/v8-raytrace-with-try-catch-high-frequency-throws.html: Added.
+
 2016-01-29  Carlos Alberto Lopez Perez  <clopez@igalia.com>
 
         [GTK] Unreviewed gardening after r195740 (v2).
diff --git a/LayoutTests/js/regress/script-tests/v8-raytrace-with-try-catch-high-frequency-throws.js b/LayoutTests/js/regress/script-tests/v8-raytrace-with-try-catch-high-frequency-throws.js
new file mode 100644 (file)
index 0000000..00e2963
--- /dev/null
@@ -0,0 +1,1101 @@
+// 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/
+
+let __exceptionCounter = 0;
+function randomException() {
+    __exceptionCounter++;
+    if (__exceptionCounter % 35 === 0) {
+        throw new Error("rando");
+    }
+}
+noInline(randomException);
+
+var Class = {
+    create: function() {
+        return function() {
+            try {
+                this.initialize.apply(this, arguments);
+                randomException();
+            } catch(e) { }
+        }
+    }
+};
+
+
+Object.extend = function(destination, source) {
+    for (var property in source) {
+        try {
+            destination[property] = source[property];
+            randomException();
+        } catch(e) { }
+    }
+    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) {
+        try {
+            if(!r) r = 0.0;
+            if(!g) g = 0.0;
+            if(!b) b = 0.0;
+
+            this.red = r;
+            this.green = g;
+            this.blue = b;
+            randomException();
+        } catch(e) { }
+    },
+
+    add : function(c1, c2){
+        try {
+            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;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    addScalar: function(c1, s){
+        try {
+            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();
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    subtract: function(c1, c2){
+        try {
+            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;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    multiply : function(c1, c2) {
+        try {
+            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;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    multiplyScalar : function(c1, f) {
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red * f;
+            result.green = c1.green * f;
+            result.blue = c1.blue * f;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    divideFactor : function(c1, f) {
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red / f;
+            result.green = c1.green / f;
+            result.blue = c1.blue / f;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    limit: function(){
+        try { 
+            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;
+
+            randomException();
+        } catch(e) { }
+    },
+
+    distance : function(color) {
+        try {
+            var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
+            randomException();
+        } catch(e) { }
+        return d;
+    },
+
+    blend: function(c1, c2, w){
+        try {
+            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)
+                    );
+            randomException();
+        } catch(e) { }
+        return result;
+    },
+
+    brightness : function() {
+        try {
+            var r = Math.floor(this.red*255);
+            var g = Math.floor(this.green*255);
+            var b = Math.floor(this.blue*255);
+            randomException();
+        } catch(e) { }
+        return (r * 77 + g * 150 + b * 29) >> 8;
+    },
+
+    toString : function () {
+        try {
+            var r = Math.floor(this.red*255);
+            var g = Math.floor(this.green*255);
+            var b = Math.floor(this.blue*255);
+            randomException();
+        } catch(e) { }
+
+        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) {
+        try {
+            this.position = pos;
+            this.color = color;
+            this.intensity = (intensity ? intensity : 10.0);
+
+            randomException();
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            var result = 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
+            randomException();
+        } catch(e) { }
+        return result;
+    }
+}
+/* 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) {
+        try {
+            this.x = (x ? x : 0);
+            this.y = (y ? y : 0);
+            this.z = (z ? z : 0);
+            randomException();
+        } catch(e) { }
+    },
+
+    copy: function(vector){
+        try {
+            this.x = vector.x;
+            this.y = vector.y;
+            this.z = vector.z;
+            randomException();
+        } catch(e) { }
+    },
+
+    normalize : function() {
+        try {
+            var m = this.magnitude();
+            var result = new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
+            randomException();
+        } catch(e) { }
+        return result;
+    },
+
+    magnitude : function() {
+        try {
+            return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
+        } catch(e)  { }
+    },
+
+    cross : function(w) {
+        try {
+            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);
+        } catch(e) { }
+    },
+
+    dot : function(w) {
+        try {
+            return this.x * w.x + this.y * w.y + this.z * w.z;
+        } catch(e) { }
+    },
+
+    add : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
+        } catch(e) { }
+    },
+
+    subtract : function(v, w) {
+        try {
+            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);
+        } catch(e) { }
+    },
+
+    multiplyVector : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
+        } catch(e) { }
+    },
+
+    multiplyScalar : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
+        } catch(e) { }
+    }
+}
+/* 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) {
+        try {
+            this.position = pos;
+            this.direction = dir;
+            randomException();
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Ray [' + this.position + ',' + this.direction + ']';
+        } catch(e) { }
+    }
+}
+/* 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() {
+        try {
+            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);
+
+            randomException();
+        } catch(e) { }
+    }
+}
+/* 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){
+        try {
+            t = t % 2.0;
+            if(t < -1) t += 2.0;
+            if(t >= 1) t -= 2.0;
+            randomException();
+        } catch(e) { }
+        return t;
+    },
+
+    toString : function () {
+        try {
+            return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+        } catch(e) { }
+    }
+}
+/* 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) {
+                try {
+                    this.color = color;
+                    this.reflection = reflection;
+                    this.transparency = transparency;
+                    this.gloss = gloss;
+                    this.hasTexture = false;
+                    randomException();
+                } catch(e) { }
+            },
+
+            getColor: function(u, v){
+                try {
+                    return this.color;
+                } catch(e) { }
+            },
+
+            toString : function () {
+                try {
+                    return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+                } catch(e) { }
+            }
+        }
+        );
+/* 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) {
+                try {
+                    this.colorEven = colorEven;
+                    this.colorOdd = colorOdd;
+                    this.reflection = reflection;
+                    this.transparency = transparency;
+                    this.gloss = gloss;
+                    this.density = density;
+                    this.hasTexture = true;
+                    randomException();
+                } catch(e) { }
+            },
+
+            getColor: function(u, v){
+                try {
+                    var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
+                    randomException();
+                } catch(e) { }
+
+                if(t < 0.0)
+                    return this.colorEven;
+                else
+                    return this.colorOdd;
+            },
+
+            toString : function () {
+                try {
+                    return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+                } catch(e) { }
+            }
+        }
+);
+/* 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) {
+        try {
+            this.radius = radius;
+            this.position = pos;
+            this.material = material;
+
+            randomException();
+        } catch(e) { }
+    },
+
+    intersect: function(ray){
+        try {
+            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;
+            }
+
+            randomException();
+        } catch(e) { }
+        return info;
+    },
+
+    toString : function () {
+        try {
+            return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
+        } catch(e) { }
+    }
+}
+/* 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) {
+        try {
+            this.position = pos;
+            this.d = d;
+            this.material = material;
+            randomException();
+        } catch(e) { }
+    },
+
+    intersect: function(ray){
+        try {
+            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);
+            }
+
+            randomException();
+        } catch(e) { }
+        return info;
+    },
+
+    toString : function () {
+        try {
+            return 'Plane [' + this.position + ', d=' + this.d + ']';
+        } catch(e) { }
+    }
+}
+/* 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() {
+        try {
+            this.color = new Flog.RayTracer.Color(0,0,0);
+            randomException();
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Intersection [' + this.position + ']';
+        } catch(e) { }
+    }
+}
+/* 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) {
+        try {
+            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);
+            randomException();
+        } catch(e) { }
+    },
+
+    getRay: function(vx, vy){
+        try {
+            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());
+
+            randomException();
+        } catch(e) { }
+        return ray;
+    },
+
+    toString : function () {
+        try {
+            return 'Ray []';
+        } catch(e) { }
+    }
+}
+/* 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) {
+        try {
+            this.color = color;
+            this.ambience = ambience;
+            randomException();
+        } catch(e) { }
+    }
+}
+/* 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){
+        try {
+            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;
+
+            randomException();
+        } catch(e) { }
+
+        /* TODO: dynamically include other scripts */
+    },
+
+    setPixel: function(x, y, color){
+        try {
+            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);
+            }
+
+            randomException();
+        } catch(e) { }
+    },
+
+    renderScene: function(scene, canvas){
+        try {
+            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++){
+                    try {
+                        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);
+
+                        randomException();
+                    } catch(e) { }
+                }
+            }
+        } catch(e) { }
+        if (checkNumber !== 2321) {
+            throw new Error("Scene rendered incorrectly");
+        }
+    },
+
+    getPixelColor: function(ray, scene){
+        try {
+            var info = this.testIntersection(ray, scene, null);
+            if(info.isHit){
+                var color = this.rayTrace(info, ray, scene, 0);
+                return color;
+            }
+            return scene.background.color;
+        } catch(e) { }
+    },
+
+    testIntersection: function(ray, scene, exclude){
+        try {
+            var hits = 0;
+            var best = new Flog.RayTracer.IntersectionInfo();
+            best.distance = 2000;
+
+            for(var i=0; i<scene.shapes.length; i++){
+                try {
+                    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++;
+                        }
+                    }
+
+                    randomException();
+                } catch(e) { }
+            }
+            best.hitCount = hits;
+
+            randomException();
+        } catch(e) { }
+        return best;
+    },
+
+    getReflectionRay: function(P,N,V){
+        try {
+            var c1 = -N.dot(V);
+            var R1 = Flog.RayTracer.Vector.prototype.add(
+                    Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
+                    V
+                    );
+
+            randomException();
+        } catch(e) { }
+        return new Flog.RayTracer.Ray(P, R1);
+    },
+
+    rayTrace: function(info, ray, scene, depth){
+        // Calc ambient
+        try {
+            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++){
+                try {
+                    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
+                                            )
+                                        )
+                                    );
+                        }
+                    }
+
+                    randomException();
+                } catch(e) { }
+
+                try {
+                    // 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 */
+                    }
+                    randomException();
+                }  catch(e) { }
+
+                /* 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);
+                    }
+                }
+
+                try {
+                    // 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
+                                );
+                    }
+                    randomException();
+                } catch(e) { }
+            }
+            color.limit();
+
+            randomException();
+        } catch(e) { }
+        return color;
+    }
+};
+
+
+function renderScene(){
+    try {
+        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);
+        randomException();
+    } catch(e) { }
+}
+
+for (var i = 0; i < 6; ++i)
+    renderScene();
diff --git a/LayoutTests/js/regress/v8-raytrace-with-try-catch-high-frequency-throws-expected.txt b/LayoutTests/js/regress/v8-raytrace-with-try-catch-high-frequency-throws-expected.txt
new file mode 100644 (file)
index 0000000..d4dcce3
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/v8-raytrace-with-try-catch-high-frequency-throws
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/v8-raytrace-with-try-catch-high-frequency-throws.html b/LayoutTests/js/regress/v8-raytrace-with-try-catch-high-frequency-throws.html
new file mode 100644 (file)
index 0000000..22ebc4b
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/v8-raytrace-with-try-catch-high-frequency-throws.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 3b1c750..dc59fae 100644 (file)
@@ -1,3 +1,103 @@
+2016-01-29  Saam barati  <sbarati@apple.com>
+
+        Exits from exceptions shouldn't jettison code
+        https://bugs.webkit.org/show_bug.cgi?id=153564
+
+        Reviewed by Geoffrey Garen.
+
+        We create two new exit kinds for exception-handling
+        OSRExits:
+        - ExceptionCheck: an exception check after a C call.
+        - GenericUnwind: an OSR exit executes because it's jumped to from genericUnwind machinery.
+
+        Having these two new exit kinds allows us to remove fields from
+        various OSRExit variants that store booleans indicating
+        if the exit is an exception handler, and if so, what kind
+        of exception handler. Most of this patch is just removing
+        those old fields and adding new equivalent functions.
+
+        This patch also implements the policy that we should never consider jettisoning
+        code from exits that happen from an exception check to an op_catch (it might be worth
+        considering a similar policy for 'throw'). We're choosing this policy because
+        it will almost never be more expensive, in total, to execute the OSR exit than
+        to execute the baseline variant of the code. When an exception is thrown, we do
+        really expensive work, like call through to genericUnwind, and also create an error
+        object with a stack trace. The cost of OSR exiting here is small in comparison to
+        those other operations. And penalizing a CodeBlock for OSR exiting from an exception
+        is silly because the basis of our implementation of exception handling in the
+        upper tiers is to OSR exit on a caught exception. So we used to penalize
+        ourselves for having an implementation that is correct w.r.t our design goals.
+
+        I've verified this hypothesis with on v8-raytrace by adding a new 
+        benchmark that throws with very high frequency. Implementing
+        this policy on that benchmark results in about a 4-5% speed up.
+
+        * bytecode/ExitKind.cpp:
+        (JSC::exitKindToString):
+        (JSC::exitKindMayJettison):
+        (JSC::exitKindIsCountable): Deleted.
+        * bytecode/ExitKind.h:
+        * dfg/DFGJITCode.cpp:
+        (JSC::DFG::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
+        * dfg/DFGJITCompiler.cpp:
+        (JSC::DFG::JITCompiler::noticeOSREntry):
+        (JSC::DFG::JITCompiler::appendExceptionHandlingOSRExit):
+        (JSC::DFG::JITCompiler::exceptionCheck):
+        (JSC::DFG::JITCompiler::recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded):
+        * dfg/DFGJITCompiler.h:
+        * dfg/DFGOSRExit.cpp:
+        (JSC::DFG::OSRExit::OSRExit):
+        * dfg/DFGOSRExit.h:
+        (JSC::DFG::OSRExit::considerAddingAsFrequentExitSite):
+        * dfg/DFGOSRExitBase.h:
+        (JSC::DFG::OSRExitBase::OSRExitBase):
+        (JSC::DFG::OSRExitBase::isExceptionHandler):
+        (JSC::DFG::OSRExitBase::isGenericUnwindHandler):
+        (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSite):
+        * dfg/DFGOSRExitCompiler.cpp:
+        * dfg/DFGOSRExitCompiler32_64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGOSRExitCompiler64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGOSRExitCompilerCommon.cpp:
+        (JSC::DFG::handleExitCounts):
+        (JSC::DFG::osrWriteBarrier):
+        (JSC::DFG::adjustAndJumpToTarget):
+        * dfg/DFGOSRExitCompilerCommon.h:
+        (JSC::DFG::adjustFrameAndStackInOSRExitCompilerThunk):
+        * ftl/FTLCompile.cpp:
+        (JSC::FTL::mmAllocateDataSection):
+        * ftl/FTLExitThunkGenerator.cpp:
+        (JSC::FTL::ExitThunkGenerator::emitThunk):
+        * ftl/FTLJITCode.cpp:
+        (JSC::FTL::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::callCheck):
+        (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExitArgumentsForPatchpointIfWillCatchException):
+        (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
+        (JSC::FTL::DFG::LowerDFGToLLVM::blessSpeculation):
+        * ftl/FTLOSRExit.cpp:
+        (JSC::FTL::OSRExitDescriptor::emitOSRExit):
+        (JSC::FTL::OSRExitDescriptor::emitOSRExitLater):
+        (JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle):
+        (JSC::FTL::OSRExit::OSRExit):
+        (JSC::FTL::OSRExit::spillRegistersToSpillSlot):
+        (JSC::FTL::OSRExit::recoverRegistersFromSpillSlot):
+        (JSC::FTL::OSRExit::willArriveAtExitFromIndirectExceptionCheck):
+        (JSC::FTL::OSRExit::willArriveAtOSRExitFromCallOperation):
+        (JSC::FTL::exceptionTypeWillArriveAtOSRExitFromGenericUnwind): Deleted.
+        (JSC::FTL::OSRExit::willArriveAtOSRExitFromGenericUnwind): Deleted.
+        * ftl/FTLOSRExit.h:
+        * ftl/FTLOSRExitCompiler.cpp:
+        (JSC::FTL::compileStub):
+        (JSC::FTL::compileFTLOSRExit):
+        * ftl/FTLPatchpointExceptionHandle.cpp:
+        (JSC::FTL::PatchpointExceptionHandle::scheduleExitCreation):
+        (JSC::FTL::PatchpointExceptionHandle::scheduleExitCreationForUnwind):
+        (JSC::FTL::PatchpointExceptionHandle::PatchpointExceptionHandle):
+        (JSC::FTL::PatchpointExceptionHandle::createHandle):
+        * ftl/FTLPatchpointExceptionHandle.h:
+
 2016-01-28  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [B3] REGRESSION(r195395): testComplex(64, 128) asserts on Linux with GCC
index 30c5e64..84ff57b 100644 (file)
@@ -84,25 +84,27 @@ const char* exitKindToString(ExitKind kind)
         return "WatchdogTimerFired";
     case DebuggerEvent:
         return "DebuggerEvent";
+    case ExceptionCheck:
+        return "ExceptionCheck";
+    case GenericUnwind:
+        return "GenericUnwind";
     }
     RELEASE_ASSERT_NOT_REACHED();
     return "Unknown";
 }
 
-bool exitKindIsCountable(ExitKind kind)
+bool exitKindMayJettison(ExitKind kind)
 {
     switch (kind) {
-    case ExitKindUnset:
-        RELEASE_ASSERT_NOT_REACHED();
-    case BadType:
-    case Uncountable:
-    case LoadFromHole: // Already counted directly by the baseline JIT.
-    case StoreToHole: // Already counted directly by the baseline JIT.
-    case OutOfBounds: // Already counted directly by the baseline JIT.
+    case ExceptionCheck:
+    case GenericUnwind:
         return false;
     default:
         return true;
     }
+
+    RELEASE_ASSERT_NOT_REACHED();
+    return false;
 }
 
 } // namespace JSC
index 700d388..22a54a1 100644 (file)
@@ -53,11 +53,13 @@ enum ExitKind : uint8_t {
     Uncountable, // We exited for none of the above reasons, and we should not count it. Most uses of this should be viewed as a FIXME.
     UncountableInvalidation, // We exited because the code block was invalidated; this means that we've already counted the reasons why the code block was invalidated.
     WatchdogTimerFired, // We exited because we need to service the watchdog timer.
-    DebuggerEvent // We exited because we need to service the debugger.
+    DebuggerEvent, // We exited because we need to service the debugger.
+    ExceptionCheck, // We exited because a direct exception check showed that we threw an exception from a C call.
+    GenericUnwind, // We exited because a we arrived at this OSR exit from genericUnwind.
 };
 
 const char* exitKindToString(ExitKind);
-bool exitKindIsCountable(ExitKind);
+bool exitKindMayJettison(ExitKind);
 
 } // namespace JSC
 
index 48217d3..b4c27bb 100644 (file)
@@ -90,7 +90,7 @@ void JITCode::reconstruct(
 RegisterSet JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite(CodeBlock* codeBlock, CallSiteIndex callSiteIndex)
 {
     for (OSRExit& exit : osrExit) {
-        if (exit.m_isExceptionHandler && exit.m_exceptionHandlerCallSiteIndex.bits() == callSiteIndex.bits()) {
+        if (exit.isExceptionHandler() && exit.m_exceptionHandlerCallSiteIndex.bits() == callSiteIndex.bits()) {
             Operands<ValueRecovery> valueRecoveries;
             reconstruct(codeBlock, exit.m_codeOrigin, exit.m_streamIndex, valueRecoveries);
             RegisterSet liveAtOSRExit;
index ff399e8..3b91e6f 100644 (file)
@@ -541,11 +541,9 @@ void JITCompiler::noticeOSREntry(BasicBlock& basicBlock, JITCompiler::Label bloc
     entry->m_reshufflings.shrinkToFit();
 }
 
-void JITCompiler::appendExceptionHandlingOSRExit(unsigned eventStreamIndex, CodeOrigin opCatchOrigin, HandlerInfo* exceptionHandler, CallSiteIndex callSite, MacroAssembler::JumpList jumpsToFail)
+void JITCompiler::appendExceptionHandlingOSRExit(ExitKind kind, unsigned eventStreamIndex, CodeOrigin opCatchOrigin, HandlerInfo* exceptionHandler, CallSiteIndex callSite, MacroAssembler::JumpList jumpsToFail)
 {
-    OSRExit exit(Uncountable, JSValueRegs(), graph().methodOfGettingAValueProfileFor(nullptr), m_speculative.get(), eventStreamIndex);
-    exit.m_willArriveAtOSRExitFromGenericUnwind = jumpsToFail.empty(); // If jumps are empty, we're going to jump here from genericUnwind from a child call frame.
-    exit.m_isExceptionHandler = true;
+    OSRExit exit(kind, JSValueRegs(), graph().methodOfGettingAValueProfileFor(nullptr), m_speculative.get(), eventStreamIndex);
     exit.m_codeOrigin = opCatchOrigin;
     exit.m_exceptionHandlerCallSiteIndex = callSite;
     OSRExitCompilationInfo& exitInfo = appendExitInfo(jumpsToFail);
@@ -582,7 +580,7 @@ void JITCompiler::exceptionCheck()
         unsigned streamIndex = m_speculative->m_outOfLineStreamIndex != UINT_MAX ? m_speculative->m_outOfLineStreamIndex : m_speculative->m_stream->size();
         MacroAssembler::Jump hadException = emitNonPatchableExceptionCheck();
         // We assume here that this is called after callOpeartion()/appendCall() is called.
-        appendExceptionHandlingOSRExit(streamIndex, opCatchOrigin, exceptionHandler, m_jitCode->common.lastCallSite(), hadException);
+        appendExceptionHandlingOSRExit(ExceptionCheck, streamIndex, opCatchOrigin, exceptionHandler, m_jitCode->common.lastCallSite(), hadException);
     } else
         m_exceptionChecks.append(emitExceptionCheck());
 }
@@ -594,7 +592,7 @@ CallSiteIndex JITCompiler::recordCallSiteAndGenerateExceptionHandlingOSRExitIfNe
     bool willCatchException = m_graph.willCatchExceptionInMachineFrame(callSiteCodeOrigin, opCatchOrigin, exceptionHandler);
     CallSiteIndex callSite = addCallSite(callSiteCodeOrigin);
     if (willCatchException)
-        appendExceptionHandlingOSRExit(eventStreamIndex, opCatchOrigin, exceptionHandler, callSite);
+        appendExceptionHandlingOSRExit(GenericUnwind, eventStreamIndex, opCatchOrigin, exceptionHandler, callSite);
     return callSite;
 }
 
index 258dfa3..4476fe8 100644 (file)
@@ -279,7 +279,7 @@ private:
     void linkOSRExits();
     void disassemble(LinkBuffer&);
 
-    void appendExceptionHandlingOSRExit(unsigned eventStreamIndex, CodeOrigin, HandlerInfo* exceptionHandler, CallSiteIndex, MacroAssembler::JumpList jumpsToFail = MacroAssembler::JumpList());
+    void appendExceptionHandlingOSRExit(ExitKind, unsigned eventStreamIndex, CodeOrigin, HandlerInfo* exceptionHandler, CallSiteIndex, MacroAssembler::JumpList jumpsToFail = MacroAssembler::JumpList());
 
     // The dataflow graph currently being generated.
     Graph& m_graph;
index 2e1e51c..b95d409 100644 (file)
@@ -43,7 +43,6 @@ OSRExit::OSRExit(ExitKind kind, JSValueSource jsValueSource, MethodOfGettingAVal
     , m_patchableCodeOffset(0)
     , m_recoveryIndex(recoveryIndex)
     , m_streamIndex(streamIndex)
-    , m_willArriveAtOSRExitFromGenericUnwind(false)
 {
     bool canExit = jit->m_origin.exitOK;
     if (!canExit && jit->m_currentNode) {
index bde57aa..a14d54f 100644 (file)
@@ -100,7 +100,6 @@ struct OSRExit : public OSRExitBase {
     void correctJump(LinkBuffer&);
 
     unsigned m_streamIndex;
-    bool m_willArriveAtOSRExitFromGenericUnwind : 1;
     void considerAddingAsFrequentExitSite(CodeBlock* profiledCodeBlock)
     {
         OSRExitBase::considerAddingAsFrequentExitSite(profiledCodeBlock, ExitFromDFG);
index eafd9c7..78510c1 100644 (file)
@@ -45,7 +45,6 @@ struct OSRExitBase {
         , m_count(0)
         , m_codeOrigin(origin)
         , m_codeOriginForExitProfile(originForProfile)
-        , m_isExceptionHandler(false)
     {
         ASSERT(m_codeOrigin.isSet());
         ASSERT(m_codeOriginForExitProfile.isSet());
@@ -58,7 +57,18 @@ struct OSRExitBase {
     CodeOrigin m_codeOriginForExitProfile;
     CallSiteIndex m_exceptionHandlerCallSiteIndex;
 
-    bool m_isExceptionHandler : 1;
+    ALWAYS_INLINE bool isExceptionHandler() const
+    {
+        return m_kind == ExceptionCheck || m_kind == GenericUnwind;
+    }
+
+    // True if this exit is used as an exception handler for unwinding. This happens to only be set when
+    // isExceptionHandler is true, but all this actually means is that the OSR exit will assume that the
+    // machine state is as it would be coming out of genericUnwind.
+    ALWAYS_INLINE bool isGenericUnwindHandler() const
+    {
+        return m_kind == GenericUnwind;
+    }
 
 protected:
     void considerAddingAsFrequentExitSite(CodeBlock* profiledCodeBlock, ExitingJITType jitType)
index 5f94132..1f3f98b 100644 (file)
@@ -131,8 +131,8 @@ void compileOSRExit(ExecState* exec)
     OSRExit& exit = codeBlock->jitCode()->dfg()->osrExit[exitIndex];
     
     if (vm->callFrameForCatch)
-        ASSERT(exit.m_willArriveAtOSRExitFromGenericUnwind);
-    if (exit.m_isExceptionHandler)
+        ASSERT(exit.m_kind == GenericUnwind);
+    if (exit.isExceptionHandler())
         ASSERT(!!vm->exception());
         
     
@@ -150,7 +150,7 @@ void compileOSRExit(ExecState* exec)
         CCallHelpers jit(vm, codeBlock);
         OSRExitCompiler exitCompiler(jit);
 
-        if (exit.m_willArriveAtOSRExitFromGenericUnwind) {
+        if (exit.m_kind == GenericUnwind) {
             // We are acting as a defacto op_catch because we arrive here from genericUnwind().
             // So, we must restore our call frame and stack pointer.
             jit.restoreCalleeSavesFromVMCalleeSavesBuffer();
index 01b8518..e419941 100644 (file)
@@ -248,7 +248,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
     m_jit.emitRestoreCalleeSaves();
     m_jit.emitSaveCalleeSavesFor(m_jit.baselineCodeBlock());
 
-    if (exit.m_isExceptionHandler)
+    if (exit.isExceptionHandler())
         m_jit.copyCalleeSavesToVMCalleeSavesBuffer();
 
     // Do all data format conversions and store the results into the stack.
@@ -397,7 +397,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
     reifyInlinedCallFrames(m_jit, exit);
     
     // And finish.
-    adjustAndJumpToTarget(m_jit, exit, exit.m_isExceptionHandler);
+    adjustAndJumpToTarget(m_jit, exit);
 }
 
 } } // namespace JSC::DFG
index b223812..6999e5c 100644 (file)
@@ -261,7 +261,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
     // The tag registers are needed to materialize recoveries below.
     m_jit.emitMaterializeTagCheckRegisters();
 
-    if (exit.m_isExceptionHandler)
+    if (exit.isExceptionHandler())
         m_jit.copyCalleeSavesToVMCalleeSavesBuffer();
 
     // Do all data format conversions and store the results into the stack.
@@ -387,7 +387,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
     reifyInlinedCallFrames(m_jit, exit);
 
     // And finish.
-    adjustAndJumpToTarget(m_jit, exit, exit.m_isExceptionHandler);
+    adjustAndJumpToTarget(m_jit, exit);
 }
 
 } } // namespace JSC::DFG
index e8479e8..f716e76 100644 (file)
@@ -39,6 +39,9 @@ namespace JSC { namespace DFG {
 void handleExitCounts(CCallHelpers& jit, const OSRExitBase& exit)
 {
     jit.add32(AssemblyHelpers::TrustedImm32(1), AssemblyHelpers::AbsoluteAddress(&exit.m_count));
+
+    if (!exitKindMayJettison(exit.m_kind))
+        return;
     
     jit.move(AssemblyHelpers::TrustedImmPtr(jit.codeBlock()), GPRInfo::regT0);
     
@@ -264,7 +267,7 @@ static void osrWriteBarrier(CCallHelpers& jit, GPRReg owner, GPRReg scratch)
     ownerIsRememberedOrInEden.link(&jit);
 }
 
-void adjustAndJumpToTarget(CCallHelpers& jit, const OSRExitBase& exit, bool isExitingToOpCatch)
+void adjustAndJumpToTarget(CCallHelpers& jit, const OSRExitBase& exit)
 {
     jit.move(
         AssemblyHelpers::TrustedImmPtr(
@@ -302,7 +305,7 @@ void adjustAndJumpToTarget(CCallHelpers& jit, const OSRExitBase& exit, bool isEx
     void* jumpTarget = codeBlockForExit->jitCode()->executableAddressAtOffset(mapping->m_machineCodeOffset);
 
     jit.addPtr(AssemblyHelpers::TrustedImm32(JIT::stackPointerOffsetFor(codeBlockForExit) * sizeof(Register)), GPRInfo::callFrameRegister, AssemblyHelpers::stackPointerRegister);
-    if (isExitingToOpCatch) {
+    if (exit.isExceptionHandler()) {
         // Since we're jumping to op_catch, we need to set callFrameForCatch.
         jit.storePtr(GPRInfo::callFrameRegister, jit.vm()->addressOfCallFrameForCatch());
     }
index 6824bf0..bbb22e0 100644 (file)
@@ -39,7 +39,7 @@ namespace JSC { namespace DFG {
 
 void handleExitCounts(CCallHelpers&, const OSRExitBase&);
 void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
-void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&, bool isExitingToOpCatch);
+void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
 
 template <typename JITCodeType>
 void adjustFrameAndStackInOSRExitCompilerThunk(MacroAssembler& jit, VM* vm, JITCode::JITType jitType)
index a790855..490e609 100644 (file)
@@ -492,7 +492,7 @@ static void fixFunctionBasedOnStackMaps(
         }
 
         OSRExitDescriptorImpl& exitDescriptorImpl = state.osrExitDescriptorImpls[i];
-        if (exceptionTypeWillArriveAtOSRExitFromGenericUnwind(exitDescriptorImpl.m_exceptionType))
+        if (exitDescriptorImpl.m_kind == GenericUnwind)
             genericUnwindOSRExitDescriptors.add(exitDescriptor, &exitDescriptorImpl);
 
         for (unsigned j = exitDescriptor->m_values.size(); j--;)
@@ -503,7 +503,7 @@ static void fixFunctionBasedOnStackMaps(
         for (unsigned j = 0; j < iter->value.size(); j++) {
             {
                 uint32_t stackmapRecordIndex = iter->value[j].index;
-                OSRExit exit(exitDescriptor, exitDescriptorImpl, stackmapRecordIndex);
+                OSRExit exit(exitDescriptor, exitDescriptorImpl.m_kind, exitDescriptorImpl, stackmapRecordIndex);
                 state.jitCode->osrExit.append(exit);
                 state.finalizer->osrExit.append(OSRExitCompilationInfo());
             }
@@ -528,7 +528,7 @@ static void fixFunctionBasedOnStackMaps(
                     // and the other that will be arrived at from the callOperation exception handler path.
                     // This code here generates the second callOperation variant.
                     uint32_t stackmapRecordIndex = iter->value[j].index;
-                    OSRExit exit(exitDescriptor, exitDescriptorImpl, stackmapRecordIndex);
+                    OSRExit exit(exitDescriptor, ExceptionCheck, exitDescriptorImpl, stackmapRecordIndex);
                     if (exitDescriptorImpl.m_exceptionType == ExceptionType::GetById)
                         exit.m_exceptionType = ExceptionType::GetByIdCallOperation;
                     else
@@ -590,7 +590,7 @@ static void fixFunctionBasedOnStackMaps(
             info.m_thunkAddress = linkBuffer->locationOf(info.m_thunkLabel);
             exit.m_patchableCodeOffset = linkBuffer->offsetOf(info.m_thunkJump);
 
-            if (exit.willArriveAtOSRExitFromGenericUnwind()) {
+            if (exit.isGenericUnwindHandler()) {
                 HandlerInfo newHandler = genericUnwindOSRExitDescriptors.get(exit.m_descriptor)->m_baselineExceptionHandler;
                 newHandler.start = exit.m_exceptionHandlerCallSiteIndex.bits();
                 newHandler.end = exit.m_exceptionHandlerCallSiteIndex.bits() + 1;
index 0778268..d1bb2db 100644 (file)
@@ -49,7 +49,7 @@ ExitThunkGenerator::~ExitThunkGenerator()
 void ExitThunkGenerator::emitThunk(unsigned index)
 {
     OSRExit& exit = m_state.jitCode->osrExit[index];
-    ASSERT_UNUSED(exit, !(exit.willArriveAtOSRExitFromGenericUnwind() && exit.willArriveAtOSRExitFromCallOperation()));
+    ASSERT_UNUSED(exit, !(exit.isGenericUnwindHandler() && exit.willArriveAtOSRExitFromCallOperation()));
     
     OSRExitCompilationInfo& info = m_state.finalizer->osrExit[index];
     info.m_thunkLabel = label();
index 13fb0e6..2c50b15 100644 (file)
@@ -170,15 +170,15 @@ RegisterSet JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite(CodeBloc
 #if FTL_USES_B3
     for (OSRExit& exit : osrExit) {
         if (exit.m_exceptionHandlerCallSiteIndex.bits() == callSiteIndex.bits()) {
-            RELEASE_ASSERT(exit.m_isExceptionHandler);
-            RELEASE_ASSERT(exit.m_isUnwindHandler);
+            RELEASE_ASSERT(exit.isExceptionHandler());
+            RELEASE_ASSERT(exit.isGenericUnwindHandler());
             return ValueRep::usedRegisters(exit.m_valueReps);
         }
     }
 #else // FTL_USES_B3
     for (OSRExit& exit : osrExit) {
         if (exit.m_exceptionHandlerCallSiteIndex.bits() == callSiteIndex.bits()) {
-            RELEASE_ASSERT(exit.m_isExceptionHandler);
+            RELEASE_ASSERT(exit.isExceptionHandler());
             return stackmaps.records[exit.m_stackmapRecordIndex].usedRegisterSet();
         }
     }
index 5604840..412026a 100644 (file)
@@ -10186,7 +10186,7 @@ private:
             bool exitOK = true;
             bool isExceptionHandler = true;
             appendOSRExit(
-                Uncountable, noValue(), nullptr, hadException,
+                ExceptionCheck, noValue(), nullptr, hadException,
                 m_origin.withForExitAndExitOK(opCatchOrigin, exitOK), isExceptionHandler);
             return;
         }
@@ -10244,7 +10244,22 @@ private:
         if (!willCatchException)
             return;
 
-        appendOSRExitDescriptor(Uncountable, exceptionType, noValue(), nullptr, m_origin.withForExitAndExitOK(opCatchOrigin, true));
+        ExitKind exitKind;
+        switch (exceptionType) {
+        case ExceptionType::JSCall:
+        case ExceptionType::GetById:
+        case ExceptionType::PutById:
+            exitKind = GenericUnwind;
+            break;
+        case ExceptionType::LazySlowPath:
+        case ExceptionType::BinaryOpGenerator:
+            exitKind = ExceptionCheck;
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        appendOSRExitDescriptor(exitKind, exceptionType, noValue(), nullptr, m_origin.withForExitAndExitOK(opCatchOrigin, true));
         OSRExitDescriptor* exitDescriptor = &m_ftlState.jitCode->osrExitDescriptors.last();
         exitDescriptor->m_stackmapID = m_stackmapIDs - 1;
 
@@ -10320,7 +10335,7 @@ private:
 
 #if FTL_USES_B3
         blessSpeculation(
-            m_out.speculate(failCondition), kind, lowValue, highValue, origin, isExceptionHandler);
+            m_out.speculate(failCondition), kind, lowValue, highValue, origin);
 #else // FTL_USES_B3
         OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(kind, isExceptionHandler ? ExceptionType::CCallException : ExceptionType::None, lowValue, highValue, origin);
 
@@ -10348,7 +10363,7 @@ private:
     }
 
 #if FTL_USES_B3
-    void blessSpeculation(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)
     {
         OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(lowValue, highValue);
         
@@ -10358,7 +10373,7 @@ private:
         value->setGenerator(
             [=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
                 exitDescriptor->emitOSRExit(
-                    *state, kind, origin, jit, params, 0, isExceptionHandler);
+                    *state, kind, origin, jit, params, 0);
             });
     }
 #endif
index 59e1f44..ffbb637 100644 (file)
@@ -69,20 +69,20 @@ void OSRExitDescriptor::validateReferences(const TrackedReferences& trackedRefer
 #if FTL_USES_B3
 RefPtr<OSRExitHandle> OSRExitDescriptor::emitOSRExit(
     State& state, ExitKind exitKind, const NodeOrigin& nodeOrigin, CCallHelpers& jit,
-    const StackmapGenerationParams& params, unsigned offset, bool isExceptionHandler)
+    const StackmapGenerationParams& params, unsigned offset)
 {
     RefPtr<OSRExitHandle> handle =
-        prepareOSRExitHandle(state, exitKind, nodeOrigin, params, offset, isExceptionHandler);
+        prepareOSRExitHandle(state, exitKind, nodeOrigin, params, offset);
     handle->emitExitThunk(state, jit);
     return handle;
 }
 
 RefPtr<OSRExitHandle> OSRExitDescriptor::emitOSRExitLater(
     State& state, ExitKind exitKind, const NodeOrigin& nodeOrigin,
-    const StackmapGenerationParams& params, unsigned offset, bool isExceptionHandler)
+    const StackmapGenerationParams& params, unsigned offset)
 {
     RefPtr<OSRExitHandle> handle =
-        prepareOSRExitHandle(state, exitKind, nodeOrigin, params, offset, isExceptionHandler);
+        prepareOSRExitHandle(state, exitKind, nodeOrigin, params, offset);
     params.addLatePath(
         [handle, &state] (CCallHelpers& jit) {
             handle->emitExitThunk(state, jit);
@@ -92,11 +92,11 @@ RefPtr<OSRExitHandle> OSRExitDescriptor::emitOSRExitLater(
 
 RefPtr<OSRExitHandle> OSRExitDescriptor::prepareOSRExitHandle(
     State& state, ExitKind exitKind, const NodeOrigin& nodeOrigin,
-    const StackmapGenerationParams& params, unsigned offset, bool isExceptionHandler)
+    const StackmapGenerationParams& params, unsigned offset)
 {
     unsigned index = state.jitCode->osrExit.size();
     OSRExit& exit = state.jitCode->osrExit.alloc(
-        this, exitKind, nodeOrigin.forExit, nodeOrigin.semantic, isExceptionHandler);
+        this, exitKind, nodeOrigin.forExit, nodeOrigin.semantic);
     RefPtr<OSRExitHandle> handle = adoptRef(new OSRExitHandle(index, exit));
     for (unsigned i = offset; i < params.size(); ++i)
         exit.m_valueReps.append(params[i]);
@@ -108,23 +108,20 @@ RefPtr<OSRExitHandle> OSRExitDescriptor::prepareOSRExitHandle(
 #if FTL_USES_B3
 OSRExit::OSRExit(
     OSRExitDescriptor* descriptor,
-    ExitKind exitKind, CodeOrigin codeOrigin, CodeOrigin codeOriginForExitProfile,
-    bool isExceptionHandler)
+    ExitKind exitKind, CodeOrigin codeOrigin, CodeOrigin codeOriginForExitProfile)
     : OSRExitBase(exitKind, codeOrigin, codeOriginForExitProfile)
     , m_descriptor(descriptor)
 {
-    m_isExceptionHandler = isExceptionHandler;
 }
 #else // FTL_USES_B3
 OSRExit::OSRExit(
-    OSRExitDescriptor* descriptor, OSRExitDescriptorImpl& exitDescriptorImpl,
+    OSRExitDescriptor* descriptor, ExitKind exitKind, OSRExitDescriptorImpl& exitDescriptorImpl,
     uint32_t stackmapRecordIndex)
-    : OSRExitBase(exitDescriptorImpl.m_kind, exitDescriptorImpl.m_codeOrigin, exitDescriptorImpl.m_codeOriginForExitProfile)
+    : OSRExitBase(exitKind, exitDescriptorImpl.m_codeOrigin, exitDescriptorImpl.m_codeOriginForExitProfile)
     , m_descriptor(descriptor)
     , m_stackmapRecordIndex(stackmapRecordIndex)
     , m_exceptionType(exitDescriptorImpl.m_exceptionType)
 {
-    m_isExceptionHandler = exitDescriptorImpl.m_exceptionType != ExceptionType::None;
 }
 #endif // FTL_USES_B3
 
@@ -178,7 +175,7 @@ void OSRExit::gatherRegistersToSpillForCallIfException(StackMaps& stackmaps, Sta
 
 void OSRExit::spillRegistersToSpillSlot(CCallHelpers& jit, int32_t stackSpillSlot)
 {
-    RELEASE_ASSERT(willArriveAtOSRExitFromGenericUnwind() || willArriveAtOSRExitFromCallOperation());
+    RELEASE_ASSERT(isGenericUnwindHandler() || willArriveAtOSRExitFromCallOperation());
     unsigned count = 0;
     for (GPRReg reg = MacroAssembler::firstRegister(); reg <= MacroAssembler::lastRegister(); reg = MacroAssembler::nextRegister(reg)) {
         if (registersToPreserveForCallThatMightThrow.get(reg)) {
@@ -196,7 +193,7 @@ void OSRExit::spillRegistersToSpillSlot(CCallHelpers& jit, int32_t stackSpillSlo
 
 void OSRExit::recoverRegistersFromSpillSlot(CCallHelpers& jit, int32_t stackSpillSlot)
 {
-    RELEASE_ASSERT(willArriveAtOSRExitFromGenericUnwind() || willArriveAtOSRExitFromCallOperation());
+    RELEASE_ASSERT(isGenericUnwindHandler() || willArriveAtOSRExitFromCallOperation());
     unsigned count = 0;
     for (GPRReg reg = MacroAssembler::firstRegister(); reg <= MacroAssembler::lastRegister(); reg = MacroAssembler::nextRegister(reg)) {
         if (registersToPreserveForCallThatMightThrow.get(reg)) {
@@ -229,24 +226,6 @@ bool OSRExit::willArriveAtExitFromIndirectExceptionCheck() const
     RELEASE_ASSERT_NOT_REACHED();
 }
 
-bool exceptionTypeWillArriveAtOSRExitFromGenericUnwind(ExceptionType exceptionType)
-{
-    switch (exceptionType) {
-    case ExceptionType::JSCall:
-    case ExceptionType::GetById:
-    case ExceptionType::PutById:
-        return true;
-    default:
-        return false;
-    }
-    RELEASE_ASSERT_NOT_REACHED();
-}
-
-bool OSRExit::willArriveAtOSRExitFromGenericUnwind() const
-{
-    return exceptionTypeWillArriveAtOSRExitFromGenericUnwind(m_exceptionType);
-}
-
 bool OSRExit::willArriveAtOSRExitFromCallOperation() const
 {
     switch (m_exceptionType) {
index b45aa0c..bb05d08 100644 (file)
@@ -80,8 +80,6 @@ enum class ExceptionType : uint8_t {
     LazySlowPath,
     BinaryOpGenerator,
 };
-
-bool exceptionTypeWillArriveAtOSRExitFromGenericUnwind(ExceptionType);
 #endif // !FTL_USES_B3
 
 struct OSRExitDescriptor {
@@ -116,7 +114,7 @@ struct OSRExitDescriptor {
     // this call, the OSRExit is simply ready to go.
     RefPtr<OSRExitHandle> emitOSRExit(
         State&, ExitKind, const DFG::NodeOrigin&, CCallHelpers&, const B3::StackmapGenerationParams&,
-        unsigned offset = 0, bool isExceptionHandler = false);
+        unsigned offset = 0);
 
     // In some cases you want an OSRExit to come into existence, but you don't want to emit it right now.
     // This will emit the OSR exit in a late path. You can't be sure exactly when that will happen, but
@@ -128,14 +126,15 @@ struct OSRExitDescriptor {
     // eventually gets access to its label.
     RefPtr<OSRExitHandle> emitOSRExitLater(
         State&, ExitKind, const DFG::NodeOrigin&, const B3::StackmapGenerationParams&,
-        unsigned offset = 0, bool isExceptionHandler = false);
+        unsigned offset = 0);
 
+private:
     // This is the low-level interface. It will create a handle representing the desire to emit code for
     // an OSR exit. You can call OSRExitHandle::emitExitThunk() once you have a place to emit it. Note
     // that the above two APIs are written in terms of this and OSRExitHandle::emitExitThunk().
     RefPtr<OSRExitHandle> prepareOSRExitHandle(
         State&, ExitKind, const DFG::NodeOrigin&, const B3::StackmapGenerationParams&,
-        unsigned offset = 0, bool isExceptionHandler = false);
+        unsigned offset = 0);
 #endif // FTL_USES_B3
 };
 
@@ -160,9 +159,9 @@ struct OSRExitDescriptorImpl {
 
 struct OSRExit : public DFG::OSRExitBase {
     OSRExit(
-        OSRExitDescriptor*,
+        OSRExitDescriptor*, ExitKind,
 #if FTL_USES_B3
-        ExitKind, CodeOrigin, CodeOrigin codeOriginForExitProfile, bool isExceptionHandler
+        CodeOrigin, CodeOrigin codeOriginForExitProfile
 #else // FTL_USES_B3
         OSRExitDescriptorImpl&, uint32_t stackmapRecordIndex
 #endif // FTL_USES_B3
@@ -174,10 +173,6 @@ struct OSRExit : public DFG::OSRExitBase {
     // This tells us where to place a jump.
     CodeLocationJump m_patchableJump;
     Vector<B3::ValueRep> m_valueReps;
-    // True if this exit is used as an exception handler for unwinding. This happens to only be set when
-    // isExceptionHandler is true, but all this actually means is that the OSR exit will assume that the
-    // machine state is as it would be coming out of genericUnwind.
-    bool m_isUnwindHandler { false };
 #else // FTL_USES_B3
     // Offset within the exit stubs of the stub for this exit.
     unsigned m_patchableCodeOffset;
@@ -198,7 +193,6 @@ struct OSRExit : public DFG::OSRExitBase {
     void spillRegistersToSpillSlot(CCallHelpers&, int32_t stackSpillSlot);
     void recoverRegistersFromSpillSlot(CCallHelpers& jit, int32_t stackSpillSlot);
 
-    bool willArriveAtOSRExitFromGenericUnwind() const;
     bool willArriveAtExitFromIndirectExceptionCheck() const;
     bool willArriveAtOSRExitFromCallOperation() const;
     bool needsRegisterRecoveryOnGenericUnwindOSRExitPath() const;
index 8c9194c..0bbae18 100644 (file)
@@ -207,13 +207,7 @@ static void compileStub(
     CCallHelpers jit(vm, codeBlock);
 
     // The first thing we need to do is restablish our frame in the case of an exception.
-    if (
-#if FTL_USES_B3
-        exit.m_isUnwindHandler
-#else // FTL_USES_B3
-        exit.willArriveAtOSRExitFromGenericUnwind()
-#endif // FTL_USES_B3
-        ) {
+    if (exit.isGenericUnwindHandler()) {
         RELEASE_ASSERT(vm->callFrameForCatch); // The first time we hit this exit, like at all other times, this field should be non-null.
         jit.restoreCalleeSavesFromVMCalleeSavesBuffer();
         jit.loadPtr(vm->addressOfCallFrameForCatch(), MacroAssembler::framePointerRegister);
@@ -488,19 +482,19 @@ static void compileStub(
     RegisterAtOffsetList* baselineCalleeSaves = baselineCodeBlock->calleeSaveRegisters();
     RegisterAtOffsetList* vmCalleeSaves = vm->getAllCalleeSaveRegisterOffsets();
     RegisterSet vmCalleeSavesToSkip = RegisterSet::stackRegisters();
-    if (exit.m_isExceptionHandler)
+    if (exit.isExceptionHandler())
         jit.move(CCallHelpers::TrustedImmPtr(vm->calleeSaveRegistersBuffer), GPRInfo::regT1);
 
     for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
         if (!allFTLCalleeSaves.get(reg)) {
-            if (exit.m_isExceptionHandler)
+            if (exit.isExceptionHandler())
                 RELEASE_ASSERT(!vmCalleeSaves->find(reg));
             continue;
         }
         unsigned unwindIndex = codeBlock->calleeSaveRegisters()->indexOf(reg);
         RegisterAtOffset* baselineRegisterOffset = baselineCalleeSaves->find(reg);
         RegisterAtOffset* vmCalleeSave = nullptr; 
-        if (exit.m_isExceptionHandler)
+        if (exit.isExceptionHandler())
             vmCalleeSave = vmCalleeSaves->find(reg);
 
         if (reg.isGPR()) {
@@ -540,7 +534,7 @@ static void compileStub(
         }
     }
 
-    if (exit.m_isExceptionHandler) {
+    if (exit.isExceptionHandler()) {
         RegisterAtOffset* vmCalleeSave = vmCalleeSaves->find(GPRInfo::tagTypeNumberRegister);
         jit.store64(GPRInfo::tagTypeNumberRegister, MacroAssembler::Address(GPRInfo::regT1, vmCalleeSave->offset()));
 
@@ -564,7 +558,7 @@ static void compileStub(
     
     handleExitCounts(jit, exit);
     reifyInlinedCallFrames(jit, exit);
-    adjustAndJumpToTarget(jit, exit, exit.m_isExceptionHandler);
+    adjustAndJumpToTarget(jit, exit);
     
     LinkBuffer patchBuffer(*vm, jit, codeBlock);
 #if FTL_USES_B3
@@ -622,13 +616,11 @@ extern "C" void* compileFTLOSRExit(ExecState* exec, unsigned exitID)
         dataLog("    Exit stackmap ID: ", exit.m_descriptor->m_stackmapID, "\n");
 #endif // !FTL_USES_B3
         dataLog("    Current call site index: ", exec->callSiteIndex().bits(), "\n");
-        dataLog("    Exit is exception handler: ", exit.m_isExceptionHandler, "\n");
-#if FTL_USES_B3
-        dataLog("    Is unwind handler: ", exit.m_isUnwindHandler, "\n");
-#else // FTL_USES_B3
-        dataLog("    Will arrive at exit from genericUnwind(): ", exit.willArriveAtOSRExitFromGenericUnwind(), "\n");
+        dataLog("    Exit is exception handler: ", exit.isExceptionHandler(), "\n");
+        dataLog("    Is unwind handler: ", exit.isGenericUnwindHandler(), "\n");
+#if !FTL_USES_B3
         dataLog("    Will arrive at exit from lazy slow path: ", exit.m_exceptionType == ExceptionType::LazySlowPath, "\n");
-#endif // FTL_USES_B3
+#endif // !FTL_USES_B3
         dataLog("    Exit values: ", exit.m_descriptor->m_values, "\n");
 #if FTL_USES_B3
         dataLog("    Value reps: ", listDump(exit.m_valueReps), "\n");
index 4ca014e..a3033cc 100644 (file)
@@ -69,7 +69,7 @@ RefPtr<ExceptionTarget> PatchpointExceptionHandle::scheduleExitCreation(
             new ExceptionTarget(isDefaultHandler, m_state.exceptionHandler, nullptr));
     }
     bool isDefaultHandler = false;
-    return adoptRef(new ExceptionTarget(isDefaultHandler, { }, createHandle(params)));
+    return adoptRef(new ExceptionTarget(isDefaultHandler, { }, createHandle(ExceptionCheck, params)));
 }
 
 void PatchpointExceptionHandle::scheduleExitCreationForUnwind(
@@ -78,9 +78,8 @@ void PatchpointExceptionHandle::scheduleExitCreationForUnwind(
     if (!m_descriptor)
         return;
     
-    RefPtr<OSRExitHandle> handle = createHandle(params);
+    RefPtr<OSRExitHandle> handle = createHandle(GenericUnwind, params);
 
-    handle->exit.m_isUnwindHandler = true;
     handle->exit.m_exceptionHandlerCallSiteIndex = callSiteIndex;
 
     HandlerInfo handler = m_handler;
@@ -110,11 +109,10 @@ PatchpointExceptionHandle::PatchpointExceptionHandle(
 }
 
 RefPtr<OSRExitHandle> PatchpointExceptionHandle::createHandle(
-    const B3::StackmapGenerationParams& params)
+    ExitKind kind, const B3::StackmapGenerationParams& params)
 {
-    bool isExceptionHandler = true;
     return m_descriptor->emitOSRExitLater(
-        m_state, Uncountable, m_origin, params, m_offset, isExceptionHandler);
+        m_state, kind, m_origin, params, m_offset);
 }
 
 } } // namespace JSC::FTL
index f9f0aef..75a03a0 100644 (file)
@@ -31,6 +31,7 @@
 #if ENABLE(FTL_JIT) && FTL_USES_B3
 
 #include "DFGNodeOrigin.h"
+#include "ExitKind.h"
 #include "HandlerInfo.h"
 #include <wtf/Ref.h>
 #include <wtf/ThreadSafeRefCounted.h>
@@ -88,7 +89,7 @@ private:
     PatchpointExceptionHandle(
         State&, OSRExitDescriptor*, DFG::NodeOrigin, unsigned offset, const HandlerInfo&);
 
-    RefPtr<OSRExitHandle> createHandle(const B3::StackmapGenerationParams&);
+    RefPtr<OSRExitHandle> createHandle(ExitKind, const B3::StackmapGenerationParams&);
 
     State& m_state;
     OSRExitDescriptor* m_descriptor;