root/blockinlining.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. iseq_special_block
  2. new_block
  3. new_ary
  4. new_assign
  5. build_Integer_times_node
  6. invoke_Integer_times_special_block
  7. build_Range_each_node
  8. build_Range_each_node_LE
  9. build_Range_each_node_LT
  10. invoke_Range_each_special_block
  11. build_Array_each_node
  12. invoke_Array_each_special_block

/**********************************************************************

  blockinlining.c -

  $Author: yugui $

  Copyright (C) 2004-2007 Koichi Sasada

**********************************************************************/

#include "ruby/ruby.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(rb_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;
}

/* [previous][next][first][last][top][bottom][index][help] */