/* Iterator routines for manipulating GENERIC and GIMPLE tree statements. Copyright (C) 2003, 2004, 2007, 2008 Free Software Foundation, Inc. Contributed by Andrew MacLeod This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tree.h" #include "gimple.h" #include "tree-iterator.h" #include "ggc.h" /* This is a cache of STATEMENT_LIST nodes. We create and destroy them fairly often during gimplification. */ static GTY ((deletable (""))) tree stmt_list_cache; tree alloc_stmt_list (void) { tree list = stmt_list_cache; if (list) { stmt_list_cache = TREE_CHAIN (list); gcc_assert (stmt_list_cache != list); memset (list, 0, sizeof(struct tree_common)); TREE_SET_CODE (list, STATEMENT_LIST); } else list = make_node (STATEMENT_LIST); TREE_TYPE (list) = void_type_node; return list; } void free_stmt_list (tree t) { gcc_assert (!STATEMENT_LIST_HEAD (t)); gcc_assert (!STATEMENT_LIST_TAIL (t)); /* If this triggers, it's a sign that the same list is being freed twice. */ gcc_assert (t != stmt_list_cache || stmt_list_cache == NULL); TREE_CHAIN (t) = stmt_list_cache; stmt_list_cache = t; } /* Links a statement, or a chain of statements, before the current stmt. */ void tsi_link_before (tree_stmt_iterator *i, tree t, enum tsi_iterator_update mode) { struct tree_statement_list_node *head, *tail, *cur; /* Die on looping. */ gcc_assert (t != i->container); if (TREE_CODE (t) == STATEMENT_LIST) { head = STATEMENT_LIST_HEAD (t); tail = STATEMENT_LIST_TAIL (t); STATEMENT_LIST_HEAD (t) = NULL; STATEMENT_LIST_TAIL (t) = NULL; free_stmt_list (t); /* Empty statement lists need no work. */ if (!head || !tail) { gcc_assert (head == tail); return; } } else { head = GGC_NEW (struct tree_statement_list_node); head->prev = NULL; head->next = NULL; head->stmt = t; tail = head; } TREE_SIDE_EFFECTS (i->container) = 1; cur = i->ptr; /* Link it into the list. */ if (cur) { head->prev = cur->prev; if (head->prev) head->prev->next = head; else STATEMENT_LIST_HEAD (i->container) = head; tail->next = cur; cur->prev = tail; } else { head->prev = STATEMENT_LIST_TAIL (i->container); if (head->prev) head->prev->next = head; else STATEMENT_LIST_HEAD (i->container) = head; STATEMENT_LIST_TAIL (i->container) = tail; } /* Update the iterator, if requested. */ switch (mode) { case TSI_NEW_STMT: case TSI_CONTINUE_LINKING: case TSI_CHAIN_START: i->ptr = head; break; case TSI_CHAIN_END: i->ptr = tail; break; case TSI_SAME_STMT: break; } } /* Links a statement, or a chain of statements, after the current stmt. */ void tsi_link_after (tree_stmt_iterator *i, tree t, enum tsi_iterator_update mode) { struct tree_statement_list_node *head, *tail, *cur; /* Die on looping. */ gcc_assert (t != i->container); if (TREE_CODE (t) == STATEMENT_LIST) { head = STATEMENT_LIST_HEAD (t); tail = STATEMENT_LIST_TAIL (t); STATEMENT_LIST_HEAD (t) = NULL; STATEMENT_LIST_TAIL (t) = NULL; free_stmt_list (t); /* Empty statement lists need no work. */ if (!head || !tail) { gcc_assert (head == tail); return; } } else { head = GGC_NEW (struct tree_statement_list_node); head->prev = NULL; head->next = NULL; head->stmt = t; tail = head; } TREE_SIDE_EFFECTS (i->container) = 1; cur = i->ptr; /* Link it into the list. */ if (cur) { tail->next = cur->next; if (tail->next) tail->next->prev = tail; else STATEMENT_LIST_TAIL (i->container) = tail; head->prev = cur; cur->next = head; } else { gcc_assert (!STATEMENT_LIST_TAIL (i->container)); STATEMENT_LIST_HEAD (i->container) = head; STATEMENT_LIST_TAIL (i->container) = tail; } /* Update the iterator, if requested. */ switch (mode) { case TSI_NEW_STMT: case TSI_CHAIN_START: i->ptr = head; break; case TSI_CONTINUE_LINKING: case TSI_CHAIN_END: i->ptr = tail; break; case TSI_SAME_STMT: gcc_assert (cur); break; } } /* Remove a stmt from the tree list. The iterator is updated to point to the next stmt. */ void tsi_delink (tree_stmt_iterator *i) { struct tree_statement_list_node *cur, *next, *prev; cur = i->ptr; next = cur->next; prev = cur->prev; if (prev) prev->next = next; else STATEMENT_LIST_HEAD (i->container) = next; if (next) next->prev = prev; else STATEMENT_LIST_TAIL (i->container) = prev; if (!next && !prev) TREE_SIDE_EFFECTS (i->container) = 0; i->ptr = next; } /* Move all statements in the statement list after I to a new statement list. I itself is unchanged. */ tree tsi_split_statement_list_after (const tree_stmt_iterator *i) { struct tree_statement_list_node *cur, *next; tree old_sl, new_sl; cur = i->ptr; /* How can we possibly split after the end, or before the beginning? */ gcc_assert (cur); next = cur->next; old_sl = i->container; new_sl = alloc_stmt_list (); TREE_SIDE_EFFECTS (new_sl) = 1; STATEMENT_LIST_HEAD (new_sl) = next; STATEMENT_LIST_TAIL (new_sl) = STATEMENT_LIST_TAIL (old_sl); STATEMENT_LIST_TAIL (old_sl) = cur; cur->next = NULL; next->prev = NULL; return new_sl; } /* Move all statements in the statement list before I to a new statement list. I is set to the head of the new list. */ tree tsi_split_statement_list_before (tree_stmt_iterator *i) { struct tree_statement_list_node *cur, *prev; tree old_sl, new_sl; cur = i->ptr; /* How can we possibly split after the end, or before the beginning? */ gcc_assert (cur); prev = cur->prev; old_sl = i->container; new_sl = alloc_stmt_list (); TREE_SIDE_EFFECTS (new_sl) = 1; i->container = new_sl; STATEMENT_LIST_HEAD (new_sl) = cur; STATEMENT_LIST_TAIL (new_sl) = STATEMENT_LIST_TAIL (old_sl); STATEMENT_LIST_TAIL (old_sl) = prev; cur->prev = NULL; if (prev) prev->next = NULL; else STATEMENT_LIST_HEAD (old_sl) = NULL; return new_sl; } /* Return the first expression in a sequence of COMPOUND_EXPRs, or in a STATEMENT_LIST. */ tree expr_first (tree expr) { if (expr == NULL_TREE) return expr; if (TREE_CODE (expr) == STATEMENT_LIST) { struct tree_statement_list_node *n = STATEMENT_LIST_HEAD (expr); return n ? n->stmt : NULL_TREE; } while (TREE_CODE (expr) == COMPOUND_EXPR) expr = TREE_OPERAND (expr, 0); return expr; } /* Return the last expression in a sequence of COMPOUND_EXPRs, or in a STATEMENT_LIST. */ #define EXPR_LAST_BODY do { \ if (expr == NULL_TREE) \ return expr;\ if (TREE_CODE (expr) == STATEMENT_LIST) \ { \ struct tree_statement_list_node *n = STATEMENT_LIST_TAIL (expr); \ return n ? n->stmt : NULL_TREE; \ } \ while (TREE_CODE (expr) == COMPOUND_EXPR) \ expr = TREE_OPERAND (expr, 1); \ return expr; \ } while (0) tree expr_last (tree expr) { if (expr == NULL_TREE) return expr; if (TREE_CODE (expr) == STATEMENT_LIST) { struct tree_statement_list_node *n = STATEMENT_LIST_TAIL (expr); return n ? n->stmt : NULL_TREE; } while (TREE_CODE (expr) == COMPOUND_EXPR) expr = TREE_OPERAND (expr, 1); return expr; } /* If EXPR is a single statement return it. If EXPR is a STATEMENT_LIST containing exactly one statement S, return S. Otherwise, return NULL. */ tree expr_only (tree expr) { if (expr == NULL_TREE) return NULL_TREE; if (TREE_CODE (expr) == STATEMENT_LIST) { struct tree_statement_list_node *n = STATEMENT_LIST_TAIL (expr); if (n && STATEMENT_LIST_HEAD (expr) == n) return n->stmt; else return NULL_TREE; } if (TREE_CODE (expr) == COMPOUND_EXPR) return NULL_TREE; return expr; } #include "gt-tree-iterator.h"