From cf39bc33d765ca0ae128e08b44f38bd41ce8c6cc Mon Sep 17 00:00:00 2001
From: Yusuke Endoh <mame@ruby-lang.org>
Date: Mon, 14 Mar 2016 21:57:43 +0900
Subject: [PATCH 3/3] introduce opt_newarray_max/min for Array#max/min

* compile.c (NODE_CALL): add optimization shortcut for Array#max/min.

* insns.def (opt_newarray_max, opt_newarray_min): added.
---
 compile.c   | 20 ++++++++++++++++++
 defs/id.def |  2 ++
 insns.def   | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vm.c        |  2 ++
 vm_core.h   |  2 ++
 5 files changed, 94 insertions(+)

diff --git a/compile.c b/compile.c
index 701beeb..24ae457 100644
--- a/compile.c
+++ b/compile.c
@@ -4916,6 +4916,26 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 	    }
 	    break;
 	}
+	/* optimization shortcut
+	*   [a, b, ...].max/min -> a, b, c, opt_newarray_max/min
+	*/
+	if (node->nd_recv && nd_type(node->nd_recv) == NODE_ARRAY &&
+	    (node->nd_mid == idMax || node->nd_mid == idMin) && node->nd_args == NULL &&
+	    ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
+	    ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
+	    COMPILE(ret, "recv", node->nd_recv);
+	    if (((INSN*)ret->last)->insn_id == BIN(newarray)) {
+		((INSN*)ret->last)->insn_id = node->nd_mid == idMax ? BIN(opt_newarray_max) :
+		BIN(opt_newarray_min);
+	    }
+	    else {
+		ADD_SEND(ret, line, node->nd_mid, INT2FIX(0));
+	    }
+	    if (poped) {
+		ADD_INSN(ret, line, pop);
+	    }
+	    break;
+	}
       case NODE_QCALL:
       case NODE_FCALL:
       case NODE_VCALL:{		/* VCALL: variable or call */
diff --git a/defs/id.def b/defs/id.def
index e186725..4fe9d3c 100644
--- a/defs/id.def
+++ b/defs/id.def
@@ -1,5 +1,7 @@
 # -*- mode: ruby; coding: us-ascii -*-
 firstline, predefined = __LINE__+1, %[\
+  max
+  min
   freeze
   inspect
   intern
diff --git a/insns.def b/insns.def
index 98871bf..5b08d06 100644
--- a/insns.def
+++ b/insns.def
@@ -986,6 +986,74 @@ opt_str_freeze
     }
 }
 
+DEFINE_INSN
+opt_newarray_max
+(rb_num_t num)
+(...)
+(VALUE val) // inc += 1 - num;
+{
+#define id_cmp  idCmp
+    if (BASIC_OP_UNREDEFINED_P(BOP_MAX, ARRAY_REDEFINED_OP_FLAG)) {
+	if (num == 0) {
+	    val = Qnil;
+	}
+	else {
+	    struct cmp_opt_data cmp_opt = { 0, 0 };
+	    VALUE result = Qundef;
+	    rb_num_t i = num - 1;
+	    result = TOPN(i);
+	    while (i-- > 0) {
+		const VALUE v = TOPN(i);
+		if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) > 0) {
+		    result = v;
+		}
+	    }
+	    val = result == Qundef ? Qnil : result;
+	}
+	POPN(num);
+    }
+    else {
+	VALUE ary = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num));
+	val = rb_funcall(ary, idMax, 0);
+	POPN(num);
+    }
+#undef id_cmp
+}
+
+DEFINE_INSN
+opt_newarray_min
+(rb_num_t num)
+(...)
+(VALUE val) // inc += 1 - num;
+{
+#define id_cmp  idCmp
+    if (BASIC_OP_UNREDEFINED_P(BOP_MIN, ARRAY_REDEFINED_OP_FLAG)) {
+	if (num == 0) {
+	    val = Qnil;
+	}
+	else {
+	    struct cmp_opt_data cmp_opt = { 0, 0 };
+	    VALUE result = Qundef;
+	    rb_num_t i = num - 1;
+	    result = TOPN(i);
+	    while (i-- > 0) {
+		const VALUE v = TOPN(i);
+		if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) < 0) {
+		    result = v;
+		}
+	    }
+	    val = result == Qundef ? Qnil : result;
+	}
+	POPN(num);
+    }
+    else {
+	VALUE ary = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num));
+	val = rb_funcall(ary, idMin, 0);
+	POPN(num);
+    }
+#undef id_cmp
+}
+
 /**
   @c optimize
   @e Invoke method without block
diff --git a/vm.c b/vm.c
index 9057552..7c91eaa 100644
--- a/vm.c
+++ b/vm.c
@@ -1475,6 +1475,8 @@ vm_init_redefined_flag(void)
     OP(Succ, SUCC), (C(Fixnum), C(String), C(Time));
     OP(EqTilde, MATCH), (C(Regexp), C(String));
     OP(Freeze, FREEZE), (C(String));
+    OP(Max, MAX), (C(Array));
+    OP(Min, MIN), (C(Array));
 #undef C
 #undef OP
 }
diff --git a/vm_core.h b/vm_core.h
index 8b5e853..ae6fd61 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -453,6 +453,8 @@ enum ruby_basic_operators {
     BOP_NEQ,
     BOP_MATCH,
     BOP_FREEZE,
+    BOP_MAX,
+    BOP_MIN,
 
     BOP_LAST_
 };
-- 
2.5.3

