Title: [202364] trunk
Revision
202364
Author
sbar...@apple.com
Date
2016-06-22 20:24:18 -0700 (Wed, 22 Jun 2016)

Log Message

TypeProfiler and TypeProfilerLog don't play nicely with the concurrent JIT
https://bugs.webkit.org/show_bug.cgi?id=159037
<rdar://problem/26935349>

Reviewed by Benjamin Poulain.

Source/_javascript_Core:

The primary focus of this patch is to make the concurrent
baseline JIT work with the type profiler. We were clearing
the type profiler log on the background baseline compiler
thread which lead to bad things happening. This patch fixes
this by processing the log before we launch the compile on
a background thread.

Secondly, I audited the type profiler code inside the DFG,
and found that we were doing some racy things. I haven't
seen any crashes because of these things, but it is possible
that they exist. We were grabbing a RefPtr to a TypeSet,
even though TypeSet was RefCounted and not ThreadSafeRefCounted.
This patch makes TypeSet ThreadSafeRefCounted. We were
also copying a StructureSet while the execution thread could
be augmenting the StructureSet. This patch puts changes to
TypeSet's StructureSet behind a ConcurrentJITLock.

I've also added two more large running tests that run with the
type profiler enabled. These are here just to catch any major bugs
in the type profiler implementation.

* jit/JIT.cpp:
(JSC::JIT::compileWithoutLinking):
(JSC::JIT::privateCompile):
(JSC::JIT::privateCompileExceptionHandlers):
(JSC::JIT::doMainThreadPreparationBeforeCompile):
(JSC::JIT::frameRegisterCountFor):
* jit/JIT.h:
(JSC::JIT::compile):
* jit/JITWorklist.cpp:
(JSC::JITWorklist::Plan::Plan):
(JSC::JITWorklist::Plan::compileInThread):
* runtime/TypeSet.cpp:
(JSC::TypeSet::addTypeInformation):
(JSC::TypeSet::invalidateCache):
* runtime/TypeSet.h:
(JSC::TypeSet::create):
(JSC::TypeSet::isEmpty):
(JSC::TypeSet::seenTypes):
(JSC::TypeSet::structureSet):
* tests/typeProfiler/deltablue-for-of.js: Added.
* tests/typeProfiler/getter-richards.js: Added.

Tools:

Run typeProfiler.yaml tests under an additional CJIT enabled mode.

* Scripts/run-jsc-stress-tests:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (202363 => 202364)


--- trunk/Source/_javascript_Core/ChangeLog	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-06-23 03:24:18 UTC (rev 202364)
@@ -1,3 +1,54 @@
+2016-06-22  Saam barati  <sbar...@apple.com>
+
+        TypeProfiler and TypeProfilerLog don't play nicely with the concurrent JIT
+        https://bugs.webkit.org/show_bug.cgi?id=159037
+        <rdar://problem/26935349>
+
+        Reviewed by Benjamin Poulain.
+
+        The primary focus of this patch is to make the concurrent
+        baseline JIT work with the type profiler. We were clearing
+        the type profiler log on the background baseline compiler
+        thread which lead to bad things happening. This patch fixes
+        this by processing the log before we launch the compile on
+        a background thread.
+
+        Secondly, I audited the type profiler code inside the DFG,
+        and found that we were doing some racy things. I haven't
+        seen any crashes because of these things, but it is possible
+        that they exist. We were grabbing a RefPtr to a TypeSet,
+        even though TypeSet was RefCounted and not ThreadSafeRefCounted.
+        This patch makes TypeSet ThreadSafeRefCounted. We were
+        also copying a StructureSet while the execution thread could
+        be augmenting the StructureSet. This patch puts changes to 
+        TypeSet's StructureSet behind a ConcurrentJITLock.
+
+        I've also added two more large running tests that run with the
+        type profiler enabled. These are here just to catch any major bugs
+        in the type profiler implementation.
+
+        * jit/JIT.cpp:
+        (JSC::JIT::compileWithoutLinking):
+        (JSC::JIT::privateCompile):
+        (JSC::JIT::privateCompileExceptionHandlers):
+        (JSC::JIT::doMainThreadPreparationBeforeCompile):
+        (JSC::JIT::frameRegisterCountFor):
+        * jit/JIT.h:
+        (JSC::JIT::compile):
+        * jit/JITWorklist.cpp:
+        (JSC::JITWorklist::Plan::Plan):
+        (JSC::JITWorklist::Plan::compileInThread):
+        * runtime/TypeSet.cpp:
+        (JSC::TypeSet::addTypeInformation):
+        (JSC::TypeSet::invalidateCache):
+        * runtime/TypeSet.h:
+        (JSC::TypeSet::create):
+        (JSC::TypeSet::isEmpty):
+        (JSC::TypeSet::seenTypes):
+        (JSC::TypeSet::structureSet):
+        * tests/typeProfiler/deltablue-for-of.js: Added.
+        * tests/typeProfiler/getter-richards.js: Added.
+
 2016-06-22  Keith Miller  <keith_mil...@apple.com>
 
         We should have a DFG intrinsic that checks if a value is a TypedArrayView

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (202363 => 202364)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-06-23 03:24:18 UTC (rev 202364)
@@ -1461,7 +1461,11 @@
                 fixEdge<OtherUse>(node->child1());
                 node->remove();
             } else if (typeSet->doesTypeConformTo(TypeObject)) {
-                StructureSet set = typeSet->structureSet();
+                StructureSet set;
+                {
+                    ConcurrentJITLocker locker(typeSet->m_lock);
+                    set = typeSet->structureSet(locker);
+                }
                 if (!set.isEmpty()) {
                     fixEdge<CellUse>(node->child1());
                     node->convertToCheckStructure(m_graph.addStructureSet(set));

Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (202363 => 202364)


--- trunk/Source/_javascript_Core/jit/JIT.cpp	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp	2016-06-23 03:24:18 UTC (rev 202364)
@@ -549,10 +549,6 @@
         break;
     }
 
-    // This ensures that we have the most up to date type information when performing typecheck optimizations for op_profile_type.
-    if (m_vm->typeProfiler())
-        m_vm->typeProfilerLog()->processLogEntries(ASCIILiteral("Preparing for JIT compilation."));
-    
     if (Options::dumpDisassembly() || (m_vm->m_perBytecodeProfiler && Options::disassembleBaselineForProfiler()))
         m_disassembler = std::make_unique<JITDisassembler>(m_codeBlock);
     if (m_vm->m_perBytecodeProfiler) {
@@ -804,6 +800,7 @@
 
 CompilationResult JIT::privateCompile(JITCompilationEffort effort)
 {
+    doMainThreadPreparationBeforeCompile();
     compileWithoutLinking(effort);
     return link();
 }
@@ -848,6 +845,13 @@
     }
 }
 
+void JIT::doMainThreadPreparationBeforeCompile()
+{
+    // This ensures that we have the most up to date type information when performing typecheck optimizations for op_profile_type.
+    if (m_vm->typeProfiler())
+        m_vm->typeProfilerLog()->processLogEntries(ASCIILiteral("Preparing for JIT compilation."));
+}
+
 unsigned JIT::frameRegisterCountFor(CodeBlock* codeBlock)
 {
     ASSERT(static_cast<unsigned>(codeBlock->m_numCalleeLocals) == WTF::roundUpToMultipleOf(stackAlignmentRegisters(), static_cast<unsigned>(codeBlock->m_numCalleeLocals)));

Modified: trunk/Source/_javascript_Core/jit/JIT.h (202363 => 202364)


--- trunk/Source/_javascript_Core/jit/JIT.h	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Source/_javascript_Core/jit/JIT.h	2016-06-23 03:24:18 UTC (rev 202364)
@@ -199,6 +199,8 @@
 
         void compileWithoutLinking(JITCompilationEffort);
         CompilationResult link();
+
+        void doMainThreadPreparationBeforeCompile();
         
         static CompilationResult compile(VM* vm, CodeBlock* codeBlock, JITCompilationEffort effort)
         {

Modified: trunk/Source/_javascript_Core/jit/JITWorklist.cpp (202363 => 202364)


--- trunk/Source/_javascript_Core/jit/JITWorklist.cpp	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Source/_javascript_Core/jit/JITWorklist.cpp	2016-06-23 03:24:18 UTC (rev 202364)
@@ -40,6 +40,7 @@
         : m_codeBlock(codeBlock)
         , m_jit(codeBlock->vm(), codeBlock)
     {
+        m_jit.doMainThreadPreparationBeforeCompile();
     }
     
     void compileInThread()

Modified: trunk/Source/_javascript_Core/runtime/TypeSet.cpp (202363 => 202364)


--- trunk/Source/_javascript_Core/runtime/TypeSet.cpp	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Source/_javascript_Core/runtime/TypeSet.cpp	2016-06-23 03:24:18 UTC (rev 202364)
@@ -37,8 +37,8 @@
 namespace JSC {
 
 TypeSet::TypeSet()
-    : m_seenTypes(TypeNothing)
-    , m_isOverflown(false)
+    : m_isOverflown(false)
+    , m_seenTypes(TypeNothing)
 {
 }
 
@@ -49,7 +49,10 @@
 
     if (structure && newShape && !runtimeTypeIsPrimitive(type)) {
         if (!m_structureSet.contains(structure)) {
-            m_structureSet.add(structure);
+            {
+                ConcurrentJITLocker locker(m_lock);
+                m_structureSet.add(structure);
+            }
             // Make one more pass making sure that: 
             // - We don't have two instances of the same shape. (Same shapes may have different Structures).
             // - We don't have two shapes that share the same prototype chain. If these shapes share the same 
@@ -81,6 +84,7 @@
 
 void TypeSet::invalidateCache()
 {
+    ConcurrentJITLocker locker(m_lock);
     auto keepMarkedStructuresFilter = [] (Structure* structure) -> bool { return Heap::isMarked(structure); };
     m_structureSet.genericFilter(keepMarkedStructuresFilter);
 }

Modified: trunk/Source/_javascript_Core/runtime/TypeSet.h (202363 => 202364)


--- trunk/Source/_javascript_Core/runtime/TypeSet.h	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Source/_javascript_Core/runtime/TypeSet.h	2016-06-23 03:24:18 UTC (rev 202364)
@@ -26,10 +26,12 @@
 #ifndef TypeSet_h
 #define TypeSet_h
 
+#include "ConcurrentJITLock.h"
 #include "RuntimeType.h"
 #include "StructureSet.h"
 #include <wtf/HashSet.h>
 #include <wtf/RefCounted.h>
+#include <wtf/ThreadSafeRefCounted.h>
 #include <wtf/text/WTFString.h>
 #include <wtf/Vector.h>
 
@@ -79,7 +81,7 @@
     bool m_isInDictionaryMode;
 };
 
-class TypeSet : public RefCounted<TypeSet> {
+class TypeSet : public ThreadSafeRefCounted<TypeSet> {
 
 public:
     static Ref<TypeSet> create() { return adoptRef(*new TypeSet); }
@@ -96,11 +98,12 @@
     bool isEmpty() const { return m_seenTypes == TypeNothing; }
     bool doesTypeConformTo(RuntimeTypeMask test) const;
     RuntimeTypeMask seenTypes() const { return m_seenTypes; }
-    StructureSet structureSet() const { return m_structureSet; };
+    StructureSet structureSet(const ConcurrentJITLocker&) const { return m_structureSet; }
 
+    ConcurrentJITLock m_lock;
 private:
+    bool m_isOverflown;
     RuntimeTypeMask m_seenTypes;
-    bool m_isOverflown;
     Vector<RefPtr<StructureShape>> m_structureHistory;
     StructureSet m_structureSet;
 };

Added: trunk/Source/_javascript_Core/tests/typeProfiler/deltablue-for-of.js (0 => 202364)


--- trunk/Source/_javascript_Core/tests/typeProfiler/deltablue-for-of.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/typeProfiler/deltablue-for-of.js	2016-06-23 03:24:18 UTC (rev 202364)
@@ -0,0 +1,870 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 1996 John Maloney and Mario Wolczko.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+// This implementation of the DeltaBlue benchmark is derived
+// from the Smalltalk implementation by John Maloney and Mario
+// Wolczko. Some parts have been translated directly, whereas
+// others have been modified more aggresively to make it feel
+// more like a _javascript_ program.
+
+/**
+ * A _javascript_ implementation of the DeltaBlue constraint-solving
+ * algorithm, as described in:
+ *
+ * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
+ *   Bjorn N. Freeman-Benson and John Maloney
+ *   January 1990 Communications of the ACM,
+ *   also available as University of Washington TR 89-08-06.
+ *
+ * Beware: this benchmark is written in a grotesque style where
+ * the constraint model is built by side-effects from constructors.
+ * I've kept it this way to avoid deviating too much from the original
+ * implementation.
+ */
+
+
+/* --- O b j e c t   M o d e l --- */
+
+Object.prototype.inheritsFrom = function (shuper) {
+  function Inheriter() { }
+  Inheriter.prototype = shuper.prototype;
+  this.prototype = new Inheriter();
+  this.superConstructor = shuper;
+}
+
+function OrderedCollection() {
+  this.elms = new Array();
+}
+
+OrderedCollection.prototype.add = function (elm) {
+  this.elms.push(elm);
+}
+
+OrderedCollection.prototype.at = function (index) {
+  return this.elms[index];
+}
+
+OrderedCollection.prototype.size = function () {
+  return this.elms.length;
+}
+
+OrderedCollection.prototype.removeFirst = function () {
+  return this.elms.pop();
+}
+
+OrderedCollection.prototype.remove = function (elm) {
+  var index = 0, skipped = 0;
+  for (var value of this.elms) {
+    if (value != elm) {
+      this.elms[index] = value;
+      index++;
+    } else {
+      skipped++;
+    }
+  }
+  for (var i = 0; i < skipped; i++)
+    this.elms.pop();
+}
+
+/* --- *
+ * S t r e n g t h
+ * --- */
+
+/**
+ * Strengths are used to measure the relative importance of constraints.
+ * New strengths may be inserted in the strength hierarchy without
+ * disrupting current constraints.  Strengths cannot be created outside
+ * this class, so pointer comparison can be used for value comparison.
+ */
+function Strength(strengthValue, name) {
+  this.strengthValue = strengthValue;
+  this.name = name;
+}
+
+Strength.stronger = function (s1, s2) {
+  return s1.strengthValue < s2.strengthValue;
+}
+
+Strength.weaker = function (s1, s2) {
+  return s1.strengthValue > s2.strengthValue;
+}
+
+Strength.weakestOf = function (s1, s2) {
+  return this.weaker(s1, s2) ? s1 : s2;
+}
+
+Strength.strongest = function (s1, s2) {
+  return this.stronger(s1, s2) ? s1 : s2;
+}
+
+Strength.prototype.nextWeaker = function () {
+  switch (this.strengthValue) {
+    case 0: return Strength.WEAKEST;
+    case 1: return Strength.WEAK_DEFAULT;
+    case 2: return Strength.NORMAL;
+    case 3: return Strength.STRONG_DEFAULT;
+    case 4: return Strength.PREFERRED;
+    case 5: return Strength.REQUIRED;
+  }
+}
+
+// Strength constants.
+Strength.REQUIRED        = new Strength(0, "required");
+Strength.STONG_PREFERRED = new Strength(1, "strongPreferred");
+Strength.PREFERRED       = new Strength(2, "preferred");
+Strength.STRONG_DEFAULT  = new Strength(3, "strongDefault");
+Strength.NORMAL          = new Strength(4, "normal");
+Strength.WEAK_DEFAULT    = new Strength(5, "weakDefault");
+Strength.WEAKEST         = new Strength(6, "weakest");
+
+/* --- *
+ * C o n s t r a i n t
+ * --- */
+
+/**
+ * An abstract class representing a system-maintainable relationship
+ * (or "constraint") between a set of variables. A constraint supplies
+ * a strength instance variable; concrete subclasses provide a means
+ * of storing the constrained variables and other information required
+ * to represent a constraint.
+ */
+function Constraint(strength) {
+  this.strength = strength;
+}
+
+/**
+ * Activate this constraint and attempt to satisfy it.
+ */
+Constraint.prototype.addConstraint = function () {
+  this.addToGraph();
+  planner.incrementalAdd(this);
+}
+
+/**
+ * Attempt to find a way to enforce this constraint. If successful,
+ * record the solution, perhaps modifying the current dataflow
+ * graph. Answer the constraint that this constraint overrides, if
+ * there is one, or nil, if there isn't.
+ * Assume: I am not already satisfied.
+ */
+Constraint.prototype.satisfy = function (mark) {
+  this.chooseMethod(mark);
+  if (!this.isSatisfied()) {
+    if (this.strength == Strength.REQUIRED)
+      alert("Could not satisfy a required constraint!");
+    return null;
+  }
+  this.markInputs(mark);
+  var out = this.output();
+  var overridden = out.determinedBy;
+  if (overridden != null) overridden.markUnsatisfied();
+  out.determinedBy = this;
+  if (!planner.addPropagate(this, mark))
+    alert("Cycle encountered");
+  out.mark = mark;
+  return overridden;
+}
+
+Constraint.prototype.destroyConstraint = function () {
+  if (this.isSatisfied()) planner.incrementalRemove(this);
+  else this.removeFromGraph();
+}
+
+/**
+ * Normal constraints are not input constraints.  An input constraint
+ * is one that depends on external state, such as the mouse, the
+ * keybord, a clock, or some arbitraty piece of imperative code.
+ */
+Constraint.prototype.isInput = function () {
+  return false;
+}
+
+/* --- *
+ * U n a r y   C o n s t r a i n t
+ * --- */
+
+/**
+ * Abstract superclass for constraints having a single possible output
+ * variable.
+ */
+function UnaryConstraint(v, strength) {
+  UnaryConstraint.superConstructor.call(this, strength);
+  this.myOutput = v;
+  this.satisfied = false;
+  this.addConstraint();
+}
+
+UnaryConstraint.inheritsFrom(Constraint);
+
+/**
+ * Adds this constraint to the constraint graph
+ */
+UnaryConstraint.prototype.addToGraph = function () {
+  this.myOutput.addConstraint(this);
+  this.satisfied = false;
+}
+
+/**
+ * Decides if this constraint can be satisfied and records that
+ * decision.
+ */
+UnaryConstraint.prototype.chooseMethod = function (mark) {
+  this.satisfied = (this.myOutput.mark != mark)
+    && Strength.stronger(this.strength, this.myOutput.walkStrength);
+}
+
+/**
+ * Returns true if this constraint is satisfied in the current solution.
+ */
+UnaryConstraint.prototype.isSatisfied = function () {
+  return this.satisfied;
+}
+
+UnaryConstraint.prototype.markInputs = function (mark) {
+  // has no inputs
+}
+
+/**
+ * Returns the current output variable.
+ */
+UnaryConstraint.prototype.output = function () {
+  return this.myOutput;
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this constraint. Assume
+ * this constraint is satisfied.
+ */
+UnaryConstraint.prototype.recalculate = function () {
+  this.myOutput.walkStrength = this.strength;
+  this.myOutput.stay = !this.isInput();
+  if (this.myOutput.stay) this.execute(); // Stay optimization
+}
+
+/**
+ * Records that this constraint is unsatisfied
+ */
+UnaryConstraint.prototype.markUnsatisfied = function () {
+  this.satisfied = false;
+}
+
+UnaryConstraint.prototype.inputsKnown = function () {
+  return true;
+}
+
+UnaryConstraint.prototype.removeFromGraph = function () {
+  if (this.myOutput != null) this.myOutput.removeConstraint(this);
+  this.satisfied = false;
+}
+
+/* --- *
+ * S t a y   C o n s t r a i n t
+ * --- */
+
+/**
+ * Variables that should, with some level of preference, stay the same.
+ * Planners may exploit the fact that instances, if satisfied, will not
+ * change their output during plan execution.  This is called "stay
+ * optimization".
+ */
+function StayConstraint(v, str) {
+  StayConstraint.superConstructor.call(this, v, str);
+}
+
+StayConstraint.inheritsFrom(UnaryConstraint);
+
+StayConstraint.prototype.execute = function () {
+  // Stay constraints do nothing
+}
+
+/* --- *
+ * E d i t   C o n s t r a i n t
+ * --- */
+
+/**
+ * A unary input constraint used to mark a variable that the client
+ * wishes to change.
+ */
+function EditConstraint(v, str) {
+  EditConstraint.superConstructor.call(this, v, str);
+}
+
+EditConstraint.inheritsFrom(UnaryConstraint);
+
+/**
+ * Edits indicate that a variable is to be changed by imperative code.
+ */
+EditConstraint.prototype.isInput = function () {
+  return true;
+}
+
+EditConstraint.prototype.execute = function () {
+  // Edit constraints do nothing
+}
+
+/* --- *
+ * B i n a r y   C o n s t r a i n t
+ * --- */
+
+var Direction = new Object();
+Direction.NONE     = 0;
+Direction.FORWARD  = 1;
+Direction.BACKWARD = -1;
+
+/**
+ * Abstract superclass for constraints having two possible output
+ * variables.
+ */
+function BinaryConstraint(var1, var2, strength) {
+  BinaryConstraint.superConstructor.call(this, strength);
+  this.v1 = var1;
+  this.v2 = var2;
+  this.direction = Direction.NONE;
+  this.addConstraint();
+}
+
+BinaryConstraint.inheritsFrom(Constraint);
+
+/**
+ * Decides if this constraint can be satisfied and which way it
+ * should flow based on the relative strength of the variables related,
+ * and record that decision.
+ */
+BinaryConstraint.prototype.chooseMethod = function (mark) {
+  if (this.v1.mark == mark) {
+    this.direction = (this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength))
+      ? Direction.FORWARD
+      : Direction.NONE;
+  }
+  if (this.v2.mark == mark) {
+    this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength))
+      ? Direction.BACKWARD
+      : Direction.NONE;
+  }
+  if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) {
+    this.direction = Strength.stronger(this.strength, this.v1.walkStrength)
+      ? Direction.BACKWARD
+      : Direction.NONE;
+  } else {
+    this.direction = Strength.stronger(this.strength, this.v2.walkStrength)
+      ? Direction.FORWARD
+      : Direction.BACKWARD
+  }
+}
+
+/**
+ * Add this constraint to the constraint graph
+ */
+BinaryConstraint.prototype.addToGraph = function () {
+  this.v1.addConstraint(this);
+  this.v2.addConstraint(this);
+  this.direction = Direction.NONE;
+}
+
+/**
+ * Answer true if this constraint is satisfied in the current solution.
+ */
+BinaryConstraint.prototype.isSatisfied = function () {
+  return this.direction != Direction.NONE;
+}
+
+/**
+ * Mark the input variable with the given mark.
+ */
+BinaryConstraint.prototype.markInputs = function (mark) {
+  this.input().mark = mark;
+}
+
+/**
+ * Returns the current input variable
+ */
+BinaryConstraint.prototype.input = function () {
+  return (this.direction == Direction.FORWARD) ? this.v1 : this.v2;
+}
+
+/**
+ * Returns the current output variable
+ */
+BinaryConstraint.prototype.output = function () {
+  return (this.direction == Direction.FORWARD) ? this.v2 : this.v1;
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this
+ * constraint. Assume this constraint is satisfied.
+ */
+BinaryConstraint.prototype.recalculate = function () {
+  var ihn = this.input(), out = this.output();
+  out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
+  out.stay = ihn.stay;
+  if (out.stay) this.execute();
+}
+
+/**
+ * Record the fact that this constraint is unsatisfied.
+ */
+BinaryConstraint.prototype.markUnsatisfied = function () {
+  this.direction = Direction.NONE;
+}
+
+BinaryConstraint.prototype.inputsKnown = function (mark) {
+  var i = this.input();
+  return i.mark == mark || i.stay || i.determinedBy == null;
+}
+
+BinaryConstraint.prototype.removeFromGraph = function () {
+  if (this.v1 != null) this.v1.removeConstraint(this);
+  if (this.v2 != null) this.v2.removeConstraint(this);
+  this.direction = Direction.NONE;
+}
+
+/* --- *
+ * S c a l e   C o n s t r a i n t
+ * --- */
+
+/**
+ * Relates two variables by the linear scaling relationship: "v2 =
+ * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
+ * this relationship but the scale factor and offset are considered
+ * read-only.
+ */
+function ScaleConstraint(src, scale, offset, dest, strength) {
+  this.direction = Direction.NONE;
+  this.scale = scale;
+  this.offset = offset;
+  ScaleConstraint.superConstructor.call(this, src, dest, strength);
+}
+
+ScaleConstraint.inheritsFrom(BinaryConstraint);
+
+/**
+ * Adds this constraint to the constraint graph.
+ */
+ScaleConstraint.prototype.addToGraph = function () {
+  ScaleConstraint.superConstructor.prototype.addToGraph.call(this);
+  this.scale.addConstraint(this);
+  this.offset.addConstraint(this);
+}
+
+ScaleConstraint.prototype.removeFromGraph = function () {
+  ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this);
+  if (this.scale != null) this.scale.removeConstraint(this);
+  if (this.offset != null) this.offset.removeConstraint(this);
+}
+
+ScaleConstraint.prototype.markInputs = function (mark) {
+  ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark);
+  this.scale.mark = this.offset.mark = mark;
+}
+
+/**
+ * Enforce this constraint. Assume that it is satisfied.
+ */
+ScaleConstraint.prototype.execute = function () {
+  if (this.direction == Direction.FORWARD) {
+    this.v2.value = this.v1.value * this.scale.value + this.offset.value;
+  } else {
+    this.v1.value = (this.v2.value - this.offset.value) / this.scale.value;
+  }
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this constraint. Assume
+ * this constraint is satisfied.
+ */
+ScaleConstraint.prototype.recalculate = function () {
+  var ihn = this.input(), out = this.output();
+  out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
+  out.stay = ihn.stay && this.scale.stay && this.offset.stay;
+  if (out.stay) this.execute();
+}
+
+/* --- *
+ * E q u a l i t  y   C o n s t r a i n t
+ * --- */
+
+/**
+ * Constrains two variables to have the same value.
+ */
+function EqualityConstraint(var1, var2, strength) {
+  EqualityConstraint.superConstructor.call(this, var1, var2, strength);
+}
+
+EqualityConstraint.inheritsFrom(BinaryConstraint);
+
+/**
+ * Enforce this constraint. Assume that it is satisfied.
+ */
+EqualityConstraint.prototype.execute = function () {
+  this.output().value = this.input().value;
+}
+
+/* --- *
+ * V a r i a b l e
+ * --- */
+
+/**
+ * A constrained variable. In addition to its value, it maintain the
+ * structure of the constraint graph, the current dataflow graph, and
+ * various parameters of interest to the DeltaBlue incremental
+ * constraint solver.
+ **/
+function Variable(name, initialValue) {
+  this.value = initialValue || 0;
+  this.constraints = new OrderedCollection();
+  this.determinedBy = null;
+  this.mark = 0;
+  this.walkStrength = Strength.WEAKEST;
+  this.stay = true;
+  this.name = name;
+}
+
+/**
+ * Add the given constraint to the set of all constraints that refer
+ * this variable.
+ */
+Variable.prototype.addConstraint = function (c) {
+  this.constraints.add(c);
+}
+
+/**
+ * Removes all traces of c from this variable.
+ */
+Variable.prototype.removeConstraint = function (c) {
+  this.constraints.remove(c);
+  if (this.determinedBy == c) this.determinedBy = null;
+}
+
+/* --- *
+ * P l a n n e r
+ * --- */
+
+/**
+ * The DeltaBlue planner
+ */
+function Planner() {
+  this.currentMark = 0;
+}
+
+/**
+ * Attempt to satisfy the given constraint and, if successful,
+ * incrementally update the dataflow graph.  Details: If satifying
+ * the constraint is successful, it may override a weaker constraint
+ * on its output. The algorithm attempts to resatisfy that
+ * constraint using some other method. This process is repeated
+ * until either a) it reaches a variable that was not previously
+ * determined by any constraint or b) it reaches a constraint that
+ * is too weak to be satisfied using any of its methods. The
+ * variables of constraints that have been processed are marked with
+ * a unique mark value so that we know where we've been. This allows
+ * the algorithm to avoid getting into an infinite loop even if the
+ * constraint graph has an inadvertent cycle.
+ */
+Planner.prototype.incrementalAdd = function (c) {
+  var mark = this.newMark();
+  var overridden = c.satisfy(mark);
+  while (overridden != null)
+    overridden = overridden.satisfy(mark);
+}
+
+/**
+ * Entry point for retracting a constraint. Remove the given
+ * constraint and incrementally update the dataflow graph.
+ * Details: Retracting the given constraint may allow some currently
+ * unsatisfiable downstream constraint to be satisfied. We therefore collect
+ * a list of unsatisfied downstream constraints and attempt to
+ * satisfy each one in turn. This list is traversed by constraint
+ * strength, strongest first, as a heuristic for avoiding
+ * unnecessarily adding and then overriding weak constraints.
+ * Assume: c is satisfied.
+ */
+Planner.prototype.incrementalRemove = function (c) {
+  var out = c.output();
+  c.markUnsatisfied();
+  c.removeFromGraph();
+  var unsatisfied = this.removePropagateFrom(out);
+  var strength = Strength.REQUIRED;
+  do {
+    for (var u of unsatisfied.elms) {
+      if (u.strength == strength)
+        this.incrementalAdd(u);
+    }
+    strength = strength.nextWeaker();
+  } while (strength != Strength.WEAKEST);
+}
+
+/**
+ * Select a previously unused mark value.
+ */
+Planner.prototype.newMark = function () {
+  return ++this.currentMark;
+}
+
+/**
+ * Extract a plan for resatisfaction starting from the given source
+ * constraints, usually a set of input constraints. This method
+ * assumes that stay optimization is desired; the plan will contain
+ * only constraints whose output variables are not stay. Constraints
+ * that do no computation, such as stay and edit constraints, are
+ * not included in the plan.
+ * Details: The outputs of a constraint are marked when it is added
+ * to the plan under construction. A constraint may be appended to
+ * the plan when all its input variables are known. A variable is
+ * known if either a) the variable is marked (indicating that has
+ * been computed by a constraint appearing earlier in the plan), b)
+ * the variable is 'stay' (i.e. it is a constant at plan execution
+ * time), or c) the variable is not determined by any
+ * constraint. The last provision is for past states of history
+ * variables, which are not stay but which are also not computed by
+ * any constraint.
+ * Assume: sources are all satisfied.
+ */
+Planner.prototype.makePlan = function (sources) {
+  var mark = this.newMark();
+  var plan = new Plan();
+  var todo = sources;
+  while (todo.size() > 0) {
+    var c = todo.removeFirst();
+    if (c.output().mark != mark && c.inputsKnown(mark)) {
+      plan.addConstraint(c);
+      c.output().mark = mark;
+      this.addConstraintsConsumingTo(c.output(), todo);
+    }
+  }
+  return plan;
+}
+
+/**
+ * Extract a plan for resatisfying starting from the output of the
+ * given constraints, usually a set of input constraints.
+ */
+Planner.prototype.extractPlanFromConstraints = function (constraints) {
+  var sources = new OrderedCollection();
+  for (var c of constraints.elms) {
+    if (c.isInput() && c.isSatisfied())
+      // not in plan already and eligible for inclusion
+      sources.add(c);
+  }
+  return this.makePlan(sources);
+}
+
+/**
+ * Recompute the walkabout strengths and stay flags of all variables
+ * downstream of the given constraint and recompute the actual
+ * values of all variables whose stay flag is true. If a cycle is
+ * detected, remove the given constraint and answer
+ * false. Otherwise, answer true.
+ * Details: Cycles are detected when a marked variable is
+ * encountered downstream of the given constraint. The sender is
+ * assumed to have marked the inputs of the given constraint with
+ * the given mark. Thus, encountering a marked node downstream of
+ * the output constraint means that there is a path from the
+ * constraint's output to one of its inputs.
+ */
+Planner.prototype.addPropagate = function (c, mark) {
+  var todo = new OrderedCollection();
+  todo.add(c);
+  while (todo.size() > 0) {
+    var d = todo.removeFirst();
+    if (d.output().mark == mark) {
+      this.incrementalRemove(c);
+      return false;
+    }
+    d.recalculate();
+    this.addConstraintsConsumingTo(d.output(), todo);
+  }
+  return true;
+}
+
+
+/**
+ * Update the walkabout strengths and stay flags of all variables
+ * downstream of the given constraint. Answer a collection of
+ * unsatisfied constraints sorted in order of decreasing strength.
+ */
+Planner.prototype.removePropagateFrom = function (out) {
+  out.determinedBy = null;
+  out.walkStrength = Strength.WEAKEST;
+  out.stay = true;
+  var unsatisfied = new OrderedCollection();
+  var todo = new OrderedCollection();
+  todo.add(out);
+  while (todo.size() > 0) {
+    var v = todo.removeFirst();
+    for (var c of v.constraints.elms) {
+      if (!c.isSatisfied())
+        unsatisfied.add(c);
+    }
+    var determining = v.determinedBy;
+    for (var next of v.constraints.elms) {
+      if (next != determining && next.isSatisfied()) {
+        next.recalculate();
+        todo.add(next.output());
+      }
+    }
+  }
+  return unsatisfied;
+}
+
+Planner.prototype.addConstraintsConsumingTo = function (v, coll) {
+  var determining = v.determinedBy;
+  var cc = v.constraints;
+  for (var c of cc.elms) {
+    if (c != determining && c.isSatisfied())
+      coll.add(c);
+  }
+}
+
+/* --- *
+ * P l a n
+ * --- */
+
+/**
+ * A Plan is an ordered list of constraints to be executed in sequence
+ * to resatisfy all currently satisfiable constraints in the face of
+ * one or more changing inputs.
+ */
+function Plan() {
+  this.v = new OrderedCollection();
+}
+
+Plan.prototype.addConstraint = function (c) {
+  this.v.add(c);
+}
+
+Plan.prototype.size = function () {
+  return this.v.size();
+}
+
+Plan.prototype.constraintAt = function (index) {
+  return this.v.at(index);
+}
+
+Plan.prototype.execute = function () {
+  for (var c of this.v.elms) {
+    c.execute();
+  }
+}
+
+/* --- *
+ * M a i n
+ * --- */
+
+/**
+ * This is the standard DeltaBlue benchmark. A long chain of equality
+ * constraints is constructed with a stay constraint on one end. An
+ * edit constraint is then added to the opposite end and the time is
+ * measured for adding and removing this constraint, and extracting
+ * and executing a constraint satisfaction plan. There are two cases.
+ * In case 1, the added constraint is stronger than the stay
+ * constraint and values must propagate down the entire length of the
+ * chain. In case 2, the added constraint is weaker than the stay
+ * constraint so it cannot be accomodated. The cost in this case is,
+ * of course, very low. Typical situations lie somewhere between these
+ * two extremes.
+ */
+function chainTest(n) {
+  planner = new Planner();
+  var prev = null, first = null, last = null;
+
+  // Build chain of n equality constraints
+  for (var i = 0; i <= n; i++) {
+    var name = "v" + i;
+    var v = new Variable(name);
+    if (prev != null)
+      new EqualityConstraint(prev, v, Strength.REQUIRED);
+    if (i == 0) first = v;
+    if (i == n) last = v;
+    prev = v;
+  }
+
+  new StayConstraint(last, Strength.STRONG_DEFAULT);
+  var edit = new EditConstraint(first, Strength.PREFERRED);
+  var edits = new OrderedCollection();
+  edits.add(edit);
+  var plan = planner.extractPlanFromConstraints(edits);
+  for (var i = 0; i < 100; i++) {
+    first.value = i;
+    plan.execute();
+    if (last.value != i)
+      alert("Chain test failed.");
+  }
+}
+
+/**
+ * This test constructs a two sets of variables related to each
+ * other by a simple linear transformation (scale and offset). The
+ * time is measured to change a variable on either side of the
+ * mapping and to change the scale and offset factors.
+ */
+function projectionTest(n) {
+  planner = new Planner();
+  var scale = new Variable("scale", 10);
+  var offset = new Variable("offset", 1000);
+  var src = "" dst = null;
+
+  var dests = new OrderedCollection();
+  for (var i = 0; i < n; i++) {
+    src = "" Variable("src" + i, i);
+    dst = new Variable("dst" + i, i);
+    dests.add(dst);
+    new StayConstraint(src, Strength.NORMAL);
+    new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED);
+  }
+
+  change(src, 17);
+  if (dst.value != 1170) alert("Projection 1 failed");
+  change(dst, 1050);
+  if (src.value != 5) alert("Projection 2 failed");
+  change(scale, 5);
+  for (var i = 0; i < n - 1; i++) {
+    if (dests.at(i).value != i * 5 + 1000)
+      alert("Projection 3 failed");
+  }
+  change(offset, 2000);
+  for (var i = 0; i < n - 1; i++) {
+    if (dests.at(i).value != i * 5 + 2000)
+      alert("Projection 4 failed");
+  }
+}
+
+function change(v, newValue) {
+  var edit = new EditConstraint(v, Strength.PREFERRED);
+  var edits = new OrderedCollection();
+  edits.add(edit);
+  var plan = planner.extractPlanFromConstraints(edits);
+  for (var i = 0; i < 10; i++) {
+    v.value = newValue;
+    plan.execute();
+  }
+  edit.destroyConstraint();
+}
+
+// Global variable holding the current planner.
+var planner = null;
+
+function deltaBlue() {
+  chainTest(50);
+  projectionTest(50);
+}
+
+for (var i = 0; i < 100; ++i)
+    deltaBlue();

Added: trunk/Source/_javascript_Core/tests/typeProfiler/getter-richards.js (0 => 202364)


--- trunk/Source/_javascript_Core/tests/typeProfiler/getter-richards.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/typeProfiler/getter-richards.js	2016-06-23 03:24:18 UTC (rev 202364)
@@ -0,0 +1,593 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2014 Apple Inc.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// This is a _javascript_ implementation of the Richards
+// benchmark from:
+//
+//    http://www.cl.cam.ac.uk/~mr10/Bench.html
+//
+// The benchmark was originally implemented in BCPL by
+// Martin Richards. It was then ported to _javascript_ by the
+// V8 project authors, and then subsequently it was modified
+// to use getters and setters by WebKit authors.
+
+
+/**
+ * The Richards benchmark simulates the task dispatcher of an
+ * operating system.
+ **/
+function runRichards() {
+  var scheduler = new Scheduler();
+  scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
+
+  var queue = new Packet(null, ID_WORKER, KIND_WORK);
+  queue = new Packet(queue,  ID_WORKER, KIND_WORK);
+  scheduler.addWorkerTask(ID_WORKER, 1000, queue);
+
+  queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
+  queue = new Packet(queue,  ID_DEVICE_A, KIND_DEVICE);
+  queue = new Packet(queue,  ID_DEVICE_A, KIND_DEVICE);
+  scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
+
+  queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
+  queue = new Packet(queue,  ID_DEVICE_B, KIND_DEVICE);
+  queue = new Packet(queue,  ID_DEVICE_B, KIND_DEVICE);
+  scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
+
+  scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
+
+  scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
+
+  scheduler.schedule();
+
+  if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
+      scheduler.holdCount != EXPECTED_HOLD_COUNT) {
+    var msg =
+        "Error during execution: queueCount = " + scheduler.queueCount +
+        ", holdCount = " + scheduler.holdCount + ".";
+    throw new Error(msg);
+  }
+}
+
+var COUNT = 1000;
+
+/**
+ * These two constants specify how many times a packet is queued and
+ * how many times a task is put on hold in a correct run of richards.
+ * They don't have any meaning a such but are characteristic of a
+ * correct run so if the actual queue or hold count is different from
+ * the expected there must be a bug in the implementation.
+ **/
+var EXPECTED_QUEUE_COUNT = 2322;
+var EXPECTED_HOLD_COUNT = 928;
+
+
+/**
+ * A scheduler can be used to schedule a set of tasks based on their relative
+ * priorities.  Scheduling is done by maintaining a list of task control blocks
+ * which holds tasks and the data queue they are processing.
+ * @constructor
+ */
+function Scheduler() {
+  this._queueCount = 0;
+  this._holdCount = 0;
+  this._blocks = new Array(NUMBER_OF_IDS);
+  this._list = null;
+  this._currentTcb = null;
+  this._currentId = null;
+}
+
+var ID_IDLE       = 0;
+var ID_WORKER     = 1;
+var ID_HANDLER_A  = 2;
+var ID_HANDLER_B  = 3;
+var ID_DEVICE_A   = 4;
+var ID_DEVICE_B   = 5;
+var NUMBER_OF_IDS = 6;
+
+var KIND_DEVICE   = 0;
+var KIND_WORK     = 1;
+
+Scheduler.prototype.__defineGetter__("queueCount", function() { return this._queueCount; });
+Scheduler.prototype.__defineSetter__("queueCount", function(value) { this._queueCount = value; });
+Scheduler.prototype.__defineGetter__("holdCount", function() { return this._holdCount; });
+Scheduler.prototype.__defineSetter__("holdCount", function(value) { this._holdCount = value; });
+Scheduler.prototype.__defineGetter__("blocks", function() { return this._blocks; });
+Scheduler.prototype.__defineSetter__("blocks", function(value) { this._blocks = value; });
+Scheduler.prototype.__defineGetter__("list", function() { return this._list; });
+Scheduler.prototype.__defineSetter__("list", function(value) { this._list = value; });
+Scheduler.prototype.__defineGetter__("currentTcb", function() { return this._currentTcb; });
+Scheduler.prototype.__defineSetter__("currentTcb", function(value) { this._currentTcb = value; });
+Scheduler.prototype.__defineGetter__("currentId", function() { return this._currentId; });
+Scheduler.prototype.__defineSetter__("currentId", function(value) { this._currentId = value; });
+
+/**
+ * Add an idle task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {int} count the number of times to schedule the task
+ */
+Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
+  this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
+};
+
+/**
+ * Add a work task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
+  this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
+};
+
+/**
+ * Add a handler task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
+  this.addTask(id, priority, queue, new HandlerTask(this));
+};
+
+/**
+ * Add a handler task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
+  this.addTask(id, priority, queue, new DeviceTask(this))
+};
+
+/**
+ * Add the specified task and mark it as running.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {Task} task the task to add
+ */
+Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
+  this.addTask(id, priority, queue, task);
+  this.currentTcb.setRunning();
+};
+
+/**
+ * Add the specified task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {Task} task the task to add
+ */
+Scheduler.prototype.addTask = function (id, priority, queue, task) {
+  this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
+  this.list = this.currentTcb;
+  this.blocks[id] = this.currentTcb;
+};
+
+/**
+ * Execute the tasks managed by this scheduler.
+ */
+Scheduler.prototype.schedule = function () {
+  this.currentTcb = this.list;
+  while (this.currentTcb != null) {
+    if (this.currentTcb.isHeldOrSuspended()) {
+      this.currentTcb = this.currentTcb.link;
+    } else {
+      this.currentId = this.currentTcb.id;
+      this.currentTcb = this.currentTcb.run();
+    }
+  }
+};
+
+/**
+ * Release a task that is currently blocked and return the next block to run.
+ * @param {int} id the id of the task to suspend
+ */
+Scheduler.prototype.release = function (id) {
+  var tcb = this.blocks[id];
+  if (tcb == null) return tcb;
+  tcb.markAsNotHeld();
+  if (tcb.priority > this.currentTcb.priority) {
+    return tcb;
+  } else {
+    return this.currentTcb;
+  }
+};
+
+/**
+ * Block the currently executing task and return the next task control block
+ * to run.  The blocked task will not be made runnable until it is explicitly
+ * released, even if new work is added to it.
+ */
+Scheduler.prototype.holdCurrent = function () {
+  this.holdCount++;
+  this.currentTcb.markAsHeld();
+  return this.currentTcb.link;
+};
+
+/**
+ * Suspend the currently executing task and return the next task control block
+ * to run.  If new work is added to the suspended task it will be made runnable.
+ */
+Scheduler.prototype.suspendCurrent = function () {
+  this.currentTcb.markAsSuspended();
+  return this.currentTcb;
+};
+
+/**
+ * Add the specified packet to the end of the worklist used by the task
+ * associated with the packet and make the task runnable if it is currently
+ * suspended.
+ * @param {Packet} packet the packet to add
+ */
+Scheduler.prototype.queue = function (packet) {
+  var t = this.blocks[packet.id];
+  if (t == null) return t;
+  this.queueCount++;
+  packet.link = null;
+  packet.id = this.currentId;
+  return t.checkPriorityAdd(this.currentTcb, packet);
+};
+
+/**
+ * A task control block manages a task and the queue of work packages associated
+ * with it.
+ * @param {TaskControlBlock} link the preceding block in the linked block list
+ * @param {int} id the id of this block
+ * @param {int} priority the priority of this block
+ * @param {Packet} queue the queue of packages to be processed by the task
+ * @param {Task} task the task
+ * @constructor
+ */
+function TaskControlBlock(link, id, priority, queue, task) {
+  this._link = link;
+  this._id = id;
+  this._priority = priority;
+  this._queue = queue;
+  this._task = task;
+  if (queue == null) {
+    this.state = STATE_SUSPENDED;
+  } else {
+    this.state = STATE_SUSPENDED_RUNNABLE;
+  }
+}
+
+/**
+ * The task is running and is currently scheduled.
+ */
+var STATE_RUNNING = 0;
+
+/**
+ * The task has packets left to process.
+ */
+var STATE_RUNNABLE = 1;
+
+/**
+ * The task is not currently running.  The task is not blocked as such and may
+* be started by the scheduler.
+ */
+var STATE_SUSPENDED = 2;
+
+/**
+ * The task is blocked and cannot be run until it is explicitly released.
+ */
+var STATE_HELD = 4;
+
+var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
+var STATE_NOT_HELD = ~STATE_HELD;
+
+TaskControlBlock.prototype.__defineGetter__("link", function() { return this._link; });
+TaskControlBlock.prototype.__defineGetter__("id", function() { return this._id; });
+TaskControlBlock.prototype.__defineGetter__("priority", function() { return this._priority; });
+TaskControlBlock.prototype.__defineGetter__("queue", function() { return this._queue; });
+TaskControlBlock.prototype.__defineSetter__("queue", function(value) { this._queue = value; });
+TaskControlBlock.prototype.__defineGetter__("task", function() { return this._task; });
+TaskControlBlock.prototype.__defineGetter__("state", function() { return this._state; });
+TaskControlBlock.prototype.__defineSetter__("state", function(value) { this._state = value; });
+
+TaskControlBlock.prototype.setRunning = function () {
+  this.state = STATE_RUNNING;
+};
+
+TaskControlBlock.prototype.markAsNotHeld = function () {
+  this.state = this.state & STATE_NOT_HELD;
+};
+
+TaskControlBlock.prototype.markAsHeld = function () {
+  this.state = this.state | STATE_HELD;
+};
+
+TaskControlBlock.prototype.isHeldOrSuspended = function () {
+  return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
+};
+
+TaskControlBlock.prototype.markAsSuspended = function () {
+  this.state = this.state | STATE_SUSPENDED;
+};
+
+TaskControlBlock.prototype.markAsRunnable = function () {
+  this.state = this.state | STATE_RUNNABLE;
+};
+
+/**
+ * Runs this task, if it is ready to be run, and returns the next task to run.
+ */
+TaskControlBlock.prototype.run = function () {
+  var packet;
+  if (this.state == STATE_SUSPENDED_RUNNABLE) {
+    packet = this.queue;
+    this.queue = packet.link;
+    if (this.queue == null) {
+      this.state = STATE_RUNNING;
+    } else {
+      this.state = STATE_RUNNABLE;
+    }
+  } else {
+    packet = null;
+  }
+  return this.task.run(packet);
+};
+
+/**
+ * Adds a packet to the worklist of this block's task, marks this as runnable if
+ * necessary, and returns the next runnable object to run (the one
+ * with the highest priority).
+ */
+TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
+  if (this.queue == null) {
+    this.queue = packet;
+    this.markAsRunnable();
+    if (this.priority > task.priority) return this;
+  } else {
+    this.queue = packet.addTo(this.queue);
+  }
+  return task;
+};
+
+TaskControlBlock.prototype.toString = function () {
+  return "tcb { " + this.task + "@" + this.state + " }";
+};
+
+/**
+ * An idle task doesn't do any work itself but cycles control between the two
+ * device tasks.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @param {int} v1 a seed value that controls how the device tasks are scheduled
+ * @param {int} count the number of times this task should be scheduled
+ * @constructor
+ */
+function IdleTask(scheduler, v1, count) {
+  this._scheduler = scheduler;
+  this._v1 = v1;
+  this._count = count;
+}
+
+IdleTask.prototype.__defineGetter__("scheduler", function() { return this._scheduler; });
+IdleTask.prototype.__defineGetter__("v1", function() { return this._v1; });
+IdleTask.prototype.__defineSetter__("v1", function(value) { this._v1 = value; });
+IdleTask.prototype.__defineGetter__("count", function() { return this._count; });
+IdleTask.prototype.__defineSetter__("count", function(value) { this._count = value; });
+
+IdleTask.prototype.run = function (packet) {
+  this.count--;
+  if (this.count == 0) return this.scheduler.holdCurrent();
+  if ((this.v1 & 1) == 0) {
+    this.v1 = this.v1 >> 1;
+    return this.scheduler.release(ID_DEVICE_A);
+  } else {
+    this.v1 = (this.v1 >> 1) ^ 0xD008;
+    return this.scheduler.release(ID_DEVICE_B);
+  }
+};
+
+IdleTask.prototype.toString = function () {
+  return "IdleTask"
+};
+
+/**
+ * A task that suspends itself after each time it has been run to simulate
+ * waiting for data from an external device.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @constructor
+ */
+function DeviceTask(scheduler) {
+  this._scheduler = scheduler;
+  this._v1 = null;
+}
+
+DeviceTask.prototype.__defineGetter__("scheduler", function() { return this._scheduler; });
+DeviceTask.prototype.__defineGetter__("v1", function() { return this._v1; });
+DeviceTask.prototype.__defineSetter__("v1", function(value) { this._v1 = value; });
+
+DeviceTask.prototype.run = function (packet) {
+  if (packet == null) {
+    if (this.v1 == null) return this.scheduler.suspendCurrent();
+    var v = this.v1;
+    this.v1 = null;
+    return this.scheduler.queue(v);
+  } else {
+    this.v1 = packet;
+    return this.scheduler.holdCurrent();
+  }
+};
+
+DeviceTask.prototype.toString = function () {
+  return "DeviceTask";
+};
+
+/**
+ * A task that manipulates work packets.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @param {int} v1 a seed used to specify how work packets are manipulated
+ * @param {int} v2 another seed used to specify how work packets are manipulated
+ * @constructor
+ */
+function WorkerTask(scheduler, v1, v2) {
+  this._scheduler = scheduler;
+  this._v1 = v1;
+  this._v2 = v2;
+}
+
+WorkerTask.prototype.__defineGetter__("scheduler", function() { return this._scheduler; });
+WorkerTask.prototype.__defineGetter__("v1", function() { return this._v1; });
+WorkerTask.prototype.__defineSetter__("v1", function(value) { this._v1 = value; });
+WorkerTask.prototype.__defineGetter__("v2", function() { return this._v2; });
+WorkerTask.prototype.__defineSetter__("v2", function(value) { this._v2 = value; });
+
+WorkerTask.prototype.run = function (packet) {
+  if (packet == null) {
+    return this.scheduler.suspendCurrent();
+  } else {
+    if (this.v1 == ID_HANDLER_A) {
+      this.v1 = ID_HANDLER_B;
+    } else {
+      this.v1 = ID_HANDLER_A;
+    }
+    packet.id = this.v1;
+    packet.a1 = 0;
+    for (var i = 0; i < DATA_SIZE; i++) {
+      this.v2++;
+      if (this.v2 > 26) this.v2 = 1;
+      packet.a2[i] = this.v2;
+    }
+    return this.scheduler.queue(packet);
+  }
+};
+
+WorkerTask.prototype.toString = function () {
+  return "WorkerTask";
+};
+
+/**
+ * A task that manipulates work packets and then suspends itself.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @constructor
+ */
+function HandlerTask(scheduler) {
+  this._scheduler = scheduler;
+  this._v1 = null;
+  this._v2 = null;
+}
+
+HandlerTask.prototype.__defineGetter__("scheduler", function() { return this._scheduler; });
+HandlerTask.prototype.__defineGetter__("v1", function() { return this._v1; });
+HandlerTask.prototype.__defineSetter__("v1", function(value) { this._v1 = value; });
+HandlerTask.prototype.__defineGetter__("v2", function() { return this._v2; });
+HandlerTask.prototype.__defineSetter__("v2", function(value) { this._v2 = value; });
+
+HandlerTask.prototype.run = function (packet) {
+  if (packet != null) {
+    if (packet.kind == KIND_WORK) {
+      this.v1 = packet.addTo(this.v1);
+    } else {
+      this.v2 = packet.addTo(this.v2);
+    }
+  }
+  if (this.v1 != null) {
+    var count = this.v1.a1;
+    var v;
+    if (count < DATA_SIZE) {
+      if (this.v2 != null) {
+        v = this.v2;
+        this.v2 = this.v2.link;
+        v.a1 = this.v1.a2[count];
+        this.v1.a1 = count + 1;
+        return this.scheduler.queue(v);
+      }
+    } else {
+      v = this.v1;
+      this.v1 = this.v1.link;
+      return this.scheduler.queue(v);
+    }
+  }
+  return this.scheduler.suspendCurrent();
+};
+
+HandlerTask.prototype.toString = function () {
+  return "HandlerTask";
+};
+
+/* --- *
+ * P a c k e t
+ * --- */
+
+var DATA_SIZE = 4;
+
+/**
+ * A simple package of data that is manipulated by the tasks.  The exact layout
+ * of the payload data carried by a packet is not importaint, and neither is the
+ * nature of the work performed on packets by the tasks.
+ *
+ * Besides carrying data, packets form linked lists and are hence used both as
+ * data and worklists.
+ * @param {Packet} link the tail of the linked list of packets
+ * @param {int} id an ID for this packet
+ * @param {int} kind the type of this packet
+ * @constructor
+ */
+function Packet(link, id, kind) {
+  this._link = link;
+  this._id = id;
+  this._kind = kind;
+  this._a1 = 0;
+  this._a2 = new Array(DATA_SIZE);
+}
+
+Packet.prototype.__defineGetter__("link", function() { return this._link; });
+Packet.prototype.__defineSetter__("link", function(value) { this._link = value; });
+Packet.prototype.__defineGetter__("id", function() { return this._id; });
+Packet.prototype.__defineSetter__("id", function(value) { this._id = value; });
+Packet.prototype.__defineGetter__("kind", function() { return this._kind; });
+Packet.prototype.__defineGetter__("a1", function() { return this._a1; });
+Packet.prototype.__defineSetter__("a1", function(value) { this._a1 = value; });
+Packet.prototype.__defineGetter__("a2", function() { return this._a2; });
+
+/**
+ * Add this packet to the end of a worklist, and return the worklist.
+ * @param {Packet} queue the worklist to add this packet to
+ */
+Packet.prototype.addTo = function (queue) {
+  this.link = null;
+  if (queue == null) return this;
+  var peek, next = queue;
+  while ((peek = next.link) != null)
+    next = peek;
+  next.link = this;
+  return queue;
+};
+
+Packet.prototype.toString = function () {
+  return "Packet";
+};
+
+for (var i = 0; i < 350; ++i)
+  runRichards();

Modified: trunk/Tools/ChangeLog (202363 => 202364)


--- trunk/Tools/ChangeLog	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Tools/ChangeLog	2016-06-23 03:24:18 UTC (rev 202364)
@@ -1,3 +1,15 @@
+2016-06-22  Saam barati  <sbar...@apple.com>
+
+        TypeProfiler and TypeProfilerLog don't play nicely with the concurrent JIT
+        https://bugs.webkit.org/show_bug.cgi?id=159037
+        <rdar://problem/26935349>
+
+        Reviewed by Benjamin Poulain.
+
+        Run typeProfiler.yaml tests under an additional CJIT enabled mode.
+
+        * Scripts/run-jsc-stress-tests:
+
 2016-06-22  Aakash Jain  <aakash_j...@apple.com>
 
         Fix style issues in webkitpy

Modified: trunk/Tools/Scripts/run-jsc-stress-tests (202363 => 202364)


--- trunk/Tools/Scripts/run-jsc-stress-tests	2016-06-23 01:39:01 UTC (rev 202363)
+++ trunk/Tools/Scripts/run-jsc-stress-tests	2016-06-23 03:24:18 UTC (rev 202364)
@@ -1025,8 +1025,10 @@
 
     if $enableFTL
         run("ftl-no-cjit-type-profiler", "--useTypeProfiler=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
+        run("ftl-type-profiler", "--useTypeProfiler=true", *(FTL_OPTIONS))
     else
         run("no-cjit-type-profiler", "--useTypeProfiler=true", *NO_CJIT_OPTIONS)
+        run("type-profiler", "--useTypeProfiler=true")
     end
 end
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to