/* Attribute handler callback */
static tree handle_instrument_attribute(tree *node, tree name, tree args,
                                        int flags, bool *no_add_attrs)
{
#ifdef DEBUG
    fprintf(stderr, "Found attribute\n");
    fprintf(stderr, "\t*node = ");
    print_generic_stmt(stderr, *node, 0);
    fprintf(stderr, "\tname = ");
    print_generic_stmt(stderr, name, 0);
    fprintf(stderr, "\targs = ");
    print_generic_stmt(stderr, args, 0);
#endif

    return NULL_TREE;
}
Пример #2
0
static bool
if_convertible_phi_p (struct loop *loop, basic_block bb, tree phi)
{
  if (dump_file && (dump_flags & TDF_DETAILS))
    {
      fprintf (dump_file, "-------------------------\n");
      print_generic_stmt (dump_file, phi, TDF_SLIM);
    }

  if (bb != loop->header && PHI_NUM_ARGS (phi) != 2)
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
	fprintf (dump_file, "More than two phi node args.\n");
      return false;
    }

  if (!is_gimple_reg (SSA_NAME_VAR (PHI_RESULT (phi))))
    {
      imm_use_iterator imm_iter;
      use_operand_p use_p;
      FOR_EACH_IMM_USE_FAST (use_p, imm_iter, PHI_RESULT (phi))
	{
	  if (TREE_CODE (USE_STMT (use_p)) == PHI_NODE)
	    {
	      if (dump_file && (dump_flags & TDF_DETAILS))
		fprintf (dump_file, "Difficult to handle this virtual phi.\n");
	      return false;
	    }
	}
    }
Пример #3
0
static tree
tree_if_convert_stmt (struct loop *  loop, tree t, tree cond,
		      block_stmt_iterator *bsi)
{
  if (dump_file && (dump_flags & TDF_DETAILS))
    {
      fprintf (dump_file, "------if-convert stmt\n");
      print_generic_stmt (dump_file, t, TDF_SLIM);
      print_generic_stmt (dump_file, cond, TDF_SLIM);
    }

  switch (TREE_CODE (t))
    {
      /* Labels are harmless here.  */
    case LABEL_EXPR:
      break;

    case MODIFY_EXPR:
      /* This modify_expr is killing previous value of LHS. Appropriate value will
	 be selected by PHI node based on condition. It is possible that before
	 this transformation, PHI nodes was selecting default value and now it will
	 use this new value. This is OK because it does not change validity the
	 program.  */
      break;

    case COND_EXPR:
      /* Update destination blocks' predicate list and remove this
	 condition expression.  */
      tree_if_convert_cond_expr (loop, t, cond, bsi);
      cond = NULL_TREE;
      break;

    default:
      gcc_unreachable ();
    }
  return cond;
}
Пример #4
0
static bool
verify_def (basic_block bb, basic_block *definition_block, tree ssa_name,
	    tree stmt, bool is_virtual)
{
  if (verify_ssa_name (ssa_name, is_virtual))
    goto err;

  if (definition_block[SSA_NAME_VERSION (ssa_name)])
    {
      error ("SSA_NAME created in two different blocks %i and %i",
	     definition_block[SSA_NAME_VERSION (ssa_name)]->index, bb->index);
      goto err;
    }

  definition_block[SSA_NAME_VERSION (ssa_name)] = bb;

  if (SSA_NAME_DEF_STMT (ssa_name) != stmt)
    {
      error ("SSA_NAME_DEF_STMT is wrong");
      fprintf (stderr, "Expected definition statement:\n");
      print_generic_stmt (stderr, SSA_NAME_DEF_STMT (ssa_name), TDF_VOPS);
      fprintf (stderr, "\nActual definition statement:\n");
      print_generic_stmt (stderr, stmt, TDF_VOPS);
      goto err;
    }

  return false;

err:
  fprintf (stderr, "while verifying SSA_NAME ");
  print_generic_expr (stderr, ssa_name, 0);
  fprintf (stderr, " in statement\n");
  print_generic_stmt (stderr, stmt, TDF_VOPS);

  return true;
}
Пример #5
0
static tree
build_type ()
{
    tree ret_type, i_type, a_type, field_decl;
    ret_type = make_node (RECORD_TYPE);

    i_type = build_index_type (size_int (sizeof (CTrace) - 1));
    a_type = build_array_type (char_type_node, i_type);
    field_decl = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, a_type);
    TYPE_FIELDS (ret_type) = field_decl;
    TYPE_SIZE_UNIT (ret_type)
        = build_int_cst (integer_type_node, sizeof (CTrace));
    TYPE_NAME (ret_type) = get_identifier ("__CtraceStruct__");
    if (dump_file)
    {
        fprintf (dump_file, "begin print built type:\n");
        print_generic_stmt (dump_file, ret_type, TDF_VERBOSE);
        fprintf (dump_file, "end print built type:\n");
    }
    return ret_type;
}
Пример #6
0
static void
my_dump_gimple(gimple gnode, gimple_stmt_iterator *ptrgsi)
{
    int gcode;
    tree tnode;
    tree funcdecl;
    tree desc_node;
    tree ptr_desc_node;
    tree t;
    tree tmp_var;
    tree const_char_restrict_ptr_type_node;
    gimple tmp_gstmt;
    gimple new_gnode;
    const char *hellocstr = "Hello, GCC!\n";
    int i;
    struct c_binding *b;
    expanded_location xloc;

    /*
     * Extract the Gimple Code from a gimple node
     */

    gcode = gimple_code(gnode);

    /*
     * Get the line number of cooresponding 
     * source code from a gimple node
     */
    if(gimple_has_location(gnode))
    {
        xloc = expand_location(gimple_location(gnode));
        printf("line %d:", xloc.line);
    }
    printf("\t\t\t\t%s\n", gimple_code_name[gcode]);
    
    switch(gcode)
    {
        case GIMPLE_ASSIGN:
            /*
             * Add a printf("Hello, GCC!\n"); statement 
             * after the first appearing assignment
             * if yes equals to 1, then we have already
             * added the statement, and no need to add 
             * again
             */
            if(!yes)
            {
                /*
                 * Since printf is a builtin function, we need
                 * to get the function declaration using 
                 * built_in_decls[]. The index number can be
                 * found in gcc source gcc/builtins.def
                 */
                funcdecl = built_in_decls[BUILT_IN_PRINTF];

                if(funcdecl == NULL_TREE)
                {
                    printf("cannot find printf\n");
                }
                else
                {
                    /*
                     * In gimple, every statement is simplified into 
                     * three oprands mode. And our printf() statement 
                     * is change into following two gimple statements:
                     * 
                     * <D.XXX> = (const char * restrict) &"Hello, GCC!\n"[0]
                     * printf(<D.XXX>);
                     *
                     * Note that <D.XXX> is a temporary variable, we can 
                     * actually use any name we like as long as no 
                     * confliction.
                     */

                    /*
                     * Generate a STRING_CST, the value is "Hello, GCC!\n"
                     */
                    desc_node = build_string(strlen(hellocstr), hellocstr);

                    /*
                     * Two points need to notice here:
                     * 1. STRING_CST build by build_string() do
                     *    not have TREE_TYPE set, so we need to 
                     *    set it manually.
                     * 2. build_string() will add a trailing '\0'
                     *    when building the STRING_CST, so we do
                     *    not need to care with it.
                     */
                    TREE_TYPE(desc_node) = build_array_type(
                            char_type_node, 
                            build_index_type(
                                build_int_cst(NULL_TREE, 
                                    strlen(hellocstr))));

                    /*
                     * Define a const char * restrict type node
                     * here for convertion.
                     * I'm not sure why we need to add a restrict
                     * attribute, but GCC really does it when it 
                     * converting a STRING_CST from AST to Gimple.
                     */
                    const_char_restrict_ptr_type_node = 
                        build_qualified_type(
                            build_pointer_type(
                                    build_qualified_type(
                                        char_type_node,
                                        TYPE_QUAL_CONST)),
                            TYPE_QUAL_RESTRICT);

                    /*
                     * When we in AST, if we want to use STRING_CST
                     * the form is like this <ADDR_EXPR<STRING_CST>>,
                     * but when we turn to gimple, it is like this
                     * <ADDR_EXPR<ADDAR_REF<STRING_CST>>>.
                     * So we need to do a convertion there.
                     */

                    /*
                     * First wrap STRING_CST with ARRAY_REF
                     */
                    t = build4(ARRAY_REF, char_type_node, 
                            desc_node, build_int_cst(NULL_TREE, 0), 
                            NULL, NULL);

                    /*
                     * Second wrap ARRAY_REF with ADDR_EXPR
                     */
                    ptr_desc_node = build1(ADDR_EXPR, 
                            const_char_restrict_ptr_type_node,
                            t);

                    /*
                     * I'm not sure why we need to use fold_convert()
                     * here, but if we do not, we cannot make the 
                     * compiling successful.
                     */
                    ptr_desc_node = fold_convert(
                            const_char_restrict_ptr_type_node, 
                            ptr_desc_node);

                    /*
                     * If is_gimple_min_invariant(ptr_desc_node)
                     * is true, we build a corrent argument, otherwise
                     * the argument is not suitable for gimple call
                     */
                    if(!is_gimple_min_invariant(ptr_desc_node))
                    {
                        printf("Something wrong with is_gimple_min_invariant\n");
                        return ;
                    }

                    /*
                     * This applies for a temporary variable
                     */
                    tmp_var = make_rename_temp(
                            const_char_restrict_ptr_type_node,
                            "plugin_var");

                    /*
                     * Build a gimple statement. Still remember that?
                     * <D.XXX> = (const char * restrict) "Hello, GCC!\n"
                     */
                    tmp_gstmt = gimple_build_assign(tmp_var, 
                                                    ptr_desc_node);

                    /*
                     * Check if the gimple statment is corrent
                     */
                    if(!is_gimple_assign(tmp_gstmt))
                    {
                        printf("tmp_gstmt is invalid\n");
                    }

                    printf("Insert gimple statment:");
                    print_gimple_stmt(stdout, tmp_gstmt, 0,
                                TDF_DETAILS | TDF_VERBOSE | TDF_TREE);
                    /*
                     * Insert the gimple statment into the basic block
                     */
                    gsi_insert_after(ptrgsi, tmp_gstmt, 
                            GSI_NEW_STMT);

                    if(is_gimple_operand(tmp_var))
                    {

                        printf("begin to insert printf\n");
                        yes = 1;
                        printf("Insert gimple statment:");
                        /*
                         * Insert the gimple statment printf 
                         * into the basic block
                         */
                        new_gnode = gimple_build_call(
                                funcdecl, 1, tmp_var);
                        print_gimple_stmt(stdout, new_gnode, 0, 0);
                        gsi_insert_after(ptrgsi, new_gnode, 
                                GSI_NEW_STMT); 
                    }
                    else
                    {
                        print_generic_stmt(stdout, ptr_desc_node, 
                                TDF_DETAILS | TDF_VERBOSE | TDF_TREE);
                        printf("Not Gimple Operands\n");
                    }

                    /*
                     * Since we have more than one consecutive statements 
                     * to insert, we can actually use build a gimple 
                     * sequence, insert all statement into the sequence, 
                     * and then insert the sequence into the basic block. 
                     * This seems to be a better method.
                     */
                }
            }
            else
            {
            }
            break;
        default:
            break;
    }
}
Пример #7
0
static bool
verify_phi_args (tree phi, basic_block bb, basic_block *definition_block)
{
  edge e;
  bool err = false;
  unsigned i, phi_num_args = PHI_NUM_ARGS (phi);

  if (EDGE_COUNT (bb->preds) != phi_num_args)
    {
      error ("incoming edge count does not match number of PHI arguments");
      err = true;
      goto error;
    }

  for (i = 0; i < phi_num_args; i++)
    {
      use_operand_p op_p = PHI_ARG_DEF_PTR (phi, i);
      tree op = USE_FROM_PTR (op_p);


      e = EDGE_PRED (bb, i);

      if (op == NULL_TREE)
	{
	  error ("PHI argument is missing for edge %d->%d",
	         e->src->index,
		 e->dest->index);
	  err = true;
	  goto error;
	}

      if (TREE_CODE (op) != SSA_NAME && !is_gimple_min_invariant (op))
	{
	  error ("PHI argument is not SSA_NAME, or invariant");
	  err = true;
	}

      if (TREE_CODE (op) == SSA_NAME)
	err = verify_use (e->src, definition_block[SSA_NAME_VERSION (op)], op_p,
			  phi, e->flags & EDGE_ABNORMAL,
			  !is_gimple_reg (PHI_RESULT (phi)),
			  NULL);

      if (e->dest != bb)
	{
	  error ("wrong edge %d->%d for PHI argument",
	         e->src->index, e->dest->index);
	  err = true;
	}

      if (err)
	{
	  fprintf (stderr, "PHI argument\n");
	  print_generic_stmt (stderr, op, TDF_VOPS);
	  goto error;
	}
    }

error:
  if (err)
    {
      fprintf (stderr, "for PHI node\n");
      print_generic_stmt (stderr, phi, TDF_VOPS);
    }


  return err;
}
Пример #8
0
static bool
verify_use (basic_block bb, basic_block def_bb, use_operand_p use_p,
	    tree stmt, bool check_abnormal, bool is_virtual,
	    bitmap names_defined_in_bb)
{
  bool err = false;
  tree ssa_name = USE_FROM_PTR (use_p);

  err = verify_ssa_name (ssa_name, is_virtual);

  if (!TREE_VISITED (ssa_name))
    if (verify_imm_links (stderr, ssa_name))
      err = true;

  TREE_VISITED (ssa_name) = 1;

  if (IS_EMPTY_STMT (SSA_NAME_DEF_STMT (ssa_name))
      && default_def (SSA_NAME_VAR (ssa_name)) == ssa_name)
    ; /* Default definitions have empty statements.  Nothing to do.  */
  else if (!def_bb)
    {
      error ("missing definition");
      err = true;
    }
  else if (bb != def_bb
	   && !dominated_by_p (CDI_DOMINATORS, bb, def_bb))
    {
      error ("definition in block %i does not dominate use in block %i",
	     def_bb->index, bb->index);
      err = true;
    }
  else if (bb == def_bb
	   && names_defined_in_bb != NULL
	   && !bitmap_bit_p (names_defined_in_bb, SSA_NAME_VERSION (ssa_name)))
    {
      error ("definition in block %i follows the use", def_bb->index);
      err = true;
    }

  if (check_abnormal
      && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssa_name))
    {
      error ("SSA_NAME_OCCURS_IN_ABNORMAL_PHI should be set");
      err = true;
    }

  /* Make sure the use is in an appropriate list by checking the previous 
     element to make sure it's the same.  */
  if (use_p->prev == NULL)
    {
      error ("no immediate_use list");
      err = true;
    }
  else
    {
      tree listvar ;
      if (use_p->prev->use == NULL)
	listvar = use_p->prev->stmt;
      else
	listvar = USE_FROM_PTR (use_p->prev);
      if (listvar != ssa_name)
        {
	  error ("wrong immediate use list");
	  err = true;
	}
    }

  if (err)
    {
      fprintf (stderr, "for SSA_NAME: ");
      print_generic_expr (stderr, ssa_name, TDF_VOPS);
      fprintf (stderr, " in statement:\n");
      print_generic_stmt (stderr, stmt, TDF_VOPS);
    }

  return err;
}
Пример #9
0
void
browse_tree (tree begin)
{
  tree head;
  TB_CODE tbc = TB_UNUSED_COMMAND;
  ssize_t rd;
  char *input = NULL;
  long input_size = 0;

  fprintf (TB_OUT_FILE, "\nTree Browser\n");

#define TB_SET_HEAD(N) do {                                           \
  vec_safe_push (TB_history_stack, N);                                \
  head = N;                                                           \
  if (TB_verbose)                                                     \
    if (head)                                                         \
      {                                                               \
	print_generic_expr (TB_OUT_FILE, head, 0);                    \
	fprintf (TB_OUT_FILE, "\n");                                  \
      }                                                               \
} while (0)

  TB_SET_HEAD (begin);

  /* Store in a hashtable information about previous and upper statements.  */
  {
    TB_up_ht = new hash_table<tree_upper_hasher> (1023);
    TB_update_up (head);
  }

  while (24)
    {
      fprintf (TB_OUT_FILE, "TB> ");
      rd = TB_getline (&input, &input_size, TB_IN_FILE);

      if (rd == -1)
	/* EOF.  */
	goto ret;

      if (rd != 1)
	/* Get a new command.  Otherwise the user just pressed enter, and thus
	   she expects the last command to be reexecuted.  */
	tbc = TB_get_command (input);

      switch (tbc)
	{
	case TB_UPDATE_UP:
	  TB_update_up (head);
	  break;

	case TB_MAX:
	  if (head && (INTEGRAL_TYPE_P (head)
		       || TREE_CODE (head) == REAL_TYPE
		       || TREE_CODE (head) == FIXED_POINT_TYPE))
	    TB_SET_HEAD (TYPE_MAX_VALUE (head));
	  else
	    TB_WF;
	  break;

	case TB_MIN:
	  if (head && (INTEGRAL_TYPE_P (head)
		       || TREE_CODE (head) == REAL_TYPE
		       || TREE_CODE (head) == FIXED_POINT_TYPE))
	    TB_SET_HEAD (TYPE_MIN_VALUE (head));
	  else
	    TB_WF;
	  break;

	case TB_ELT:
	  if (head && TREE_CODE (head) == TREE_VEC)
	    {
	      /* This command takes another argument: the element number:
		 for example "elt 1".  */
	      TB_NIY;
	    }
	  else if (head && TREE_CODE (head) == VECTOR_CST)
	    {
	      /* This command takes another argument: the element number:
                 for example "elt 1".  */
              TB_NIY;
	    }
	  else
	    TB_WF;
	  break;

	case TB_VALUE:
	  if (head && TREE_CODE (head) == TREE_LIST)
	    TB_SET_HEAD (TREE_VALUE (head));
	  else
	    TB_WF;
	  break;

	case TB_PURPOSE:
	  if (head && TREE_CODE (head) == TREE_LIST)
	    TB_SET_HEAD (TREE_PURPOSE (head));
	  else
	    TB_WF;
	  break;

	case TB_IMAG:
	  if (head && TREE_CODE (head) == COMPLEX_CST)
	    TB_SET_HEAD (TREE_IMAGPART (head));
	  else
	    TB_WF;
	  break;

	case TB_REAL:
	  if (head && TREE_CODE (head) == COMPLEX_CST)
	    TB_SET_HEAD (TREE_REALPART (head));
	  else
	    TB_WF;
	  break;

	case TB_BLOCK:
	  if (head && TREE_CODE (head) == BIND_EXPR)
	    TB_SET_HEAD (TREE_OPERAND (head, 2));
	  else
	    TB_WF;
	  break;

	case TB_SUBBLOCKS:
	  if (head && TREE_CODE (head) == BLOCK)
	    TB_SET_HEAD (BLOCK_SUBBLOCKS (head));
	  else
	    TB_WF;
	  break;

	case TB_SUPERCONTEXT:
	  if (head && TREE_CODE (head) == BLOCK)
	    TB_SET_HEAD (BLOCK_SUPERCONTEXT (head));
	  else
	    TB_WF;
	  break;

	case TB_VARS:
	  if (head && TREE_CODE (head) == BLOCK)
	    TB_SET_HEAD (BLOCK_VARS (head));
	  else if (head && TREE_CODE (head) == BIND_EXPR)
	    TB_SET_HEAD (TREE_OPERAND (head, 0));
	  else
	    TB_WF;
	  break;

	case TB_REFERENCE_TO_THIS:
	  if (head && TYPE_P (head))
	    TB_SET_HEAD (TYPE_REFERENCE_TO (head));
	  else
	    TB_WF;
	  break;

	case TB_POINTER_TO_THIS:
	  if (head && TYPE_P (head))
	    TB_SET_HEAD (TYPE_POINTER_TO (head));
	  else
	    TB_WF;
	  break;

	case TB_BASETYPE:
	  if (head && TREE_CODE (head) == OFFSET_TYPE)
	    TB_SET_HEAD (TYPE_OFFSET_BASETYPE (head));
	  else
	    TB_WF;
	  break;

	case TB_ARG_TYPES:
	  if (head && (TREE_CODE (head) == FUNCTION_TYPE
		       || TREE_CODE (head) == METHOD_TYPE))
	    TB_SET_HEAD (TYPE_ARG_TYPES (head));
	  else
	    TB_WF;
	  break;

	case TB_METHOD_BASE_TYPE:
	  if (head && (TREE_CODE (head) == FUNCTION_TYPE
		       || TREE_CODE (head) == METHOD_TYPE)
	      && TYPE_METHOD_BASETYPE (head))
	    TB_SET_HEAD (TYPE_METHOD_BASETYPE (head));
	  else
	    TB_WF;
	  break;

	case TB_FIELDS:
	  if (head && (TREE_CODE (head) == RECORD_TYPE
		       || TREE_CODE (head) == UNION_TYPE
		       || TREE_CODE (head) == QUAL_UNION_TYPE))
	    TB_SET_HEAD (TYPE_FIELDS (head));
	  else
	    TB_WF;
	  break;

	case TB_DOMAIN:
	  if (head && TREE_CODE (head) == ARRAY_TYPE)
	    TB_SET_HEAD (TYPE_DOMAIN (head));
	  else
	    TB_WF;
	  break;

	case TB_VALUES:
	  if (head && TREE_CODE (head) == ENUMERAL_TYPE)
	    TB_SET_HEAD (TYPE_VALUES (head));
	  else
	    TB_WF;
	  break;

	case TB_ARG_TYPE:
	  if (head && TREE_CODE (head) == PARM_DECL)
	    TB_SET_HEAD (DECL_ARG_TYPE (head));
	  else
	    TB_WF;
	  break;

	case TB_INITIAL:
	  if (head && DECL_P (head))
	    TB_SET_HEAD (DECL_INITIAL (head));
	  else
	    TB_WF;
	  break;

	case TB_RESULT:
	  if (head && DECL_P (head))
	    TB_SET_HEAD (DECL_RESULT_FLD (head));
	  else
	    TB_WF;
	  break;

	case TB_ARGUMENTS:
	  if (head && DECL_P (head))
	    TB_SET_HEAD (DECL_ARGUMENTS (head));
	  else
	    TB_WF;
	  break;

	case TB_ABSTRACT_ORIGIN:
	  if (head && DECL_P (head))
	    TB_SET_HEAD (DECL_ABSTRACT_ORIGIN (head));
	  else if (head && TREE_CODE (head) == BLOCK)
	    TB_SET_HEAD (BLOCK_ABSTRACT_ORIGIN (head));
	  else
	    TB_WF;
	  break;

	case TB_ATTRIBUTES:
	  if (head && DECL_P (head))
	    TB_SET_HEAD (DECL_ATTRIBUTES (head));
	  else if (head && TYPE_P (head))
	    TB_SET_HEAD (TYPE_ATTRIBUTES (head));
	  else
	    TB_WF;
	  break;

	case TB_CONTEXT:
	  if (head && DECL_P (head))
	    TB_SET_HEAD (DECL_CONTEXT (head));
	  else if (head && TYPE_P (head)
		   && TYPE_CONTEXT (head))
	    TB_SET_HEAD (TYPE_CONTEXT (head));
	  else
	    TB_WF;
	  break;

	case TB_OFFSET:
	  if (head && TREE_CODE (head) == FIELD_DECL)
	    TB_SET_HEAD (DECL_FIELD_OFFSET (head));
	  else
	    TB_WF;
	  break;

	case TB_BIT_OFFSET:
	  if (head && TREE_CODE (head) == FIELD_DECL)
	    TB_SET_HEAD (DECL_FIELD_BIT_OFFSET (head));
	  else
	    TB_WF;
          break;

	case TB_UNIT_SIZE:
	  if (head && DECL_P (head))
	    TB_SET_HEAD (DECL_SIZE_UNIT (head));
	  else if (head && TYPE_P (head))
	    TB_SET_HEAD (TYPE_SIZE_UNIT (head));
	  else
	    TB_WF;
	  break;

	case TB_SIZE:
	  if (head && DECL_P (head))
	    TB_SET_HEAD (DECL_SIZE (head));
	  else if (head && TYPE_P (head))
	    TB_SET_HEAD (TYPE_SIZE (head));
	  else
	    TB_WF;
	  break;

	case TB_TYPE:
	  if (head && TREE_TYPE (head))
	    TB_SET_HEAD (TREE_TYPE (head));
	  else
	    TB_WF;
	  break;

	case TB_DECL_SAVED_TREE:
	  if (head && TREE_CODE (head) == FUNCTION_DECL
	      && DECL_SAVED_TREE (head))
	    TB_SET_HEAD (DECL_SAVED_TREE (head));
	  else
	    TB_WF;
	  break;

	case TB_BODY:
	  if (head && TREE_CODE (head) == BIND_EXPR)
	    TB_SET_HEAD (TREE_OPERAND (head, 1));
	  else
	    TB_WF;
	  break;

	case TB_CHILD_0:
	  if (head && EXPR_P (head) && TREE_OPERAND (head, 0))
	    TB_SET_HEAD (TREE_OPERAND (head, 0));
	  else
	    TB_WF;
	  break;

	case TB_CHILD_1:
          if (head && EXPR_P (head) && TREE_OPERAND (head, 1))
	    TB_SET_HEAD (TREE_OPERAND (head, 1));
	  else
	    TB_WF;
          break;

	case TB_CHILD_2:
          if (head && EXPR_P (head) && TREE_OPERAND (head, 2))
	    TB_SET_HEAD (TREE_OPERAND (head, 2));
	  else
	    TB_WF;
	  break;

	case TB_CHILD_3:
	  if (head && EXPR_P (head) && TREE_OPERAND (head, 3))
	    TB_SET_HEAD (TREE_OPERAND (head, 3));
	  else
	    TB_WF;
          break;

	case TB_PRINT:
	  if (head)
	    debug_tree (head);
	  else
	    TB_WF;
	  break;

	case TB_PRETTY_PRINT:
	  if (head)
	    {
	      print_generic_stmt (TB_OUT_FILE, head, 0);
	      fprintf (TB_OUT_FILE, "\n");
	    }
	  else
	    TB_WF;
	  break;

	case TB_SEARCH_NAME:

	  break;

	case TB_SEARCH_CODE:
	  {
	    enum tree_code code;
	    char *arg_text;

	    arg_text = strchr (input, ' ');
	    if (arg_text == NULL)
	      {
		fprintf (TB_OUT_FILE, "First argument is missing.  This isn't a valid search command.  \n");
		break;
	      }
	    code = TB_get_tree_code (arg_text + 1);

	    /* Search in the subtree a node with the given code.  */
	    {
	      tree res;

	      res = walk_tree (&head, find_node_with_code, &code, NULL);
	      if (res == NULL_TREE)
		{
		  fprintf (TB_OUT_FILE, "There's no node with this code (reachable via the walk_tree function from this node).\n");
		}
	      else
		{
		  fprintf (TB_OUT_FILE, "Achoo!  I got this node in the tree.\n");
		  TB_SET_HEAD (res);
		}
	    }
	    break;
	  }

#define TB_MOVE_HEAD(FCT) do {       \
  if (head)                          \
    {                                \
      tree t;                        \
      t = FCT (head);                \
      if (t)                         \
        TB_SET_HEAD (t);             \
      else                           \
	TB_WF;                       \
    }                                \
  else                               \
    TB_WF;                           \
} while (0)

	case TB_FIRST:
	  TB_MOVE_HEAD (TB_first_in_bind);
          break;

        case TB_LAST:
          TB_MOVE_HEAD (TB_last_in_bind);
          break;

	case TB_UP:
	  TB_MOVE_HEAD (TB_up_expr);
	  break;

	case TB_PREV:
	  TB_MOVE_HEAD (TB_prev_expr);
	  break;

	case TB_NEXT:
	  TB_MOVE_HEAD (TB_next_expr);
	  break;

	case TB_HPREV:
	  /* This command is a little bit special, since it deals with history
	     stack.  For this reason it should keep the "head = ..." statement
	     and not use TB_MOVE_HEAD.  */
	  if (head)
	    {
	      tree t;
	      t = TB_history_prev ();
	      if (t)
		{
		  head = t;
		  if (TB_verbose)
		    {
		      print_generic_expr (TB_OUT_FILE, head, 0);
		      fprintf (TB_OUT_FILE, "\n");
		    }
		}
	      else
		TB_WF;
	    }
	  else
	    TB_WF;
	  break;

	case TB_CHAIN:
	  /* Don't go further if it's the last node in this chain.  */
	  if (head && TREE_CODE (head) == BLOCK)
	    TB_SET_HEAD (BLOCK_CHAIN (head));
	  else if (head && TREE_CHAIN (head))
	    TB_SET_HEAD (TREE_CHAIN (head));
	  else
	    TB_WF;
	  break;

	case TB_FUN:
	  /* Go up to the current function declaration.  */
	  TB_SET_HEAD (current_function_decl);
	  fprintf (TB_OUT_FILE, "Current function declaration.\n");
	  break;

	case TB_HELP:
	  /* Display a help message.  */
	  {
	    int i;
	    fprintf (TB_OUT_FILE, "Possible commands are:\n\n");
	    for (i = 0; i < TB_UNUSED_COMMAND; i++)
	      {
		fprintf (TB_OUT_FILE, "%20s  -  %s\n", TB_COMMAND_TEXT (i), TB_COMMAND_HELP (i));
	      }
	  }
	  break;

	case TB_VERBOSE:
	  if (TB_verbose == 0)
	    {
	      TB_verbose = 1;
	      fprintf (TB_OUT_FILE, "Verbose on.\n");
	    }
	  else
	    {
	      TB_verbose = 0;
	      fprintf (TB_OUT_FILE, "Verbose off.\n");
	    }
	  break;

	case TB_EXIT:
	case TB_QUIT:
	  /* Just exit from this function.  */
	  goto ret;

	default:
	  TB_NIY;
	}
    }

 ret:;
  delete TB_up_ht;
  TB_up_ht = NULL;
  return;
}