Index: eval.c
===================================================================
RCS file: /home/cvs/ruby/eval.c,v
retrieving revision 1.106
diff -u -r1.106 eval.c
--- eval.c	2000/09/18 16:28:34	1.106
+++ eval.c	2000/09/19 03:38:55
@@ -97,6 +97,8 @@
 #define SCOPE_SET(f)  do {scope_vmode=(f);} while(0)
 #define SCOPE_TEST(f) (scope_vmode&(f))
 
+int ruby_optimize_level = 0;
+
 int ruby_safe_level = 0;
 /* safe-level:
    0 - strings from streams/environment/ARGV are tainted (default)
@@ -504,6 +506,11 @@
 static struct FRAME *top_frame;
 static struct SCOPE *top_scope;
 
+static VALUE tail_call_recv = Qnil;
+static ID tail_call_mid = 0;
+static VALUE tail_call_args = Qnil;
+static int tail_call_scope = 0;
+
 #define PUSH_FRAME() {			\
     struct FRAME _frame;		\
     _frame.prev = ruby_frame;		\
@@ -770,6 +777,7 @@
 #define TAG_RAISE	0x6
 #define TAG_THROW	0x7
 #define TAG_FATAL	0x8
+#define TAG_TAIL_CALL	0x9
 #define TAG_MASK	0xf
 
 VALUE ruby_class;
@@ -1915,6 +1923,9 @@
 static void return_check _((void));
 #define return_value(v) prot_tag->retval = (v)
 
+static void optimize_tail_call _((NODE*));
+static void optimize_return _((NODE*));
+
 static VALUE
 rb_eval(self, n)
     VALUE self;
@@ -2416,6 +2427,53 @@
 	result = rb_call(CLASS_OF(self),self,node->nd_mid,0,0,2);
 	break;
 
+      case NODE_TAIL_CALL:
+        {
+	  VALUE recv;
+	  int argc; VALUE *argv; /* used in SETUP_ARGS */
+	  TMP_PROTECT;
+
+	  return_check();
+	  BEGIN_CALLARGS;
+	  recv = rb_eval(self, node->nd_recv);
+	  SETUP_ARGS(node->nd_args);
+	  END_CALLARGS;
+	  tail_call_recv = recv;
+	  tail_call_mid = node->nd_mid;
+	  tail_call_args = rb_ary_new4(argc, argv);
+	  tail_call_scope = 0;
+	  TMP_PROTECT_END;
+	  JUMP_TAG(TAG_TAIL_CALL);
+        }
+	break;
+
+      case NODE_TAIL_FCALL:
+        {
+	  int argc; VALUE *argv; /* used in SETUP_ARGS */
+	  TMP_PROTECT;
+
+	  return_check();
+	  BEGIN_CALLARGS;
+	  SETUP_ARGS(node->nd_args);
+	  END_CALLARGS;
+	  tail_call_recv = self;
+	  tail_call_mid = node->nd_mid;
+	  tail_call_args = rb_ary_new4(argc, argv);
+	  tail_call_scope = 1;
+	  TMP_PROTECT_END;
+	  JUMP_TAG(TAG_TAIL_CALL);
+        }
+	break;
+
+      case NODE_TAIL_VCALL:
+	return_check();
+	tail_call_recv = self;
+	tail_call_mid = node->nd_mid;
+	tail_call_args = Qnil;
+	tail_call_scope = 2;
+	JUMP_TAG(TAG_TAIL_CALL);
+	break;
+
       case NODE_SUPER:
       case NODE_ZSUPER:
 	{
@@ -2847,6 +2905,10 @@
 	    if (body && origin == ruby_class && body->nd_noex & NOEX_UNDEF) {
 		noex |= NOEX_UNDEF;
 	    }
+	    if (ruby_optimize_level > 0) {
+		optimize_tail_call(node->nd_defn);
+		optimize_return(node->nd_defn);
+	    }
 	    rb_add_method(ruby_class, node->nd_mid, node->nd_defn, noex);
 	    if (scope_vmode == SCOPE_MODFUNC) {
 		rb_add_method(rb_singleton_class(ruby_class),
@@ -4309,45 +4371,86 @@
     VALUE *argv;		/* OK */
     int scope;
 {
+    VALUE result;
     NODE  *body;		/* OK */
     int    noex;
-    ID     id = mid;
+    ID     id;
     struct cache_entry *ent;
+    int state;
 
-    /* is it in the method cache? */
-    ent = cache + EXPR1(klass, mid);
-    if (ent->mid == mid && ent->klass == klass) {
-	if (!ent->method)
-	    return rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
-	klass = ent->origin;
-	id    = ent->mid0;
-	noex  = ent->noex;
-	body  = ent->method;
-    }
-    else if ((body = rb_get_method_body(&klass, &id, &noex)) == 0) {
-	if (scope == 3) {
-	    rb_raise(rb_eNameError, "super: no superclass method `%s'",
-		     rb_id2name(mid));
-	}
-	return rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
-    }
-
-    if (mid != missing) {
-	/* receiver specified form for private method */
-	if ((noex & NOEX_PRIVATE) && scope == 0)
-	    return rb_undefined(recv, mid, argc, argv, CSTAT_PRIV);
-
-	/* self must be kind of a specified form for private method */
-	if ((noex & NOEX_PROTECTED)) {
-	    VALUE defined_class = klass;
-	    while (TYPE(defined_class) == T_ICLASS)
-		defined_class = RBASIC(defined_class)->klass;
-	    if (!rb_obj_is_kind_of(ruby_frame->self, defined_class))
-		return rb_undefined(recv, mid, argc, argv, CSTAT_PROT);
+ again:
+    id = mid;
+    PUSH_TAG(PROT_NONE);
+    if ((state = EXEC_TAG()) == 0) {
+	/* is it in the method cache? */
+	ent = cache + EXPR1(klass, mid);
+	if (ent->mid == mid && ent->klass == klass) {
+	    if (!ent->method) {
+		result = rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+		goto end;
+	    }
+	    klass = ent->origin;
+	    id    = ent->mid0;
+	    noex  = ent->noex;
+	    body  = ent->method;
+	}
+	else if ((body = rb_get_method_body(&klass, &id, &noex)) == 0) {
+	    if (scope == 3) {
+		rb_raise(rb_eNameError, "super: no superclass method `%s'",
+			 rb_id2name(mid));
+	    }
+	    result = rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+	    goto end;
+	}
+
+	if (mid != missing) {
+	    /* receiver specified form for private method */
+	    if ((noex & NOEX_PRIVATE) && scope == 0) {
+		result = rb_undefined(recv, mid, argc, argv, CSTAT_PRIV);
+		goto end;
+	    }
+	    
+	    /* self must be kind of a specified form for private method */
+	    if ((noex & NOEX_PROTECTED)) {
+		VALUE defined_class = klass;
+		while (TYPE(defined_class) == T_ICLASS)
+		    defined_class = RBASIC(defined_class)->klass;
+		if (!rb_obj_is_kind_of(ruby_frame->self, defined_class)) {
+		    result = rb_undefined(recv, mid, argc, argv, CSTAT_PROT);
+		    goto end;
+		}
+	    }
 	}
+
+	result = rb_call0(klass, recv, id, argc, argv, body, noex & NOEX_UNDEF);
     }
+ end:
+    POP_TAG();
+    switch (state) {
+    case 0:
+	return result;
+    case TAG_TAIL_CALL:
+	recv = tail_call_recv;
+	klass = CLASS_OF(recv);
+	mid = tail_call_mid;
+	if (NIL_P(tail_call_args)) {
+	    argc = 0;
+	    argv = 0;
+	}
+	else {
+	    argc = RARRAY(tail_call_args)->len;
+	    argv = RARRAY(tail_call_args)->ptr;
+	}
+	scope = tail_call_scope;
 
-    return rb_call0(klass, recv, id, argc, argv, body, noex & NOEX_UNDEF);
+	tail_call_recv = Qnil;
+	tail_call_mid = 0;
+	tail_call_args = Qnil;
+	tail_call_scope = 0;
+	goto again;
+    default:
+	JUMP_TAG(state);
+    }
 }
 
 VALUE
@@ -5614,6 +5717,9 @@
     rb_global_variable((VALUE*)&ruby_eval_tree);
     rb_global_variable((VALUE*)&ruby_dyna_vars);
 
+    rb_global_variable(&tail_call_recv);
+    rb_global_variable(&tail_call_args);
+
     rb_define_virtual_variable("$@", errat_getter, errat_setter);
     rb_define_hooked_variable("$!", &ruby_errinfo, 0, errinfo_setter);
 
@@ -8410,3 +8516,137 @@
     }
 }
 
+static void
+optimize_tail_call(node)
+    NODE *node;
+{
+  again:
+    if (!node) return;
+
+    switch (nd_type(node)) {
+      case NODE_SCOPE:
+	node = node->nd_next;
+	goto again;
+
+      case NODE_BLOCK:
+	while (node->nd_next) {
+	    node = node->nd_next;
+	}
+	node = node->nd_head;
+	goto again;
+
+      case NODE_BEGIN:
+	node = node->nd_body;
+	goto again;
+
+      case NODE_NEWLINE:
+	node = node->nd_next;
+	goto again;
+
+      case NODE_IF:
+	optimize_tail_call(node->nd_body);
+	optimize_tail_call(node->nd_else);
+	break;
+
+      case NODE_CASE:
+	node = node->nd_body;
+	while (node) {
+	    if (nd_type(node) != NODE_WHEN)
+		goto again;
+	    optimize_tail_call(node->nd_body);
+	    node = node->nd_next;
+	}
+	break;
+
+      case NODE_CALL:
+	nd_set_type(node, NODE_TAIL_CALL);
+	break;
+
+      case NODE_FCALL:
+	nd_set_type(node, NODE_TAIL_FCALL);
+	break;
+
+      case NODE_VCALL:
+	nd_set_type(node, NODE_TAIL_VCALL);
+	break;
+
+      case NODE_RESCUE:
+      case NODE_ENSURE:
+      default:
+	break;
+    }
+}
+
+static void
+optimize_return(node)
+    NODE *node;
+{
+  again:
+    if (!node) return;
+
+    switch (nd_type(node)) {
+      case NODE_SCOPE:
+	node = node->nd_next;
+	goto again;
+
+      case NODE_BLOCK:
+	while (node->nd_next) {
+	    optimize_return(node->nd_head);
+	    node = node->nd_next;
+	}
+	node = node->nd_head;
+	goto again;
+
+      case NODE_BEGIN:
+	node = node->nd_body;
+	goto again;
+
+      case NODE_NEWLINE:
+	node = node->nd_next;
+	goto again;
+
+      case NODE_IF:
+	optimize_return(node->nd_body);
+	optimize_return(node->nd_else);
+	break;
+
+      case NODE_CASE:
+	node = node->nd_body;
+	while (node) {
+	    if (nd_type(node) != NODE_WHEN)
+		goto again;
+	    optimize_return(node->nd_body);
+	    node = node->nd_next;
+	}
+	break;
+
+      case NODE_WHILE:
+      case NODE_UNTIL:
+      case NODE_FOR:
+	node = node->nd_body;
+	goto again;
+
+      case NODE_RETURN:
+	if (node->nd_stts) {
+	    node = node->nd_stts;
+	    switch (nd_type(node)) {
+	      case NODE_CALL:
+		nd_set_type(node, NODE_TAIL_CALL);
+		break;
+	      case NODE_FCALL:
+		nd_set_type(node, NODE_TAIL_FCALL);
+		break;
+	      case NODE_VCALL:
+		nd_set_type(node, NODE_TAIL_VCALL);
+		break;
+	    }
+	}
+	break;
+
+      case NODE_ITER:		/* maybe catch */
+      case NODE_RESCUE:
+      case NODE_ENSURE:
+      default:
+	break;
+    }
+}
Index: node.h
===================================================================
RCS file: /home/cvs/ruby/node.h,v
retrieving revision 1.16
diff -u -r1.16 node.h
--- node.h	2000/09/07 06:59:36	1.16
+++ node.h	2000/09/19 03:38:55
@@ -58,6 +58,9 @@
     NODE_CALL,
     NODE_FCALL,
     NODE_VCALL,
+    NODE_TAIL_CALL,
+    NODE_TAIL_FCALL,
+    NODE_TAIL_VCALL,
     NODE_SUPER,
     NODE_ZSUPER,
     NODE_ARRAY,
Index: ruby.c
===================================================================
RCS file: /home/cvs/ruby/ruby.c,v
retrieving revision 1.32
diff -u -r1.32 ruby.c
--- ruby.c	2000/08/15 06:22:42	1.32
+++ ruby.c	2000/09/19 03:38:55
@@ -554,6 +554,19 @@
 	    }
 	    break;
 
+	  case 'O':
+	    {
+		int numlen;
+		int v = 1;
+
+		if (*++s) {
+		    v = scan_oct(s, 2, &numlen);
+		    if (numlen == 0) v = 1;
+		}
+		ruby_optimize_level = v;
+	    }
+	    break;
+
 	  case '0':
 	    {
 		int numlen;
Index: ruby.h
===================================================================
RCS file: /home/cvs/ruby/ruby.h,v
retrieving revision 1.25
diff -u -r1.25 ruby.h
--- ruby.h	2000/09/15 06:00:28	1.25
+++ ruby.h	2000/09/19 03:38:55
@@ -185,6 +185,8 @@
 #define rb_safe_level() (ruby_safe_level)
 void rb_set_safe_level _((int));
 
+EXTERN int ruby_optimize_level;
+
 long rb_num2long _((VALUE));
 unsigned long rb_num2ulong _((VALUE));
 #define NUM2LONG(x) (FIXNUM_P(x)?FIX2LONG(x):rb_num2long((VALUE)x))
