<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -190,6 +190,8 @@ public class RubyInstanceConfig {
     public static final boolean THREADLESS_COMPILE_ENABLED
             = FASTEST_COMPILE_ENABLED
             || SafePropertyAccessor.getBoolean(&quot;jruby.compile.threadless&quot;);
+    public static final boolean FASTCASE_COMPILE_ENABLED =
+            SafePropertyAccessor.getBoolean(&quot;jruby.compile.fastcase&quot;);
     public static final boolean LAZYHANDLES_COMPILE = SafePropertyAccessor.getBoolean(&quot;jruby.compile.lazyHandles&quot;, false);
     public static final boolean FORK_ENABLED
             = SafePropertyAccessor.getBoolean(&quot;jruby.fork.enabled&quot;);
@@ -421,10 +423,12 @@ public class RubyInstanceConfig {
                 .append(&quot;       (EXPERIMENTAL) Turn on compilation without polling for \&quot;unsafe\&quot; thread events. Default is false\n&quot;)
                 .append(&quot;    jruby.compile.fastops=true|false\n&quot;)
                 .append(&quot;       (EXPERIMENTAL) Turn on fast operators for Fixnum. Default is false\n&quot;)
+                .append(&quot;    jruby.compile.fastcase=true|false\n&quot;)
+                .append(&quot;       (EXPERIMENTAL) Turn on fast case/when for all-Fixnum whens. Default is false\n&quot;)
                 .append(&quot;    jruby.compile.chainsize=&lt;line count&gt;\n&quot;)
                 .append(&quot;       Set the number of lines at which compiled bodies are \&quot;chained\&quot;. Default is &quot; + CHAINED_COMPILE_LINE_COUNT_DEFAULT + &quot;\n&quot;)
                 .append(&quot;    jruby.compile.lazyHandles=true|false\n&quot;)
-                .append(&quot;       Generate method bindings (handles) for compiled methods lazily. Default is false.&quot;)
+                .append(&quot;       Generate method bindings (handles) for compiled methods lazily. Default is false.\n&quot;)
                 .append(&quot;\nJIT SETTINGS:\n&quot;)
                 .append(&quot;    jruby.jit.threshold=&lt;invocation count&gt;\n&quot;)
                 .append(&quot;       Set the JIT threshold to the specified method invocation count. Default is &quot; + JIT_THRESHOLD + &quot;.\n&quot;)</diff>
      <filename>src/org/jruby/RubyInstanceConfig.java</filename>
    </modified>
    <modified>
      <diff>@@ -29,7 +29,12 @@
 
 package org.jruby.compiler;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Iterator;
+import java.util.List;
+import org.jruby.RubyFixnum;
 import org.jruby.RubyInstanceConfig;
 import org.jruby.ast.AliasNode;
 import org.jruby.ast.AndNode;
@@ -710,9 +715,126 @@ public class ASTCompiler {
         }
 
         context.pollThreadEvents();
-
+        
         Node firstWhenNode = caseNode.getFirstWhenNode();
-        compileWhen(firstWhenNode, context, hasCase);
+
+        // NOTE: Currently this optimization is limited to the following situations:
+        // * No multi-valued whens
+        // * All whens must be an int-ranged literal fixnum
+        // It also still emits the code for the &quot;safe&quot; when logic, which is rather
+        // wasteful (since it essentially doubles each code body). As such it is
+        // normally disabled, but it serves as an example of how this optimization
+        // could be done. Ideally, it should be combined with the when processing
+        // to improve code reuse before it's generally available.
+        if (hasCase &amp;&amp; caseIsAllIntRangedFixnums(caseNode) &amp;&amp; RubyInstanceConfig.FASTCASE_COMPILE_ENABLED) {
+            compileFixnumCase(firstWhenNode, context);
+        } else {
+            compileWhen(firstWhenNode, context, hasCase);
+        }
+    }
+
+    private boolean caseIsAllIntRangedFixnums(CaseNode caseNode) {
+        boolean caseIsAllLiterals = true;
+        for (Node node = caseNode.getFirstWhenNode(); node != null &amp;&amp; node instanceof WhenNode; node = ((WhenNode)node).getNextCase()) {
+            WhenNode whenNode = (WhenNode)node;
+            if (whenNode.getExpressionNodes() instanceof ArrayNode) {
+                ArrayNode arrayNode = (ArrayNode)whenNode.getExpressionNodes();
+                if (arrayNode.size() == 1 &amp;&amp; arrayNode.get(0) instanceof FixnumNode) {
+                    FixnumNode fixnumNode = (FixnumNode)arrayNode.get(0);
+                    long value = fixnumNode.getValue();
+                    if (value &lt;= Integer.MAX_VALUE &amp;&amp; value &gt;= Integer.MIN_VALUE) {
+                        // OK! we can safely use it in a case
+                        continue;
+                    }
+                }
+            }
+            // if we get here, our rigid requirements have not been met; fail
+            caseIsAllLiterals = false;
+            break;
+        }
+        return caseIsAllLiterals;
+    }
+
+    @SuppressWarnings(&quot;unchecked&quot;)
+    public void compileFixnumCase(final Node node, MethodCompiler context) {
+        List&lt;WhenNode&gt; cases = new ArrayList&lt;WhenNode&gt;();
+        Node elseBody = null;
+
+        // first get all when nodes
+        for (Node tmpNode = node; tmpNode != null; tmpNode = ((WhenNode)tmpNode).getNextCase()) {
+            WhenNode whenNode = (WhenNode)tmpNode;
+
+            cases.add(whenNode);
+
+            if (whenNode.getNextCase() != null) {
+                // if there's another node coming, make sure it will be a when...
+                if (whenNode.getNextCase() instanceof WhenNode) {
+                    // normal when, continue
+                    continue;
+                } else {
+                    // ...or handle it as an else and we're done
+                    elseBody = whenNode.getNextCase();
+                    break;
+                }
+            }
+        }
+        // sort them based on literal value
+        Collections.sort(cases, new Comparator() {
+            public int compare(Object o1, Object o2) {
+                WhenNode w1 = (WhenNode)o1;
+                WhenNode w2 = (WhenNode)o2;
+                Integer value1 = (int)((FixnumNode)((ArrayNode)w1.getExpressionNodes()).get(0)).getValue();
+                Integer value2 = (int)((FixnumNode)((ArrayNode)w2.getExpressionNodes()).get(0)).getValue();
+                return value1.compareTo(value2);
+            }
+        });
+        final int[] intCases = new int[cases.size()];
+        final Node[] bodies = new Node[intCases.length];
+
+        // prepare arrays of int cases and code bodies
+        for (int i = 0 ; i &lt; intCases.length; i++) {
+            intCases[i] = (int)((FixnumNode)((ArrayNode)cases.get(i).getExpressionNodes()).get(0)).getValue();
+            bodies[i] = cases.get(i).getBodyNode();
+        }
+        
+        final ArrayCallback callback = new ArrayCallback() {
+            public void nextValue(MethodCompiler context, Object array, int index) {
+                Node[] bodies = (Node[])array;
+                if (bodies[index] != null) {
+                    compile(bodies[index], context);
+                } else {
+                    context.loadNil();
+                }
+            }
+        };
+
+        final Node finalElse = elseBody;
+        final CompilerCallback elseCallback = new CompilerCallback() {
+            public void call(MethodCompiler context) {
+                if (finalElse == null) {
+                    context.loadNil();
+                } else {
+                    compile(finalElse, context);
+                }
+            }
+        };
+
+        // compile the literal switch; embed the fast version, but also the slow
+        // in case we need to fall back on it
+        // TODO: It would be nice to detect only types that fixnum is === to here
+        // and just fail fast for the others...maybe examine Ruby 1.9's optz?
+        context.typeCheckBranch(RubyFixnum.class,
+                new BranchCallback() {
+                    public void branch(MethodCompiler context) {
+                        context.literalSwitch(intCases, bodies, callback, elseCallback);
+                    }
+                },
+                new BranchCallback() {
+                    public void branch(MethodCompiler context) {
+                        compileWhen(node, context, true);
+                    }
+                }
+        );
     }
 
     public void compileWhen(Node node, MethodCompiler context, final boolean hasCase) {</diff>
      <filename>src/org/jruby/compiler/ASTCompiler.java</filename>
    </modified>
    <modified>
      <diff>@@ -541,4 +541,6 @@ public interface MethodCompiler {
 
     public MethodCompiler chainToMethod(String name, ASTInspector inspector);
     public void wrapJavaException();
+    public void literalSwitch(int[] caseInts, Object caseBodies, ArrayCallback casesCallback, CompilerCallback defaultCallback);
+    public void typeCheckBranch(Class type, BranchCallback trueCallback, BranchCallback falseCallback);
 }</diff>
      <filename>src/org/jruby/compiler/MethodCompiler.java</filename>
    </modified>
    <modified>
      <diff>@@ -47,6 +47,7 @@ import org.jruby.RubyBignum;
 import org.jruby.RubyBoolean;
 import org.jruby.RubyClass;
 import org.jruby.RubyException;
+import org.jruby.RubyFixnum;
 import org.jruby.RubyFloat;
 import org.jruby.RubyHash;
 import org.jruby.RubyInstanceConfig;
@@ -2835,6 +2836,46 @@ public class StandardASMCompiler implements ScriptCompiler, Opcodes {
             method.athrow();
             method.label(ifEnd);
         }
+
+        public void literalSwitch(int[] cases, Object bodies, ArrayCallback arrayCallback, CompilerCallback defaultCallback) {
+            // TODO assuming case is a fixnum
+            method.checkcast(p(RubyFixnum.class));
+            method.invokevirtual(p(RubyFixnum.class), &quot;getLongValue&quot;, sig(long.class));
+            method.l2i();
+            
+            Label[] labels = new Label[cases.length];
+            for (int i = 0; i &lt; labels.length; i++) labels[i] = new Label();
+            Label defaultLabel = new Label();
+            Label endLabel = new Label();
+
+            method.lookupswitch(defaultLabel, cases, labels);
+            for (int i = 0; i &lt; cases.length; i ++) {
+                method.label(labels[i]);
+                arrayCallback.nextValue(this, bodies, i);
+                method.go_to(endLabel);
+            }
+
+            method.label(defaultLabel);
+            defaultCallback.call(this);
+            method.label(endLabel);
+        }
+
+        public void typeCheckBranch(Class type, BranchCallback trueCallback, BranchCallback falseCallback) {
+            Label elseLabel = new Label();
+            Label done = new Label();
+
+            method.dup();
+            method.instance_of(p(type));
+            method.ifeq(elseLabel);
+
+            trueCallback.branch(this);
+            method.go_to(done);
+
+            method.label(elseLabel);
+            falseCallback.branch(this);
+
+            method.label(done);
+        }
     }
 
     public class ASMClosureCompiler extends AbstractMethodCompiler {</diff>
      <filename>src/org/jruby/compiler/impl/StandardASMCompiler.java</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>b454ae238ee062a80570b855edc26e92e2daca4c</id>
    </parent>
  </parents>
  <author>
    <name>headius</name>
    <email>headius@961051c9-f516-0410-bf72-c9f7e237a7b7</email>
  </author>
  <url>http://github.com/bobmcwhirter/jruby/commit/59213f5dfaa7c370d87e995170611a253b140384</url>
  <id>59213f5dfaa7c370d87e995170611a253b140384</id>
  <committed-date>2008-09-09T02:25:41-07:00</committed-date>
  <authored-date>2008-09-09T02:25:41-07:00</authored-date>
  <message>Add experimental switch support in compiler for all-int-Fixnum case/when. Could be expanded with more formalization, an invariant hash algorithm for all literals, and more efficient bytecode emission to save the body-doubling.


git-svn-id: http://svn.codehaus.org/jruby/trunk/jruby@7666 961051c9-f516-0410-bf72-c9f7e237a7b7</message>
  <tree>d74ba69ae4f777da853a778cbb9d6add8e61df4f</tree>
  <committer>
    <name>headius</name>
    <email>headius@961051c9-f516-0410-bf72-c9f7e237a7b7</email>
  </committer>
</commit>
