#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "errors.h"
#include "varray.h"
#include "c-tree.h"
#include "c-common.h"
#include "tree-gimple.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "tree-flow.h"
#include "tree-inline.h"
#include "diagnostic.h"
#include "langhooks.h"
#include "langhooks-def.h"
#include "flags.h"
#include "rtl.h"
#include "toplev.h"
#include "tree-dump.h"
#include "c-pretty-print.h"
#include "cgraph.h"
static enum gimplify_status gimplify_expr_stmt (tree *);
static enum gimplify_status gimplify_decl_stmt (tree *);
static enum gimplify_status gimplify_for_stmt (tree *, tree *);
static enum gimplify_status gimplify_while_stmt (tree *);
static enum gimplify_status gimplify_do_stmt (tree *);
static enum gimplify_status gimplify_if_stmt (tree *);
static enum gimplify_status gimplify_switch_stmt (tree *);
static enum gimplify_status gimplify_return_stmt (tree *);
static enum gimplify_status gimplify_stmt_expr (tree *);
static enum gimplify_status gimplify_compound_literal_expr (tree *);
#if defined ENABLE_CHECKING
static int is_last_stmt_of_scope (tree);
#endif
static enum gimplify_status gimplify_block (tree *, tree *);
static enum gimplify_status gimplify_cleanup (tree *, tree *);
static tree gimplify_c_loop (tree, tree, tree, bool);
static void push_context (void);
static void pop_context (void);
static tree c_build_bind_expr (tree, tree);
static void add_block_to_enclosing (tree);
static void gimplify_condition (tree *);
enum bc_t { bc_break = 0, bc_continue = 1 };
static tree begin_bc_block (enum bc_t);
static tree finish_bc_block (tree, tree);
static tree build_bc_goto (enum bc_t);
static struct c_gimplify_ctx
{
tree current_bc_label;
tree bc_id[2];
} *ctxp;
static void
push_context (void)
{
if (ctxp)
abort ();
ctxp = (struct c_gimplify_ctx *) xcalloc (1, sizeof (struct c_gimplify_ctx));
ctxp->bc_id[bc_continue] = get_identifier ("continue");
ctxp->bc_id[bc_break] = get_identifier ("break");
}
static void
pop_context (void)
{
if (!ctxp || ctxp->current_bc_label)
abort ();
free (ctxp);
ctxp = NULL;
}
void
c_genericize (tree fndecl)
{
FILE *dump_file;
int local_dump_flags;
struct cgraph_node *cgn;
dump_file = dump_begin (TDI_original, &local_dump_flags);
if (dump_file)
{
fprintf (dump_file, "\n;; Function %s",
lang_hooks.decl_printable_name (fndecl, 2));
fprintf (dump_file, " (%s)\n",
IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl)));
fprintf (dump_file, ";; enabled by -%s\n", dump_flag_name (TDI_original));
fprintf (dump_file, "\n");
if (local_dump_flags & TDF_RAW)
dump_node (DECL_SAVED_TREE (fndecl),
TDF_SLIM | local_dump_flags, dump_file);
else
print_c_tree (dump_file, DECL_SAVED_TREE (fndecl));
fprintf (dump_file, "\n");
dump_end (TDI_original, dump_file);
}
push_context ();
gimplify_function_tree (fndecl);
pop_context ();
dump_function (TDI_generic, fndecl);
cgn = cgraph_node (fndecl);
for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
c_genericize (cgn->decl);
}
int
c_gimplify_stmt (tree *stmt_p)
{
tree stmt, next;
tree outer_pre = NULL_TREE;
for (stmt = *stmt_p; stmt && stmt != error_mark_node; stmt = next)
{
tree pre, post;
int saved_stmts_are_full_exprs_p;
location_t stmt_locus;
enum gimplify_status ret;
saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p ();
prep_stmt (stmt);
stmt_locus = input_location;
pre = NULL_TREE;
post = NULL_TREE;
next = TREE_CHAIN (stmt);
switch (TREE_CODE (stmt))
{
case COMPOUND_STMT:
stmt = COMPOUND_BODY (stmt);
ret = GS_OK;
break;
case SCOPE_STMT:
ret = gimplify_block (&stmt, &next);
break;
case FOR_STMT:
ret = gimplify_for_stmt (&stmt, &next);
break;
case WHILE_STMT:
ret = gimplify_while_stmt (&stmt);
break;
case DO_STMT:
ret = gimplify_do_stmt (&stmt);
break;
case IF_STMT:
ret = gimplify_if_stmt (&stmt);
break;
case SWITCH_STMT:
ret = gimplify_switch_stmt (&stmt);
break;
case EXPR_STMT:
ret = gimplify_expr_stmt (&stmt);
break;
case RETURN_STMT:
ret = gimplify_return_stmt (&stmt);
break;
case DECL_STMT:
ret = gimplify_decl_stmt (&stmt);
break;
case LABEL_STMT:
stmt = build1 (LABEL_EXPR, void_type_node, LABEL_STMT_LABEL (stmt));
ret = GS_OK;
break;
case GOTO_STMT:
stmt = build1 (GOTO_EXPR, void_type_node, GOTO_DESTINATION (stmt));
ret = GS_OK;
break;
case CASE_LABEL:
{
tree label = create_artificial_label ();
stmt = build (CASE_LABEL_EXPR, void_type_node,
CASE_LOW (stmt), CASE_HIGH (stmt), label);
ret = GS_OK;
}
break;
case CONTINUE_STMT:
stmt = build_bc_goto (bc_continue);
ret = GS_OK;
break;
case BREAK_STMT:
stmt = build_bc_goto (bc_break);
ret = GS_OK;
break;
case CLEANUP_STMT:
ret = gimplify_cleanup (&stmt, &next);
break;
case ASM_STMT:
{
tree new_stmt = build (ASM_EXPR, void_type_node, ASM_STRING (stmt),
ASM_OUTPUTS (stmt), ASM_INPUTS (stmt),
ASM_CLOBBERS (stmt));
ASM_INPUT_P (new_stmt) = ASM_INPUT_P (stmt);
ASM_VOLATILE_P (new_stmt) = ASM_VOLATILE_P (stmt);
stmt = new_stmt;
ret = GS_OK;
}
break;
default:
if (lang_gimplify_stmt && (*lang_gimplify_stmt) (&stmt, &next))
{
ret = GS_OK;
break;
}
fprintf (stderr, "unhandled statement node in c_gimplify_stmt:\n");
debug_tree (stmt);
abort ();
break;
}
switch (ret)
{
case GS_ERROR:
goto cont;
case GS_OK:
gimplify_stmt (&stmt);
break;
case GS_ALL_DONE:
break;
default:
abort ();
}
append_to_statement_list (stmt, &pre);
append_to_statement_list (post, &pre);
annotate_all_with_locus (&pre, stmt_locus);
append_to_statement_list (pre, &outer_pre);
cont:
current_stmt_tree ()->stmts_are_full_exprs_p
= saved_stmts_are_full_exprs_p;
}
append_to_statement_list (stmt, &outer_pre);
*stmt_p = outer_pre;
return GS_ALL_DONE;
}
static void
add_block_to_enclosing (tree block)
{
tree enclosing;
for (enclosing = gimple_current_bind_expr ();
enclosing; enclosing = TREE_CHAIN (enclosing))
if (BIND_EXPR_BLOCK (enclosing))
break;
enclosing = BIND_EXPR_BLOCK (enclosing);
BLOCK_SUBBLOCKS (enclosing) = chainon (BLOCK_SUBBLOCKS (enclosing), block);
}
static tree
c_build_bind_expr (tree block, tree body)
{
tree decls, bind;
if (block == NULL_TREE)
decls = NULL_TREE;
else if (TREE_CODE (block) == BLOCK)
decls = BLOCK_VARS (block);
else
{
decls = block;
if (DECL_ARTIFICIAL (decls))
block = NULL_TREE;
else
{
block = make_node (BLOCK);
BLOCK_VARS (block) = decls;
add_block_to_enclosing (block);
}
}
if (!body)
body = build_empty_stmt ();
bind = build (BIND_EXPR, void_type_node, decls, body, block);
TREE_SIDE_EFFECTS (bind) = 1;
return bind;
}
static enum gimplify_status
gimplify_block (tree *stmt_p, tree *next_p)
{
tree *p;
tree block;
tree bind;
int depth;
location_t stmt_locus;
if (!SCOPE_BEGIN_P (*stmt_p))
{
if (!errorcount && !sorrycount)
abort ();
*stmt_p = NULL;
return GS_ERROR;
}
block = SCOPE_STMT_BLOCK (*stmt_p);
depth = 1;
for (p = &TREE_CHAIN (*stmt_p);; p = &TREE_CHAIN (*p))
{
if (*p == NULL)
break;
if (TREE_CODE (*p) == SCOPE_STMT)
{
if (SCOPE_BEGIN_P (*p))
++depth;
else if (--depth == 0)
break;
}
}
stmt_locus = input_location;
if (*p)
{
if (SCOPE_STMT_BLOCK (*p) != block)
abort ();
if (EXPR_LOCUS (*p))
stmt_locus = *EXPR_LOCUS (*p);
*next_p = TREE_CHAIN (*p);
*p = NULL_TREE;
}
else
{
if (!errorcount && !sorrycount)
abort ();
}
bind = c_build_bind_expr (block, TREE_CHAIN (*stmt_p));
*stmt_p = bind;
input_location = stmt_locus;
return GS_OK;
}
static enum gimplify_status
gimplify_cleanup (tree *stmt_p, tree *next_p)
{
tree stmt = *stmt_p;
tree body = TREE_CHAIN (stmt);
tree cleanup = CLEANUP_EXPR (stmt);
enum tree_code code
= (CLEANUP_EH_ONLY (stmt) ? TRY_CATCH_EXPR : TRY_FINALLY_EXPR);
if (!body)
body = build_empty_stmt ();
if (!cleanup)
cleanup = build_empty_stmt ();
*stmt_p = build (code, void_type_node, body, cleanup);
*next_p = NULL_TREE;
return GS_OK;
}
static enum gimplify_status
gimplify_expr_stmt (tree *stmt_p)
{
tree stmt = EXPR_STMT_EXPR (*stmt_p);
if (stmt == error_mark_node)
stmt = NULL;
if (stmt && (extra_warnings || warn_unused_value))
{
if (!TREE_SIDE_EFFECTS (stmt))
{
if (!IS_EMPTY_STMT (stmt)
&& !VOID_TYPE_P (TREE_TYPE (stmt))
&& !TREE_NO_WARNING (stmt))
warning ("statement with no effect");
}
else if (warn_unused_value)
{
set_file_and_line_for_stmt (input_location);
warn_if_unused_value (stmt);
}
}
if (stmt == NULL_TREE)
stmt = build_empty_stmt ();
else if (stmts_are_full_exprs_p ())
stmt = build1 (CLEANUP_POINT_EXPR, void_type_node, stmt);
*stmt_p = stmt;
return GS_OK;
}
static void
gimplify_condition (tree *cond_p)
{
tree cond = *cond_p;
if (cond && TREE_CODE (cond) == TREE_LIST)
{
tree decl = TREE_PURPOSE (cond);
tree value = TREE_VALUE (cond);
c_gimplify_stmt (&decl);
*cond_p = build (COMPOUND_EXPR, TREE_TYPE (value), decl, value);
}
}
static tree
begin_bc_block (enum bc_t bc)
{
tree label = create_artificial_label ();
DECL_NAME (label) = ctxp->bc_id[bc];
TREE_CHAIN (label) = ctxp->current_bc_label;
ctxp->current_bc_label = label;
return label;
}
static tree
finish_bc_block (tree label, tree body)
{
if (label != ctxp->current_bc_label)
abort ();
if (TREE_USED (label))
{
tree t, sl = NULL;
DECL_NAME (label) = NULL_TREE;
t = build1 (LABEL_EXPR, void_type_node, label);
append_to_statement_list (body, &sl);
append_to_statement_list (t, &sl);
body = sl;
}
ctxp->current_bc_label = TREE_CHAIN (label);
TREE_CHAIN (label) = NULL_TREE;
return body;
}
static tree
build_bc_goto (enum bc_t bc)
{
tree label;
tree target_name = ctxp->bc_id[bc];
for (label = ctxp->current_bc_label;
label;
label = TREE_CHAIN (label))
if (DECL_NAME (label) == target_name)
break;
if (label == NULL_TREE)
{
if (bc == bc_break)
error ("break statement not within loop or switch");
else
error ("continue statement not within loop or switch");
return NULL_TREE;
}
TREE_USED (label) = 1;
return build1 (GOTO_EXPR, void_type_node, label);
}
static tree
gimplify_c_loop (tree cond, tree body, tree incr, bool cond_is_first)
{
tree top, entry, exit, cont_block, break_block, stmt_list, t;
location_t stmt_locus;
stmt_locus = input_location;
if (!cond_is_first && cond && integer_zerop (cond))
top = cond = NULL;
else
{
top = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
}
break_block = begin_bc_block (bc_break);
if (top)
{
exit = build_and_jump (&LABEL_EXPR_LABEL (top));
if (cond)
{
gimplify_condition (&cond);
t = build_bc_goto (bc_break);
exit = build (COND_EXPR, void_type_node, cond, exit, t);
exit = fold (exit);
gimplify_stmt (&exit);
}
}
else
exit = NULL_TREE;
cont_block = begin_bc_block (bc_continue);
gimplify_stmt (&body);
if (incr && stmts_are_full_exprs_p ())
incr = fold (build1 (CLEANUP_POINT_EXPR, void_type_node, incr));
gimplify_stmt (&incr);
body = finish_bc_block (cont_block, body);
stmt_list = NULL;
if (cond_is_first && cond)
{
entry = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
t = build_and_jump (&LABEL_EXPR_LABEL (entry));
append_to_statement_list (t, &stmt_list);
}
else
entry = NULL_TREE;
append_to_statement_list (top, &stmt_list);
append_to_statement_list (body, &stmt_list);
append_to_statement_list (incr, &stmt_list);
append_to_statement_list (entry, &stmt_list);
append_to_statement_list (exit, &stmt_list);
annotate_all_with_locus (&stmt_list, stmt_locus);
return finish_bc_block (break_block, stmt_list);
}
static enum gimplify_status
gimplify_for_stmt (tree *stmt_p, tree *next_p)
{
tree stmt = *stmt_p;
tree init = FOR_INIT_STMT (stmt);
if (init)
{
FOR_INIT_STMT (stmt) = NULL_TREE;
chainon (init, stmt);
*stmt_p = init;
*next_p = TREE_CHAIN (stmt);
TREE_CHAIN (stmt) = NULL_TREE;
c_gimplify_stmt (stmt_p);
}
else
*stmt_p = gimplify_c_loop (FOR_COND (stmt), FOR_BODY (stmt),
FOR_EXPR (stmt), 1);
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_while_stmt (tree *stmt_p)
{
tree stmt = *stmt_p;
*stmt_p = gimplify_c_loop (WHILE_COND (stmt), WHILE_BODY (stmt),
NULL_TREE, 1);
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_do_stmt (tree *stmt_p)
{
tree stmt = *stmt_p;
*stmt_p = gimplify_c_loop (DO_COND (stmt), DO_BODY (stmt),
NULL_TREE, 0);
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_if_stmt (tree *stmt_p)
{
tree stmt, then_, else_;
stmt = *stmt_p;
restart:
then_ = THEN_CLAUSE (stmt);
else_ = ELSE_CLAUSE (stmt);
if (!then_)
then_ = build_empty_stmt ();
if (!else_)
else_ = build_empty_stmt ();
stmt = build (COND_EXPR, void_type_node, IF_COND (stmt), then_, else_);
gimplify_condition (& TREE_OPERAND (stmt, 0));
*stmt_p = stmt;
annotate_with_locus (stmt, input_location);
if (TREE_CODE (else_) == IF_STMT && !TREE_CHAIN (else_))
{
stmt_p = &COND_EXPR_ELSE (stmt);
stmt = else_;
prep_stmt (stmt);
goto restart;
}
return GS_OK;
}
static enum gimplify_status
gimplify_switch_stmt (tree *stmt_p)
{
tree stmt = *stmt_p;
tree break_block, body;
location_t stmt_locus = input_location;
break_block = begin_bc_block (bc_break);
gimplify_condition (&SWITCH_COND (stmt));
body = SWITCH_BODY (stmt);
if (!body)
body = build_empty_stmt ();
*stmt_p = build (SWITCH_EXPR, SWITCH_TYPE (stmt), SWITCH_COND (stmt),
body, NULL_TREE);
annotate_with_locus (*stmt_p, stmt_locus);
gimplify_stmt (stmt_p);
*stmt_p = finish_bc_block (break_block, *stmt_p);
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_return_stmt (tree *stmt_p)
{
tree expr = RETURN_STMT_EXPR (*stmt_p);
expr = build1 (RETURN_EXPR, void_type_node, expr);
if (stmts_are_full_exprs_p ())
expr = build1 (CLEANUP_POINT_EXPR, void_type_node, expr);
*stmt_p = expr;
return GS_OK;
}
static enum gimplify_status
gimplify_decl_stmt (tree *stmt_p)
{
tree stmt = *stmt_p;
tree decl = DECL_STMT_DECL (stmt);
tree pre = NULL_TREE;
tree post = NULL_TREE;
if (TREE_TYPE (decl) == error_mark_node)
{
*stmt_p = NULL;
return GS_ERROR;
}
if (TREE_CODE (decl) == TYPE_DECL)
{
tree type = TREE_TYPE (decl);
if (TYPE_SIZE_UNIT (type)
&& !TREE_CONSTANT (TYPE_SIZE_UNIT (type)))
{
tree temp = TYPE_SIZE_UNIT (type);
gimplify_expr (&temp, &pre, &post, is_gimple_val, fb_rvalue);
}
}
if (TREE_CODE (decl) == VAR_DECL && !DECL_EXTERNAL (decl))
{
tree init = DECL_INITIAL (decl);
if (!TREE_CONSTANT (DECL_SIZE (decl)))
{
tree pt_type = build_pointer_type (TREE_TYPE (decl));
tree alloc, size;
size = get_initialized_tmp_var (DECL_SIZE_UNIT (decl), &pre, &post);
DECL_DEFER_OUTPUT (decl) = 1;
alloc = build_function_call_expr
(implicit_built_in_decls[BUILT_IN_STACK_ALLOC],
tree_cons (NULL_TREE,
build1 (ADDR_EXPR, pt_type, decl),
tree_cons (NULL_TREE, size, NULL_TREE)));
append_to_compound_expr (alloc, &pre);
}
if (init && init != error_mark_node)
{
if (!TREE_STATIC (decl))
{
if (init == decl && !warn_init_self)
TREE_NO_WARNING (decl) = 1;
DECL_INITIAL (decl) = NULL_TREE;
init = build (MODIFY_EXPR, void_type_node, decl, init);
if (stmts_are_full_exprs_p ())
init = build1 (CLEANUP_POINT_EXPR, void_type_node, init);
append_to_compound_expr (init, &pre);
}
else
{
walk_tree (&init, force_labels_r, NULL, NULL);
}
}
if (DECL_ARTIFICIAL (decl) && DECL_NAME (decl) == NULL_TREE)
gimple_add_tmp_var (decl);
}
append_to_compound_expr (post, &pre);
*stmt_p = pre;
return GS_OK;
}
static enum gimplify_status
gimplify_compound_literal_expr (tree *expr_p)
{
tree decl_s = COMPOUND_LITERAL_EXPR_DECL_STMT (*expr_p);
tree decl = DECL_STMT_DECL (decl_s);
if (DECL_NAME (decl) == NULL_TREE)
gimple_add_tmp_var (decl);
gimplify_decl_stmt (&decl_s);
*expr_p = decl_s ? decl_s : decl;
return GS_OK;
}
int
c_gimplify_expr (tree *expr_p, tree *pre_p ATTRIBUTE_UNUSED,
tree *post_p ATTRIBUTE_UNUSED)
{
enum tree_code code = TREE_CODE (*expr_p);
if (STATEMENT_CODE_P (code))
return c_gimplify_stmt (expr_p);
switch (code)
{
case COMPOUND_LITERAL_EXPR:
return gimplify_compound_literal_expr (expr_p);
case STMT_EXPR:
return gimplify_stmt_expr (expr_p);
default:
return GS_UNHANDLED;
}
}
tree
stmt_expr_last_stmt (tree stmt_expr)
{
tree body = STMT_EXPR_STMT (stmt_expr);
tree last_stmt, substmt;
last_stmt = NULL_TREE;
for (substmt = COMPOUND_BODY (body); substmt;
substmt = TREE_CHAIN (substmt))
if (TREE_CODE (substmt) != SCOPE_STMT)
last_stmt = substmt;
if (last_stmt == NULL_TREE
|| TREE_CODE (last_stmt) != EXPR_STMT
|| (TREE_TYPE (last_stmt)
&& VOID_TYPE_P (TREE_TYPE (last_stmt))))
{
location_t loc;
if (last_stmt && EXPR_LOCUS (last_stmt))
loc = *EXPR_LOCUS (last_stmt);
else if (EXPR_LOCUS (stmt_expr))
loc = *EXPR_LOCUS (stmt_expr);
else
loc = input_location;
warning ("%Hstatement-expressions should end with a "
"non-void expression", &loc);
last_stmt = NULL_TREE;
}
#if defined ENABLE_CHECKING
if (last_stmt && !is_last_stmt_of_scope (last_stmt))
abort ();
#endif
return last_stmt;
}
static enum gimplify_status
gimplify_stmt_expr (tree *expr_p)
{
tree body = STMT_EXPR_STMT (*expr_p);
if (VOID_TYPE_P (TREE_TYPE (*expr_p)))
{
*expr_p = body;
return c_gimplify_stmt (expr_p);
}
else
{
tree last_stmt = stmt_expr_last_stmt (*expr_p);
tree last_expr = NULL_TREE;
if (last_stmt)
{
last_expr = EXPR_STMT_EXPR (last_stmt);
if (stmts_are_full_exprs_p ())
last_expr = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (last_expr),
last_expr);
EXPR_STMT_EXPR (last_stmt) = NULL_TREE;
}
c_gimplify_stmt (&body);
if (last_expr)
{
tree *sub_p;
if (!STMT_EXPR_NO_SCOPE (*expr_p))
{
body = expr_first (body);
sub_p = &BIND_EXPR_BODY (body);
append_to_statement_list_force (last_expr, sub_p);
TREE_TYPE (body) = TREE_TYPE (last_expr);
}
else
append_to_compound_expr (last_expr, &body);
}
*expr_p = body;
return GS_OK;
}
}
#if defined ENABLE_CHECKING
static int
is_last_stmt_of_scope (tree stmt)
{
return (TREE_CHAIN (stmt) == NULL_TREE
|| (TREE_CODE (TREE_CHAIN (stmt)) == SCOPE_STMT
&& SCOPE_END_P (TREE_CHAIN (stmt))));
}
#endif