summaryrefslogtreecommitdiff
path: root/trunk/blockinlining.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/blockinlining.c')
-rw-r--r--trunk/blockinlining.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/trunk/blockinlining.c b/trunk/blockinlining.c
new file mode 100644
index 0000000000..3f4aa86fe5
--- /dev/null
+++ b/trunk/blockinlining.c
@@ -0,0 +1,465 @@
+/**********************************************************************
+
+ blockinlining.c -
+
+ $Author$
+
+ Copyright (C) 2004-2007 Koichi Sasada
+
+**********************************************************************/
+
+#include "ruby/ruby.h"
+#include "ruby/node.h"
+#include "vm_core.h"
+
+static VALUE
+iseq_special_block(rb_iseq_t *iseq, void *builder)
+{
+#if OPT_BLOCKINLINING
+ VALUE parent = Qfalse;
+ VALUE iseqval;
+
+ if (iseq->argc > 1 || iseq->arg_simple == 0) {
+ /* argument check */
+ return 0;
+ }
+
+ if (iseq->cached_special_block_builder) {
+ if (iseq->cached_special_block_builder == builder) {
+ return iseq->cached_special_block;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ iseq->cached_special_block_builder = (void *)1;
+ }
+
+ if (iseq->parent_iseq) {
+ parent = iseq->parent_iseq->self;
+ }
+ iseqval = rb_iseq_new_with_bopt(iseq->node, iseq->name, iseq->filename,
+ parent, iseq->type,
+ GC_GUARDED_PTR(builder));
+ if (0) {
+ printf("%s\n", RSTRING_PTR(ruby_iseq_disasm(iseqval)));
+ }
+ iseq->cached_special_block = iseqval;
+ iseq->cached_special_block_builder = builder;
+ return iseqval;
+#else
+ return 0;
+#endif
+}
+
+static NODE *
+new_block(NODE * head, NODE * tail)
+{
+ head = NEW_BLOCK(head);
+ tail = NEW_BLOCK(tail);
+ head->nd_next = tail;
+ return head;
+}
+
+static NODE *
+new_ary(NODE * head, NODE * tail)
+{
+ head = NEW_ARRAY(head);
+ head->nd_next = tail;
+ return head;
+}
+
+static NODE *
+new_assign(NODE * lnode, NODE * rhs)
+{
+ switch (nd_type(lnode)) {
+ case NODE_LASGN:{
+ return NEW_NODE(NODE_LASGN, lnode->nd_vid, rhs, lnode->nd_cnt);
+ /* NEW_LASGN(lnode->nd_vid, rhs); */
+ }
+ case NODE_GASGN:{
+ return NEW_GASGN(lnode->nd_vid, rhs);
+ }
+ case NODE_DASGN:{
+ return NEW_DASGN(lnode->nd_vid, rhs);
+ }
+ case NODE_ATTRASGN:{
+ NODE *args = 0;
+ if (lnode->nd_args) {
+ args = NEW_ARRAY(lnode->nd_args->nd_head);
+ args->nd_next = NEW_ARRAY(rhs);
+ args->nd_alen = 2;
+ }
+ else {
+ args = NEW_ARRAY(rhs);
+ }
+
+ return NEW_ATTRASGN(lnode->nd_recv,
+ lnode->nd_mid,
+ args);
+ }
+ default:
+ rb_bug("unimplemented (block inlining): %s", ruby_node_name(nd_type(lnode)));
+ }
+ return 0;
+}
+
+static NODE *
+build_Integer_times_node(rb_iseq_t *iseq, NODE * node, NODE * lnode,
+ VALUE param_vars, VALUE local_vars)
+{
+ /* Special Block for Integer#times
+ {|e, _self|
+ _e = e
+ while(e < _self)
+ e = _e
+ redo_point:
+ BODY
+ next_point:
+ _e = _e.succ
+ end
+ }
+
+ {|e, _self|
+ while(e < _self)
+ BODY
+ next_point:
+ e = e.succ
+ end
+ }
+ */
+ ID _self;
+ CONST_ID(_self, "#_self");
+ if (iseq->argc == 0) {
+ ID e;
+ CONST_ID(e, "#e");
+ rb_ary_push(param_vars, ID2SYM(e));
+ rb_ary_push(param_vars, ID2SYM(_self));
+ iseq->argc += 2;
+
+ node =
+ NEW_WHILE(NEW_CALL
+ (NEW_DVAR(e), idLT, new_ary(NEW_DVAR(_self), 0)),
+ new_block(NEW_OPTBLOCK(node),
+ NEW_DASGN(e,
+ NEW_CALL(NEW_DVAR(e), idSucc, 0))),
+ Qundef);
+ }
+ else {
+ ID _e;
+ ID e = SYM2ID(rb_ary_entry(param_vars, 0));
+ NODE *assign;
+
+ CONST_ID(_e, "#_e");
+ rb_ary_push(param_vars, ID2SYM(_self));
+ rb_ary_push(local_vars, ID2SYM(_e));
+ iseq->argc++;
+
+ if (nd_type(lnode) == NODE_DASGN_CURR) {
+ assign = NEW_DASGN(e, NEW_DVAR(_e));
+ }
+ else {
+ assign = new_assign(lnode, NEW_DVAR(_e));
+ }
+
+ node =
+ new_block(NEW_DASGN(_e, NEW_DVAR(e)),
+ NEW_WHILE(NEW_CALL
+ (NEW_DVAR(_e), idLT,
+ new_ary(NEW_DVAR(_self), 0)),
+ new_block(assign,
+ new_block(NEW_OPTBLOCK(node),
+ NEW_DASGN(_e,
+ NEW_CALL
+ (NEW_DVAR(_e),
+ idSucc, 0)))),
+ Qundef));
+ }
+ return node;
+}
+
+VALUE
+invoke_Integer_times_special_block(VALUE num)
+{
+ rb_thread_t *th = GET_THREAD();
+ rb_block_t *orig_block = GC_GUARDED_PTR_REF(th->cfp->lfp[0]);
+
+ if (orig_block && BUILTIN_TYPE(orig_block->iseq) != T_NODE) {
+ VALUE tsiseqval = iseq_special_block(orig_block->iseq,
+ build_Integer_times_node);
+ rb_iseq_t *tsiseq;
+ VALUE argv[2], val;
+
+ if (tsiseqval) {
+ rb_block_t block = *orig_block;
+ GetISeqPtr(tsiseqval, tsiseq);
+ block.iseq = tsiseq;
+ th->cfp->lfp[0] = GC_GUARDED_PTR(&block);
+ argv[0] = INT2FIX(0);
+ argv[1] = num;
+ val = rb_yield_values(2, argv);
+ if (val == Qundef) {
+ return num;
+ }
+ else {
+ return val;
+ }
+ }
+ }
+ return Qundef;
+}
+
+static NODE *
+build_Range_each_node(rb_iseq_t *iseq, NODE * node, NODE * lnode,
+ VALUE param_vars, VALUE local_vars, ID mid)
+{
+ /* Special Block for Range#each
+ {|e, _last|
+ _e = e
+ while _e < _last
+ e = _e
+ next_point:
+ BODY
+ redo_point:
+ _e = _e.succ
+ end
+ }
+ {|e, _last|
+ while e < _last
+ BODY
+ redo_point:
+ e = e.succ
+ end
+ }
+ */
+ ID _last;
+ CONST_ID(_last, "#_last");
+ if (iseq->argc == 0) {
+ ID e;
+ CONST_ID(e, "#e");
+ rb_ary_push(param_vars, ID2SYM(e));
+ rb_ary_push(param_vars, ID2SYM(_last));
+ iseq->argc += 2;
+
+ node =
+ NEW_WHILE(NEW_CALL(NEW_DVAR(e), mid, new_ary(NEW_DVAR(_last), 0)),
+ new_block(NEW_OPTBLOCK(node),
+ NEW_DASGN(e,
+ NEW_CALL(NEW_DVAR(e), idSucc, 0))),
+ Qundef);
+ }
+ else {
+ ID _e;
+ ID e = SYM2ID(rb_ary_entry(param_vars, 0));
+ NODE *assign;
+
+ CONST_ID(_e, "#_e");
+ rb_ary_push(param_vars, ID2SYM(_last));
+ rb_ary_push(local_vars, ID2SYM(_e));
+ iseq->argc++;
+
+ if (nd_type(lnode) == NODE_DASGN_CURR) {
+ assign = NEW_DASGN(e, NEW_DVAR(_e));
+ }
+ else {
+ assign = new_assign(lnode, NEW_DVAR(_e));
+ }
+
+ node =
+ new_block(NEW_DASGN(_e, NEW_DVAR(e)),
+ NEW_WHILE(NEW_CALL
+ (NEW_DVAR(_e), mid,
+ new_ary(NEW_DVAR(_last), 0)),
+ new_block(assign,
+ new_block(NEW_OPTBLOCK(node),
+ NEW_DASGN(_e,
+ NEW_CALL
+ (NEW_DVAR(_e),
+ idSucc, 0)))),
+ Qundef));
+ }
+ return node;
+}
+
+static NODE *
+build_Range_each_node_LE(rb_iseq_t *iseq, NODE * node, NODE * lnode,
+ VALUE param_vars, VALUE local_vars)
+{
+ return build_Range_each_node(iseq, node, lnode,
+ param_vars, local_vars, idLE);
+}
+
+static NODE *
+build_Range_each_node_LT(rb_iseq_t *iseq, NODE * node, NODE * lnode,
+ VALUE param_vars, VALUE local_vars)
+{
+ return build_Range_each_node(iseq, node, lnode,
+ param_vars, local_vars, idLT);
+}
+
+VALUE
+invoke_Range_each_special_block(VALUE range,
+ VALUE beg, VALUE end, int excl)
+{
+ rb_thread_t *th = GET_THREAD();
+ rb_block_t *orig_block = GC_GUARDED_PTR_REF(th->cfp->lfp[0]);
+
+ if (BUILTIN_TYPE(orig_block->iseq) != T_NODE) {
+ void *builder =
+ excl ? build_Range_each_node_LT : build_Range_each_node_LE;
+ VALUE tsiseqval = iseq_special_block(orig_block->iseq, builder);
+ rb_iseq_t *tsiseq;
+ VALUE argv[2];
+
+ if (tsiseqval) {
+ VALUE val;
+ rb_block_t block = *orig_block;
+ GetISeqPtr(tsiseqval, tsiseq);
+ block.iseq = tsiseq;
+ th->cfp->lfp[0] = GC_GUARDED_PTR(&block);
+ argv[0] = beg;
+ argv[1] = end;
+ val = rb_yield_values(2, argv);
+ if (val == Qundef) {
+ return range;
+ }
+ else {
+ return val;
+ }
+ }
+ }
+ return Qundef;
+}
+
+
+static NODE *
+build_Array_each_node(rb_iseq_t *iseq, NODE * node, NODE * lnode,
+ VALUE param_vars, VALUE local_vars)
+{
+ /* Special block for Array#each
+ ary.each{|e|
+ BODY
+ }
+ =>
+ {|e, _self|
+ _i = 0
+ while _i < _self.length
+ e = _self[_i]
+ redo_point:
+ BODY
+ next_point:
+ _i = _i.succ
+ end
+ }
+
+ ary.each{
+ BODY
+ }
+ =>
+ {|_i, _self|
+ _i = 0
+ while _i < _self.length
+ redo_point:
+ BODY
+ next_point:
+ _i = _i.succ
+ end
+ }
+ */
+
+ ID _self, _i;
+
+ CONST_ID(_self, "#_self");
+ CONST_ID(_i, "#_i");
+ if (iseq->argc == 0) {
+ ID _e;
+ CONST_ID(_e, "#_e");
+ rb_ary_push(param_vars, ID2SYM(_e));
+ rb_ary_push(param_vars, ID2SYM(_self));
+ iseq->argc += 2;
+ rb_ary_push(local_vars, ID2SYM(_i));
+
+ node =
+ new_block(NEW_DASGN(_i, NEW_LIT(INT2FIX(0))),
+ NEW_WHILE(NEW_CALL(NEW_DVAR(_i), idLT,
+ new_ary(NEW_CALL
+ (NEW_DVAR(_self), idLength,
+ 0), 0)),
+ new_block(NEW_OPTBLOCK(node),
+ NEW_DASGN(_i,
+ NEW_CALL(NEW_DVAR(_i),
+ idSucc, 0))),
+ Qundef));
+ }
+ else {
+ ID e = SYM2ID(rb_ary_entry(param_vars, 0));
+ NODE *assign;
+
+ rb_ary_push(param_vars, ID2SYM(_self));
+ iseq->argc++;
+ rb_ary_push(local_vars, ID2SYM(_i));
+
+ if (nd_type(lnode) == NODE_DASGN_CURR) {
+ assign = NEW_DASGN(e,
+ NEW_CALL(NEW_DVAR(_self), idAREF,
+ new_ary(NEW_DVAR(_i), 0)));
+ }
+ else {
+ assign = new_assign(lnode,
+ NEW_CALL(NEW_DVAR(_self), idAREF,
+ new_ary(NEW_DVAR(_i), 0)));
+ }
+
+ node =
+ new_block(NEW_DASGN(_i, NEW_LIT(INT2FIX(0))),
+ NEW_WHILE(NEW_CALL(NEW_DVAR(_i), idLT,
+ new_ary(NEW_CALL
+ (NEW_DVAR(_self), idLength,
+ 0), 0)), new_block(assign,
+ new_block
+ (NEW_OPTBLOCK
+ (node),
+ NEW_DASGN
+ (_i,
+ NEW_CALL
+ (NEW_DVAR
+ (_i),
+ idSucc,
+ 0)))),
+ Qundef));
+ }
+ return node;
+}
+
+VALUE
+invoke_Array_each_special_block(VALUE ary)
+{
+ rb_thread_t *th = GET_THREAD();
+ rb_block_t *orig_block = GC_GUARDED_PTR_REF(th->cfp->lfp[0]);
+
+ if (BUILTIN_TYPE(orig_block->iseq) != T_NODE) {
+ VALUE tsiseqval = iseq_special_block(orig_block->iseq,
+ build_Array_each_node);
+ rb_iseq_t *tsiseq;
+ VALUE argv[2];
+
+ if (tsiseqval) {
+ VALUE val;
+ rb_block_t block = *orig_block;
+ GetISeqPtr(tsiseqval, tsiseq);
+ block.iseq = tsiseq;
+ th->cfp->lfp[0] = GC_GUARDED_PTR(&block);
+ argv[0] = 0;
+ argv[1] = ary;
+ val = rb_yield_values(2, argv);
+ if (val == Qundef) {
+ return ary;
+ }
+ else {
+ return val;
+ }
+ }
+ }
+ return Qundef;
+}