#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "errors.h"
#include "varray.h"
#include "tree-gimple.h"
#include "tree-inline.h"
#include "diagnostic.h"
#include "langhooks.h"
#include "langhooks-def.h"
#include "tree-flow.h"
#include "timevar.h"
#include "except.h"
#include "hashtab.h"
#include "flags.h"
#include "real.h"
#include "function.h"
#include "output.h"
#include "expr.h"
#include "ggc.h"
static struct gimplify_ctx
{
tree current_bind_expr;
bool save_stack;
tree temps;
tree conditional_cleanups;
int conditions;
tree exit_label;
varray_type case_labels;
htab_t temp_htab;
} *gimplify_ctxp;
typedef struct gimple_temp_hash_elt
{
tree val;
tree temp;
} elt_t;
static hashval_t
gimple_tree_hash (const void *p)
{
tree t = ((const elt_t *)p)->val;
return iterative_hash_expr (t, 0);
}
static int
gimple_tree_eq (const void *p1, const void *p2)
{
tree t1 = ((const elt_t *)p1)->val;
tree t2 = ((const elt_t *)p2)->val;
enum tree_code code = TREE_CODE (t1);
if (TREE_CODE (t2) != code
|| TREE_TYPE (t1) != TREE_TYPE (t2))
return 0;
if (!operand_equal_p (t1, t2, 0))
return 0;
if (gimple_tree_hash (p1) != gimple_tree_hash (p2))
abort ();
return 1;
}
void
push_gimplify_context (void)
{
if (gimplify_ctxp)
abort ();
gimplify_ctxp
= (struct gimplify_ctx *) xcalloc (1, sizeof (struct gimplify_ctx));
gimplify_ctxp->temp_htab
= htab_create (1000, gimple_tree_hash, gimple_tree_eq, free);
}
void
pop_gimplify_context (tree body)
{
if (!gimplify_ctxp || gimplify_ctxp->current_bind_expr)
abort ();
if (body)
declare_tmp_vars (gimplify_ctxp->temps, body);
else
record_vars (gimplify_ctxp->temps);
#if 0
if (!quiet_flag)
fprintf (stderr, " collisions: %f ",
htab_collisions (gimplify_ctxp->temp_htab));
#endif
htab_delete (gimplify_ctxp->temp_htab);
free (gimplify_ctxp);
gimplify_ctxp = NULL;
}
void
gimple_push_bind_expr (tree bind)
{
TREE_CHAIN (bind) = gimplify_ctxp->current_bind_expr;
gimplify_ctxp->current_bind_expr = bind;
}
void
gimple_pop_bind_expr (void)
{
gimplify_ctxp->current_bind_expr
= TREE_CHAIN (gimplify_ctxp->current_bind_expr);
}
tree
gimple_current_bind_expr (void)
{
return gimplify_ctxp->current_bind_expr;
}
static bool
gimple_conditional_context (void)
{
return gimplify_ctxp->conditions > 0;
}
static void
gimple_push_condition (void)
{
++(gimplify_ctxp->conditions);
}
static void
gimple_pop_condition (tree *pre_p)
{
int conds = --(gimplify_ctxp->conditions);
if (conds == 0)
{
append_to_statement_list (gimplify_ctxp->conditional_cleanups, pre_p);
gimplify_ctxp->conditional_cleanups = NULL_TREE;
}
else if (conds < 0)
abort ();
}
static void
append_to_statement_list_1 (tree t, tree *list_p, bool side_effects)
{
tree list = *list_p;
tree_stmt_iterator i;
if (!list)
{
if (t && TREE_CODE (t) == STATEMENT_LIST)
{
*list_p = t;
return;
}
*list_p = list = alloc_stmt_list ();
}
if (!side_effects)
return;
i = tsi_last (list);
tsi_link_after (&i, t, TSI_CONTINUE_LINKING);
}
void
append_to_statement_list (tree t, tree *list_p)
{
append_to_statement_list_1 (t, list_p, t ? TREE_SIDE_EFFECTS (t) : false);
}
void
append_to_statement_list_force (tree t, tree *list_p)
{
append_to_statement_list_1 (t, list_p, t != NULL);
}
void
append_to_compound_expr (tree t, tree *list_p)
{
if (!t)
return;
if (!*list_p)
*list_p = t;
else
*list_p = build (COMPOUND_EXPR, TREE_TYPE (t), *list_p, t);
}
static inline void
remove_suffix (char *name, int len)
{
int i;
for (i = 2; i < 8 && len > i; i++)
{
if (name[len - i] == '.')
{
name[len - i] = '\0';
break;
}
}
}
tree
create_artificial_label (void)
{
tree lab = build_decl (LABEL_DECL, NULL_TREE, void_type_node);
DECL_ARTIFICIAL (lab) = 1;
DECL_CONTEXT (lab) = current_function_decl;
return lab;
}
static GTY(()) unsigned int tmp_var_id_num;
tree
create_tmp_var_name (const char *prefix)
{
char *tmp_name;
if (prefix)
{
char *preftmp = ASTRDUP (prefix);
remove_suffix (preftmp, strlen (preftmp));
prefix = preftmp;
}
ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix ? prefix : "T", tmp_var_id_num++);
return get_identifier (tmp_name);
}
tree
create_tmp_var_raw (tree type, const char *prefix)
{
tree tmp_var;
tree new_type;
new_type = build_type_variant (type, 0, 0);
TYPE_ATTRIBUTES (new_type) = TYPE_ATTRIBUTES (type);
tmp_var = build_decl (VAR_DECL, create_tmp_var_name (prefix), type);
DECL_ARTIFICIAL (tmp_var) = 1;
DECL_IGNORED_P (tmp_var) = 1;
TREE_READONLY (tmp_var) = 0;
DECL_EXTERNAL (tmp_var) = 0;
TREE_STATIC (tmp_var) = 0;
TREE_USED (tmp_var) = 1;
return tmp_var;
}
tree
create_tmp_var (tree type, const char *prefix)
{
tree tmp_var;
#if defined ENABLE_CHECKING
if (TREE_CODE (type) == ARRAY_TYPE || TREE_ADDRESSABLE (type))
abort ();
if (!COMPLETE_TYPE_P (type))
abort ();
if (TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST)
abort ();
#endif
tmp_var = create_tmp_var_raw (type, prefix);
gimple_add_tmp_var (tmp_var);
return tmp_var;
}
const char *
get_name (tree t)
{
tree stripped_decl;
stripped_decl = t;
STRIP_NOPS (stripped_decl);
if (DECL_P (stripped_decl) && DECL_NAME (stripped_decl))
return IDENTIFIER_POINTER (DECL_NAME (stripped_decl));
else
{
switch (TREE_CODE (stripped_decl))
{
case ADDR_EXPR:
return get_name (TREE_OPERAND (stripped_decl, 0));
break;
default:
return NULL;
}
}
}
static inline tree
create_tmp_from_val (tree val)
{
return create_tmp_var (TREE_TYPE (val), get_name (val));
}
static tree
lookup_tmp_var (tree val, bool is_formal)
{
if (!is_formal || TREE_SIDE_EFFECTS (val))
return create_tmp_from_val (val);
else
{
elt_t elt, *elt_p;
void **slot;
elt.val = val;
slot = htab_find_slot (gimplify_ctxp->temp_htab, (void *)&elt, INSERT);
if (*slot == NULL)
{
elt_p = xmalloc (sizeof (*elt_p));
elt_p->val = val;
elt_p->temp = create_tmp_from_val (val);
*slot = (void *)elt_p;
}
else
elt_p = (elt_t *) *slot;
return elt_p->temp;
}
}
static tree
internal_get_tmp_var (tree val, tree *pre_p, tree *post_p, bool is_formal)
{
tree t, mod;
char class;
gimplify_expr (&val, pre_p, post_p, is_gimple_rhs, fb_rvalue);
t = lookup_tmp_var (val, is_formal);
mod = build (MODIFY_EXPR, TREE_TYPE (t), t, val);
class = TREE_CODE_CLASS (TREE_CODE (val));
if (EXPR_LOCUS (val))
SET_EXPR_LOCUS (mod, EXPR_LOCUS (val));
else
annotate_with_locus (mod, input_location);
gimplify_stmt (&mod);
append_to_statement_list (mod, pre_p);
return t;
}
tree
get_formal_tmp_var (tree val, tree *pre_p)
{
return internal_get_tmp_var (val, pre_p, NULL, true);
}
tree
get_initialized_tmp_var (tree val, tree *pre_p, tree *post_p)
{
return internal_get_tmp_var (val, pre_p, post_p, false);
}
bool
is_gimple_tmp_var (tree t)
{
return (TREE_CODE (t) == VAR_DECL && DECL_ARTIFICIAL (t)
&& !TREE_STATIC (t) && !DECL_EXTERNAL (t));
}
void
declare_tmp_vars (tree vars, tree scope)
{
tree last = vars;
if (last)
{
tree temps;
while (TREE_CODE (scope) == COMPOUND_EXPR)
scope = TREE_OPERAND (scope, 0);
if (TREE_CODE (scope) != BIND_EXPR)
abort ();
temps = nreverse (last);
TREE_CHAIN (last) = BIND_EXPR_VARS (scope);
BIND_EXPR_VARS (scope) = temps;
}
}
void
gimple_add_tmp_var (tree tmp)
{
if (TREE_CHAIN (tmp) || tmp->decl.seen_in_bind_expr)
abort ();
DECL_CONTEXT (tmp) = current_function_decl;
tmp->decl.seen_in_bind_expr = 1;
if (gimplify_ctxp)
{
TREE_CHAIN (tmp) = gimplify_ctxp->temps;
gimplify_ctxp->temps = tmp;
}
else if (cfun)
record_vars (tmp);
else
declare_tmp_vars (tmp, DECL_SAVED_TREE (current_function_decl));
}
static bool
should_carry_locus_p (tree stmt)
{
if (TREE_CODE (stmt) == LABEL_EXPR)
return false;
if (!TREE_SIDE_EFFECTS (stmt))
return false;
return true;
}
void
annotate_all_with_locus (tree *stmt_p, location_t locus)
{
tree_stmt_iterator i;
if (!*stmt_p)
return;
for (i = tsi_start (*stmt_p); !tsi_end_p (i); tsi_next (&i))
{
tree t = tsi_stmt (i);
#ifdef ENABLE_CHECKING
if (TREE_CODE (t) == STATEMENT_LIST
|| TREE_CODE (t) == COMPOUND_EXPR)
abort ();
#endif
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (t)))
&& ! EXPR_HAS_LOCATION (t)
&& should_carry_locus_p (t))
annotate_with_locus (t, locus);
}
}
static tree
mostly_copy_tree_r (tree *tp, int *walk_subtrees, void *data)
{
enum tree_code code = TREE_CODE (*tp);
if (TREE_CODE_CLASS (code) == 't'
|| TREE_CODE_CLASS (code) == 'd'
|| TREE_CODE_CLASS (code) == 'c'
|| code == SAVE_EXPR || code == TARGET_EXPR
|| code == BLOCK)
*walk_subtrees = 0;
else if (code == BIND_EXPR)
abort ();
else
copy_tree_r (tp, walk_subtrees, data);
return NULL_TREE;
}
static tree
mark_decls_volatile_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
if (SSA_VAR_P (*tp))
TREE_THIS_VOLATILE (*tp) = 1;
return NULL_TREE;
}
static tree
copy_if_shared_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
tree t = *tp;
enum tree_code code = TREE_CODE (t);
if (TREE_CODE_CLASS (code) == 't'
|| TREE_CODE_CLASS (code) == 'd'
|| TREE_CODE_CLASS (code) == 'c')
*walk_subtrees = 0;
else if (code == BIND_EXPR)
{
if (TREE_VISITED (t))
abort ();
TREE_VISITED (t) = 1;
*walk_subtrees = 0;
walk_tree (&BIND_EXPR_BODY (t), copy_if_shared_r, NULL, NULL);
}
else if (TREE_VISITED (t))
{
walk_tree (tp, mostly_copy_tree_r, NULL, NULL);
*walk_subtrees = 0;
}
else
{
TREE_VISITED (t) = 1;
if (TREE_CODE (*tp) == VA_ARG_EXPR)
{
walk_tree (&TREE_OPERAND (*tp, 0), mark_decls_volatile_r,
NULL, NULL);
}
}
return NULL_TREE;
}
static tree
unmark_visited_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
if (TREE_VISITED (*tp))
TREE_VISITED (*tp) = 0;
else
*walk_subtrees = 0;
return NULL_TREE;
}
void
unshare_all_trees (tree t)
{
walk_tree (&t, copy_if_shared_r, NULL, NULL);
walk_tree (&t, unmark_visited_r, NULL, NULL);
}
tree
unshare_expr (tree expr)
{
walk_tree (&expr, mostly_copy_tree_r, NULL, NULL);
return expr;
}
tree
gimple_build_eh_filter (tree body, tree allowed, tree failure)
{
tree t;
t = build (EH_FILTER_EXPR, void_type_node, allowed, NULL_TREE);
append_to_statement_list (failure, &EH_FILTER_FAILURE (t));
t = build (TRY_CATCH_EXPR, void_type_node, NULL_TREE, t);
append_to_statement_list (body, &TREE_OPERAND (t, 0));
return t;
}
tree
voidify_wrapper_expr (tree wrapper)
{
if (!VOID_TYPE_P (TREE_TYPE (wrapper)))
{
tree *p;
tree temp;
switch (TREE_CODE (wrapper))
{
case BIND_EXPR:
p = &BIND_EXPR_BODY (wrapper);
break;
default:
p = &TREE_OPERAND (wrapper, 0);
break;
}
if (TREE_CODE (*p) == STATEMENT_LIST)
{
tree_stmt_iterator i = tsi_last (*p);
p = tsi_end_p (i) ? NULL : tsi_stmt_ptr (i);
}
else
{
for (; TREE_CODE (*p) == COMPOUND_EXPR; p = &TREE_OPERAND (*p, 1))
{
TREE_SIDE_EFFECTS (*p) = 1;
TREE_TYPE (*p) = void_type_node;
}
}
if (p && TREE_CODE (*p) == INIT_EXPR)
{
;
temp = TREE_OPERAND (*p, 0);
}
else if (p && TREE_CODE (*p) == INDIRECT_REF)
{
tree ptr = TREE_OPERAND (*p, 0);
temp = create_tmp_var (TREE_TYPE (ptr), "retval");
*p = build (MODIFY_EXPR, TREE_TYPE (ptr), temp, ptr);
temp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (temp)), temp);
TREE_SIDE_EFFECTS (wrapper) = 1;
}
else
{
temp = create_tmp_var (TREE_TYPE (wrapper), "retval");
if (p && !IS_EMPTY_STMT (*p))
{
*p = build (MODIFY_EXPR, TREE_TYPE (temp), temp, *p);
TREE_SIDE_EFFECTS (wrapper) = 1;
}
}
TREE_TYPE (wrapper) = void_type_node;
return temp;
}
return NULL_TREE;
}
static void
build_stack_save_restore (tree *save, tree *restore)
{
tree save_call, tmp_var;
save_call =
build_function_call_expr (implicit_built_in_decls[BUILT_IN_STACK_SAVE],
NULL_TREE);
tmp_var = create_tmp_var (ptr_type_node, "saved_stack");
*save = build (MODIFY_EXPR, ptr_type_node, tmp_var, save_call);
*restore =
build_function_call_expr (implicit_built_in_decls[BUILT_IN_STACK_RESTORE],
tree_cons (NULL_TREE, tmp_var, NULL_TREE));
}
static enum gimplify_status
gimplify_bind_expr (tree *expr_p, tree *pre_p)
{
tree bind_expr = *expr_p;
tree temp = voidify_wrapper_expr (bind_expr);
bool old_save_stack = gimplify_ctxp->save_stack;
tree t;
for (t = BIND_EXPR_VARS (bind_expr); t ; t = TREE_CHAIN (t))
t->decl.seen_in_bind_expr = 1;
gimple_push_bind_expr (bind_expr);
gimplify_ctxp->save_stack = false;
gimplify_to_stmt_list (&BIND_EXPR_BODY (bind_expr));
if (gimplify_ctxp->save_stack)
{
tree stack_save, stack_restore;
build_stack_save_restore (&stack_save, &stack_restore);
t = build (TRY_FINALLY_EXPR, void_type_node,
BIND_EXPR_BODY (bind_expr), NULL_TREE);
append_to_statement_list (stack_restore, &TREE_OPERAND (t, 1));
BIND_EXPR_BODY (bind_expr) = NULL_TREE;
append_to_statement_list (stack_save, &BIND_EXPR_BODY (bind_expr));
append_to_statement_list (t, &BIND_EXPR_BODY (bind_expr));
}
gimplify_ctxp->save_stack = old_save_stack;
gimple_pop_bind_expr ();
if (temp)
{
*expr_p = temp;
append_to_statement_list (bind_expr, pre_p);
return GS_OK;
}
else
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_return_expr (tree stmt, tree *pre_p)
{
tree ret_expr = TREE_OPERAND (stmt, 0);
tree result;
if (!ret_expr || TREE_CODE (ret_expr) == RESULT_DECL)
return GS_ALL_DONE;
if (ret_expr == error_mark_node)
return GS_ERROR;
if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
result = NULL_TREE;
else
{
result = TREE_OPERAND (ret_expr, 0);
#ifdef ENABLE_CHECKING
if ((TREE_CODE (ret_expr) != MODIFY_EXPR
&& TREE_CODE (ret_expr) != INIT_EXPR)
|| TREE_CODE (result) != RESULT_DECL)
abort ();
#endif
}
gimplify_stmt (&ret_expr);
if (result == NULL_TREE)
TREE_OPERAND (stmt, 0) = NULL_TREE;
else if (ret_expr == TREE_OPERAND (stmt, 0))
return GS_ALL_DONE;
else
{
tree ret = NULL_TREE;
if (TREE_CODE (ret_expr) == STATEMENT_LIST)
{
tree_stmt_iterator si;
for (si = tsi_start (ret_expr); !tsi_end_p (si); tsi_next (&si))
{
tree sub = tsi_stmt (si);
if (TREE_CODE (sub) == MODIFY_EXPR
&& TREE_OPERAND (sub, 0) == result)
{
ret = sub;
if (tsi_one_before_end_p (si))
tsi_delink (&si);
else
{
tree tmp = create_tmp_var (TREE_TYPE (result), "retval");
TREE_OPERAND (ret, 0) = tmp;
ret = build (MODIFY_EXPR, TREE_TYPE (result),
result, tmp);
}
break;
}
}
}
if (ret)
TREE_OPERAND (stmt, 0) = ret;
else
TREE_OPERAND (stmt, 0) = result;
}
append_to_statement_list (ret_expr, pre_p);
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_loop_expr (tree *expr_p, tree *pre_p)
{
tree saved_label = gimplify_ctxp->exit_label;
tree start_label = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
tree jump_stmt = build_and_jump (&LABEL_EXPR_LABEL (start_label));
append_to_statement_list (start_label, pre_p);
gimplify_ctxp->exit_label = NULL_TREE;
gimplify_stmt (&LOOP_EXPR_BODY (*expr_p));
append_to_statement_list (LOOP_EXPR_BODY (*expr_p), pre_p);
if (gimplify_ctxp->exit_label)
{
append_to_statement_list (jump_stmt, pre_p);
*expr_p = build1 (LABEL_EXPR, void_type_node, gimplify_ctxp->exit_label);
}
else
*expr_p = jump_stmt;
gimplify_ctxp->exit_label = saved_label;
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_switch_expr (tree *expr_p, tree *pre_p)
{
tree switch_expr = *expr_p;
enum gimplify_status ret;
ret = gimplify_expr (&SWITCH_COND (switch_expr), pre_p, NULL,
is_gimple_val, fb_rvalue);
if (SWITCH_BODY (switch_expr))
{
varray_type labels, saved_labels;
bool saw_default;
tree label_vec, t;
size_t i, len;
if (SWITCH_LABELS (switch_expr))
abort ();
saved_labels = gimplify_ctxp->case_labels;
VARRAY_TREE_INIT (gimplify_ctxp->case_labels, 8, "case_labels");
gimplify_to_stmt_list (&SWITCH_BODY (switch_expr));
labels = gimplify_ctxp->case_labels;
gimplify_ctxp->case_labels = saved_labels;
len = VARRAY_ACTIVE_SIZE (labels);
saw_default = false;
for (i = 0; i < len; ++i)
{
t = VARRAY_TREE (labels, i);
if (!CASE_LOW (t))
{
saw_default = true;
break;
}
}
label_vec = make_tree_vec (len + !saw_default);
SWITCH_LABELS (*expr_p) = label_vec;
for (i = 0; i < len; ++i)
TREE_VEC_ELT (label_vec, i) = VARRAY_TREE (labels, i);
append_to_statement_list (switch_expr, pre_p);
if (!saw_default)
{
t = build (CASE_LABEL_EXPR, void_type_node, NULL_TREE,
NULL_TREE, create_artificial_label ());
TREE_VEC_ELT (label_vec, len) = t;
append_to_statement_list (SWITCH_BODY (switch_expr), pre_p);
*expr_p = build (LABEL_EXPR, void_type_node, CASE_LABEL (t));
}
else
*expr_p = SWITCH_BODY (switch_expr);
SWITCH_BODY (switch_expr) = NULL;
}
else if (!SWITCH_LABELS (switch_expr))
abort ();
return ret;
}
static enum gimplify_status
gimplify_case_label_expr (tree *expr_p)
{
tree expr = *expr_p;
if (gimplify_ctxp->case_labels)
VARRAY_PUSH_TREE (gimplify_ctxp->case_labels, expr);
else
abort ();
*expr_p = build (LABEL_EXPR, void_type_node, CASE_LABEL (expr));
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_labeled_block_expr (tree *expr_p)
{
tree body = LABELED_BLOCK_BODY (*expr_p);
tree label = LABELED_BLOCK_LABEL (*expr_p);
tree t;
DECL_CONTEXT (label) = current_function_decl;
t = build (LABEL_EXPR, void_type_node, label);
if (body != NULL_TREE)
t = build (COMPOUND_EXPR, void_type_node, body, t);
*expr_p = t;
return GS_OK;
}
static enum gimplify_status
gimplify_exit_block_expr (tree *expr_p)
{
tree labeled_block = TREE_OPERAND (*expr_p, 0);
tree label;
#if defined ENABLE_CHECKING
if (TREE_CODE (labeled_block) != LABELED_BLOCK_EXPR)
abort ();
#endif
label = LABELED_BLOCK_LABEL (labeled_block);
*expr_p = build1 (GOTO_EXPR, void_type_node, label);
return GS_OK;
}
tree
build_and_jump (tree *label_p)
{
if (label_p == NULL)
return build_empty_stmt ();
if (*label_p == NULL_TREE)
{
tree label = create_artificial_label ();
*label_p = label;
}
return build1 (GOTO_EXPR, void_type_node, *label_p);
}
static enum gimplify_status
gimplify_exit_expr (tree *expr_p)
{
tree cond = TREE_OPERAND (*expr_p, 0);
tree expr;
expr = build_and_jump (&gimplify_ctxp->exit_label);
expr = build (COND_EXPR, void_type_node, cond, expr, build_empty_stmt ());
*expr_p = expr;
return GS_OK;
}
tree
force_labels_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
{
if (TYPE_P (*tp))
*walk_subtrees = 0;
if (TREE_CODE (*tp) == LABEL_DECL)
FORCED_LABEL (*tp) = 1;
return NULL_TREE;
}
static enum gimplify_status
gimplify_init_constructor (tree *expr_p, tree *pre_p,
tree *post_p, int want_value)
{
tree object = TREE_OPERAND (*expr_p, 0);
tree ctor = TREE_OPERAND (*expr_p, 1);
tree type = TREE_TYPE (ctor);
enum gimplify_status ret;
tree elt_list;
if (TREE_CODE (ctor) != CONSTRUCTOR)
return GS_UNHANDLED;
elt_list = CONSTRUCTOR_ELTS (ctor);
ret = GS_ALL_DONE;
switch (TREE_CODE (type))
{
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
case ARRAY_TYPE:
{
HOST_WIDE_INT i, num_elements, num_nonzero_elements;
HOST_WIDE_INT num_nonconstant_elements;
bool cleared;
if (elt_list == NULL)
{
if (want_value)
{
*expr_p = object;
return GS_OK;
}
else
return GS_ALL_DONE;
}
categorize_ctor_elements (ctor, &num_nonzero_elements,
&num_nonconstant_elements);
num_elements = count_type_elements (TREE_TYPE (ctor));
if (num_nonconstant_elements == 0
&& TREE_READONLY (object)
&& TREE_CODE (object) == VAR_DECL)
{
DECL_INITIAL (object) = ctor;
TREE_STATIC (object) = 1;
if (!DECL_NAME (object))
DECL_NAME (object) = create_tmp_var_name ("C");
walk_tree (&DECL_INITIAL (object), force_labels_r, NULL, NULL);
lhd_set_decl_assembler_name (object);
*expr_p = build_empty_stmt ();
break;
}
if (num_nonconstant_elements == 0)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
unsigned int align;
if (size < 0)
{
size = int_size_in_bytes (TREE_TYPE (object));
if (size >= 0)
TREE_TYPE (ctor) = type = TREE_TYPE (object);
}
if (DECL_P (object))
align = DECL_ALIGN (object);
else
align = TYPE_ALIGN (type);
if (size > 0 && !can_move_by_pieces (size, align))
{
tree new = create_tmp_var_raw (type, "C");
gimple_add_tmp_var (new);
TREE_STATIC (new) = 1;
TREE_READONLY (new) = 1;
DECL_INITIAL (new) = ctor;
if (align > DECL_ALIGN (new))
{
DECL_ALIGN (new) = align;
DECL_USER_ALIGN (new) = 1;
}
walk_tree (&DECL_INITIAL (new), force_labels_r, NULL, NULL);
TREE_OPERAND (*expr_p, 1) = new;
break;
}
}
cleared = false;
if (num_elements - num_nonzero_elements > CLEAR_RATIO
&& num_nonzero_elements < num_elements/4)
cleared = true;
else
{
HOST_WIDE_INT len = list_length (elt_list);
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree nelts = array_type_nelts (type);
if (!host_integerp (nelts, 1)
|| tree_low_cst (nelts, 1) != len)
cleared = 1;;
}
else if (len != fields_length (type))
cleared = 1;
}
if (cleared)
{
CONSTRUCTOR_ELTS (ctor) = NULL_TREE;
append_to_statement_list (*expr_p, pre_p);
}
for (i = 0; elt_list; i++, elt_list = TREE_CHAIN (elt_list))
{
tree purpose, value, cref, init;
purpose = TREE_PURPOSE (elt_list);
value = TREE_VALUE (elt_list);
if (cleared && initializer_zerop (value))
continue;
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree t = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
if (!purpose)
abort ();
if (TREE_CODE (purpose) == RANGE_EXPR)
abort ();
cref = build (ARRAY_REF, t, object, purpose);
}
else
{
cref = build (COMPONENT_REF, TREE_TYPE (purpose),
object, purpose);
}
init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value);
gimplify_stmt (&init);
append_to_statement_list (init, pre_p);
}
*expr_p = build_empty_stmt ();
}
break;
case COMPLEX_TYPE:
{
tree r, i;
r = i = NULL_TREE;
if (elt_list)
{
r = TREE_VALUE (elt_list);
elt_list = TREE_CHAIN (elt_list);
if (elt_list)
{
i = TREE_VALUE (elt_list);
if (TREE_CHAIN (elt_list))
abort ();
}
}
if (r == NULL || i == NULL)
{
tree zero = convert (TREE_TYPE (type), integer_zero_node);
if (r == NULL)
r = zero;
if (i == NULL)
i = zero;
}
if (TREE_CONSTANT (r) && TREE_CONSTANT (i))
{
ctor = build_complex (type, r, i);
TREE_OPERAND (*expr_p, 1) = ctor;
}
else
{
ctor = build (COMPLEX_EXPR, type, r, i);
TREE_OPERAND (*expr_p, 1) = ctor;
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
is_gimple_rhs, fb_rvalue);
}
}
break;
case VECTOR_TYPE:
if (TREE_CONSTANT (ctor))
TREE_OPERAND (*expr_p, 1) = build_vector (type, elt_list);
else
{
for (; elt_list; elt_list = TREE_CHAIN (elt_list))
{
enum gimplify_status tret;
tret = gimplify_expr (&TREE_VALUE (elt_list), pre_p, post_p,
is_gimple_constructor_elt, fb_rvalue);
if (tret == GS_ERROR)
ret = GS_ERROR;
}
}
break;
default:
abort ();
}
if (ret == GS_ERROR)
return GS_ERROR;
else if (want_value)
{
append_to_statement_list (*expr_p, pre_p);
*expr_p = object;
return GS_OK;
}
else
return GS_ALL_DONE;
}
static void
canonicalize_component_ref (tree *expr_p)
{
tree expr = *expr_p;
tree type;
if (TREE_CODE (expr) != COMPONENT_REF)
abort ();
if (INTEGRAL_TYPE_P (TREE_TYPE (expr)))
type = TREE_TYPE (get_unwidened (expr, NULL_TREE));
else
type = TREE_TYPE (TREE_OPERAND (expr, 1));
if (TREE_TYPE (expr) != type)
{
tree old_type = TREE_TYPE (expr);
TREE_TYPE (expr) = type;
expr = build1 (NOP_EXPR, old_type, expr);
*expr_p = expr;
}
}
static void
canonicalize_addr_expr (tree* expr_p)
{
tree expr = *expr_p;
tree ctype = TREE_TYPE (expr);
tree addr_expr = TREE_OPERAND (expr, 0);
tree atype = TREE_TYPE (addr_expr);
tree dctype, datype, ddatype, otype, obj_expr;
if (!POINTER_TYPE_P (ctype) || !POINTER_TYPE_P (atype))
return;
datype = TREE_TYPE (atype);
if (TREE_CODE (datype) != ARRAY_TYPE)
return;
dctype = TREE_TYPE (ctype);
ddatype = TREE_TYPE (datype);
if (!lang_hooks.types_compatible_p (ddatype, dctype))
return;
obj_expr = TREE_OPERAND (addr_expr, 0);
otype = TREE_TYPE (obj_expr);
if (!lang_hooks.types_compatible_p (otype, datype))
return;
*expr_p = build1 (ADDR_EXPR, ctype, obj_expr);
}
static enum gimplify_status
gimplify_conversion (tree *expr_p)
{
STRIP_USELESS_TYPE_CONVERSION (*expr_p);
if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR)
{
STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
if (tree_ssa_useless_type_conversion (*expr_p))
*expr_p = TREE_OPERAND (*expr_p, 0);
}
if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR)
{
tree sub = TREE_OPERAND (*expr_p, 0);
if (TREE_CODE (sub) == COMPONENT_REF)
canonicalize_component_ref (&TREE_OPERAND (*expr_p, 0));
else if (TREE_CODE (sub) == ADDR_EXPR)
canonicalize_addr_expr (expr_p);
}
return GS_OK;
}
static enum gimplify_status
gimplify_minimax_expr (tree *expr_p, tree *pre_p, tree *post_p)
{
tree op1 = TREE_OPERAND (*expr_p, 0);
tree op2 = TREE_OPERAND (*expr_p, 1);
enum tree_code code;
enum gimplify_status r0, r1;
if (TREE_CODE (*expr_p) == MIN_EXPR)
code = LE_EXPR;
else
code = GE_EXPR;
r0 = gimplify_expr (&op1, pre_p, post_p, is_gimple_val, fb_rvalue);
r1 = gimplify_expr (&op2, pre_p, post_p, is_gimple_val, fb_rvalue);
*expr_p = build (COND_EXPR, TREE_TYPE (*expr_p),
build (code, boolean_type_node, op1, op2),
op1, op2);
if (r0 == GS_ERROR || r1 == GS_ERROR)
return GS_ERROR;
else
return GS_OK;
}
static tree
build_addr_expr_with_type (tree t, tree ptrtype)
{
if (TREE_CODE (t) == INDIRECT_REF)
{
t = TREE_OPERAND (t, 0);
if (TREE_TYPE (t) != ptrtype)
t = build1 (NOP_EXPR, ptrtype, t);
}
else
{
tree base = t;
while (TREE_CODE (base) == COMPONENT_REF
|| TREE_CODE (base) == ARRAY_REF)
base = TREE_OPERAND (base, 0);
if (DECL_P (base))
TREE_ADDRESSABLE (base) = 1;
t = build1 (ADDR_EXPR, ptrtype, t);
}
return t;
}
static tree
build_addr_expr (tree t)
{
return build_addr_expr_with_type (t, build_pointer_type (TREE_TYPE (t)));
}
static enum gimplify_status
gimplify_array_ref_to_plus (tree *expr_p, tree *pre_p, tree *post_p)
{
tree array = TREE_OPERAND (*expr_p, 0);
tree arrtype = TREE_TYPE (array);
tree elttype = TREE_TYPE (arrtype);
tree size = size_in_bytes (elttype);
tree ptrtype = build_pointer_type (elttype);
enum tree_code add_code = PLUS_EXPR;
tree idx = TREE_OPERAND (*expr_p, 1);
tree minidx, offset, addr, result;
enum gimplify_status ret;
minidx = TYPE_DOMAIN (arrtype);
if (minidx)
{
minidx = TYPE_MIN_VALUE (minidx);
if (minidx && !integer_zerop (minidx))
{
idx = convert (TREE_TYPE (minidx), idx);
idx = fold (build (MINUS_EXPR, TREE_TYPE (minidx), idx, minidx));
}
}
if (TREE_CODE (idx) == INTEGER_CST && tree_int_cst_sgn (idx) < 0)
{
idx = fold (build1 (NEGATE_EXPR, TREE_TYPE (idx), idx));
add_code = MINUS_EXPR;
}
idx = convert (sizetype, idx);
offset = size_binop (MULT_EXPR, size, idx);
ret = gimplify_expr (&array, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
if (ret == GS_ERROR)
return ret;
addr = build_addr_expr_with_type (array, ptrtype);
result = fold (build (add_code, ptrtype, addr, offset));
*expr_p = build1 (INDIRECT_REF, elttype, result);
return GS_OK;
}
static enum gimplify_status
gimplify_compound_lval (tree *expr_p, tree *pre_p,
tree *post_p, int want_lvalue)
{
tree *p;
enum tree_code code;
varray_type stack;
enum gimplify_status ret;
#if defined ENABLE_CHECKING
if (TREE_CODE (*expr_p) != ARRAY_REF
&& TREE_CODE (*expr_p) != COMPONENT_REF
&& TREE_CODE (*expr_p) != REALPART_EXPR
&& TREE_CODE (*expr_p) != IMAGPART_EXPR)
abort ();
#endif
code = ERROR_MARK;
VARRAY_TREE_INIT (stack, 10, "stack");
for (p = expr_p;
TREE_CODE (*p) == ARRAY_REF
|| TREE_CODE (*p) == COMPONENT_REF
|| TREE_CODE (*p) == REALPART_EXPR
|| TREE_CODE (*p) == IMAGPART_EXPR;
p = &TREE_OPERAND (*p, 0))
{
code = TREE_CODE (*p);
if (code == ARRAY_REF)
{
tree elttype = TREE_TYPE (TREE_TYPE (TREE_OPERAND (*p, 0)));
if (!TREE_CONSTANT (TYPE_SIZE_UNIT (elttype)))
break;
}
VARRAY_PUSH_TREE (stack, *p);
}
ret = gimplify_expr (p, pre_p, post_p, is_gimple_min_lval,
code != ARRAY_REF ? fb_either : fb_lvalue);
for (; VARRAY_ACTIVE_SIZE (stack) > 0; )
{
tree t = VARRAY_TOP_TREE (stack);
if (TREE_CODE (t) == ARRAY_REF)
{
enum gimplify_status tret;
if (!is_gimple_min_invariant (TREE_OPERAND (t, 1)))
{
tret = gimplify_expr (&TREE_OPERAND (t, 1), pre_p, post_p,
is_gimple_tmp_var, fb_rvalue);
if (tret == GS_ERROR)
ret = GS_ERROR;
}
}
recalculate_side_effects (t);
VARRAY_POP (stack);
}
if (!want_lvalue && TREE_CODE (*expr_p) == COMPONENT_REF)
{
canonicalize_component_ref (expr_p);
ret = MIN (ret, GS_OK);
}
return ret;
}
static enum gimplify_status
gimplify_array_ref (tree *expr_p, tree *pre_p,
tree *post_p, int want_lvalue)
{
tree elttype = TREE_TYPE (TREE_TYPE (TREE_OPERAND (*expr_p, 0)));
if (!TREE_CONSTANT (TYPE_SIZE_UNIT (elttype)))
return gimplify_array_ref_to_plus (expr_p, pre_p, post_p);
else
return gimplify_compound_lval (expr_p, pre_p, post_p, want_lvalue);
}
static enum gimplify_status
gimplify_self_mod_expr (tree *expr_p, tree *pre_p, tree *post_p,
int want_value)
{
enum tree_code code;
tree lhs, lvalue, rhs, t1;
bool postfix;
enum tree_code arith_code;
enum gimplify_status ret;
code = TREE_CODE (*expr_p);
#if defined ENABLE_CHECKING
if (code != POSTINCREMENT_EXPR
&& code != POSTDECREMENT_EXPR
&& code != PREINCREMENT_EXPR
&& code != PREDECREMENT_EXPR)
abort ();
#endif
if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR)
postfix = want_value;
else
postfix = false;
if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR)
arith_code = PLUS_EXPR;
else
arith_code = MINUS_EXPR;
lvalue = TREE_OPERAND (*expr_p, 0);
ret = gimplify_expr (&lvalue, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
if (ret == GS_ERROR)
return ret;
lhs = lvalue;
rhs = TREE_OPERAND (*expr_p, 1);
if (postfix)
{
ret = gimplify_expr (&lhs, pre_p, post_p, is_gimple_val, fb_rvalue);
if (ret == GS_ERROR)
return ret;
}
t1 = build (arith_code, TREE_TYPE (*expr_p), lhs, rhs);
t1 = build (MODIFY_EXPR, TREE_TYPE (lvalue), lvalue, t1);
if (postfix)
{
gimplify_stmt (&t1);
append_to_statement_list (t1, post_p);
*expr_p = lhs;
return GS_ALL_DONE;
}
else
{
*expr_p = t1;
return GS_OK;
}
}
static enum gimplify_status
gimplify_call_expr (tree *expr_p, tree *pre_p, bool (*gimple_test_f) (tree))
{
tree decl;
tree arglist;
enum gimplify_status ret;
#if defined ENABLE_CHECKING
if (TREE_CODE (*expr_p) != CALL_EXPR)
abort ();
#endif
if (!EXPR_LOCUS (*expr_p))
annotate_with_locus (*expr_p, input_location);
decl = get_callee_fndecl (*expr_p);
if (decl && DECL_BUILT_IN (decl))
{
tree new;
if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_ALLOC)
gimplify_ctxp->save_stack = true;
if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_RESTORE)
gimplify_ctxp->save_stack = false;
new = simplify_builtin (*expr_p, gimple_test_f == is_gimple_stmt);
if (new && new != *expr_p)
{
*expr_p = new;
return GS_OK;
}
}
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, NULL,
is_gimple_val, fb_rvalue);
if (PUSH_ARGS_REVERSED)
TREE_OPERAND (*expr_p, 1) = nreverse (TREE_OPERAND (*expr_p, 1));
for (arglist = TREE_OPERAND (*expr_p, 1); arglist;
arglist = TREE_CHAIN (arglist))
{
enum gimplify_status t;
t = gimplify_expr (&TREE_VALUE (arglist), pre_p, NULL, is_gimple_val,
fb_rvalue);
if (t == GS_ERROR)
ret = GS_ERROR;
}
if (PUSH_ARGS_REVERSED)
TREE_OPERAND (*expr_p, 1) = nreverse (TREE_OPERAND (*expr_p, 1));
if (ret != GS_ERROR && decl && DECL_BUILT_IN (decl))
{
tree new = simplify_builtin (*expr_p, gimple_test_f == is_gimple_stmt);
if (new && new != *expr_p)
{
*expr_p = new;
return GS_OK;
}
}
if (TREE_CODE (*expr_p) == CALL_EXPR
&& (call_expr_flags (*expr_p) & (ECF_CONST | ECF_PURE)))
TREE_SIDE_EFFECTS (*expr_p) = 0;
return ret;
}
static tree
shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p)
{
tree local_label = NULL_TREE;
tree t, expr = NULL;
if (TREE_CODE (pred) == TRUTH_ANDIF_EXPR)
{
if (false_label_p == NULL)
false_label_p = &local_label;
t = shortcut_cond_r (TREE_OPERAND (pred, 0), NULL, false_label_p);
append_to_statement_list (t, &expr);
t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
false_label_p);
append_to_statement_list (t, &expr);
}
else if (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
{
if (true_label_p == NULL)
true_label_p = &local_label;
t = shortcut_cond_r (TREE_OPERAND (pred, 0), true_label_p, NULL);
append_to_statement_list (t, &expr);
t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
false_label_p);
append_to_statement_list (t, &expr);
}
else if (TREE_CODE (pred) == COND_EXPR)
{
expr = build (COND_EXPR, void_type_node, TREE_OPERAND (pred, 0),
shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
false_label_p),
shortcut_cond_r (TREE_OPERAND (pred, 2), true_label_p,
false_label_p));
}
else
{
expr = build (COND_EXPR, void_type_node, pred,
build_and_jump (true_label_p),
build_and_jump (false_label_p));
}
if (local_label)
{
t = build1 (LABEL_EXPR, void_type_node, local_label);
append_to_statement_list (t, &expr);
}
return expr;
}
static tree
shortcut_cond_expr (tree expr)
{
tree pred = TREE_OPERAND (expr, 0);
tree then_ = TREE_OPERAND (expr, 1);
tree else_ = TREE_OPERAND (expr, 2);
tree true_label, false_label, end_label, t;
tree *true_label_p;
tree *false_label_p;
bool emit_end, emit_false;
if (!TREE_SIDE_EFFECTS (else_))
{
while (TREE_CODE (pred) == TRUTH_ANDIF_EXPR)
{
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
then_ = shortcut_cond_expr (expr);
pred = TREE_OPERAND (pred, 0);
expr = build (COND_EXPR, void_type_node, pred, then_,
build_empty_stmt ());
}
}
if (!TREE_SIDE_EFFECTS (then_))
{
while (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
{
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
else_ = shortcut_cond_expr (expr);
pred = TREE_OPERAND (pred, 0);
expr = build (COND_EXPR, void_type_node, pred,
build_empty_stmt (), else_);
}
}
if (TREE_CODE (pred) != TRUTH_ANDIF_EXPR
&& TREE_CODE (pred) != TRUTH_ORIF_EXPR)
return expr;
true_label = false_label = end_label = NULL_TREE;
if (TREE_CODE (then_) == GOTO_EXPR
&& TREE_CODE (GOTO_DESTINATION (then_)) == LABEL_DECL)
{
true_label = GOTO_DESTINATION (then_);
then_ = build_empty_stmt ();
}
if (TREE_CODE (else_) == GOTO_EXPR
&& TREE_CODE (GOTO_DESTINATION (else_)) == LABEL_DECL)
{
false_label = GOTO_DESTINATION (else_);
else_ = build_empty_stmt ();
}
if (true_label)
true_label_p = &true_label;
else
true_label_p = NULL;
if (false_label || TREE_SIDE_EFFECTS (else_))
false_label_p = &false_label;
else
false_label_p = NULL;
if (!TREE_SIDE_EFFECTS (then_) && !TREE_SIDE_EFFECTS (else_))
return shortcut_cond_r (pred, true_label_p, false_label_p);
if (TREE_SIDE_EFFECTS (else_))
expr = expr_last (else_);
else
expr = expr_last (then_);
if (TREE_CODE (expr) == LABEL_EXPR)
end_label = LABEL_EXPR_LABEL (expr);
if (!false_label_p)
false_label_p = &end_label;
emit_end = (end_label == NULL_TREE);
emit_false = (false_label == NULL_TREE);
pred = shortcut_cond_r (pred, true_label_p, false_label_p);
expr = NULL;
append_to_statement_list (pred, &expr);
append_to_statement_list (then_, &expr);
if (TREE_SIDE_EFFECTS (else_))
{
t = build_and_jump (&end_label);
append_to_statement_list (t, &expr);
if (emit_false)
{
t = build1 (LABEL_EXPR, void_type_node, false_label);
append_to_statement_list (t, &expr);
}
append_to_statement_list (else_, &expr);
}
if (emit_end && end_label)
{
t = build1 (LABEL_EXPR, void_type_node, end_label);
append_to_statement_list (t, &expr);
}
return expr;
}
static tree
gimple_boolify (tree expr)
{
tree type = TREE_TYPE (expr);
if (TREE_CODE (type) == BOOLEAN_TYPE)
return expr;
expr = lang_hooks.truthvalue_conversion (expr);
switch (TREE_CODE (expr))
{
case TRUTH_AND_EXPR:
case TRUTH_OR_EXPR:
case TRUTH_XOR_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
TREE_OPERAND (expr, 1) = gimple_boolify (TREE_OPERAND (expr, 1));
case TRUTH_NOT_EXPR:
TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
case EQ_EXPR: case NE_EXPR:
case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR:
TREE_TYPE (expr) = boolean_type_node;
return expr;
default:
return convert (boolean_type_node, expr);
}
}
static enum gimplify_status
gimplify_cond_expr (tree *expr_p, tree *pre_p, tree target)
{
tree expr = *expr_p;
tree tmp;
enum gimplify_status ret;
if (! VOID_TYPE_P (TREE_TYPE (expr)))
{
if (target)
{
tmp = target;
ret = GS_OK;
}
else
{
tmp = create_tmp_var (TREE_TYPE (expr), "iftmp");
ret = GS_ALL_DONE;
}
if (TREE_TYPE (TREE_OPERAND (expr, 1)) != void_type_node)
TREE_OPERAND (expr, 1)
= build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 1));
if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node)
TREE_OPERAND (expr, 2)
= build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 2));
TREE_TYPE (expr) = void_type_node;
recalculate_side_effects (expr);
gimplify_stmt (&expr);
append_to_statement_list (expr, pre_p);
*expr_p = tmp;
return ret;
}
TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
if (TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ANDIF_EXPR
|| TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ORIF_EXPR)
{
expr = shortcut_cond_expr (expr);
if (expr != *expr_p)
{
*expr_p = expr;
gimple_push_condition ();
gimplify_stmt (expr_p);
gimple_pop_condition (pre_p);
return GS_ALL_DONE;
}
}
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
is_gimple_condexpr, fb_rvalue);
gimple_push_condition ();
gimplify_to_stmt_list (&TREE_OPERAND (expr, 1));
gimplify_to_stmt_list (&TREE_OPERAND (expr, 2));
recalculate_side_effects (expr);
gimple_pop_condition (pre_p);
if (ret == GS_ERROR)
;
else if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)))
ret = GS_ALL_DONE;
else if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 2)))
{
TREE_OPERAND (expr, 0) = invert_truthvalue (TREE_OPERAND (expr, 0));
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
is_gimple_condexpr, fb_rvalue);
tmp = TREE_OPERAND (expr, 1);
TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 2);
TREE_OPERAND (expr, 2) = tmp;
}
else
expr = TREE_OPERAND (expr, 0);
*expr_p = expr;
return ret;
}
static enum gimplify_status
gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value)
{
tree *from_p = &TREE_OPERAND (*expr_p, 1);
tree *to_p = &TREE_OPERAND (*expr_p, 0);
enum gimplify_status ret;
#if defined ENABLE_CHECKING
if (TREE_CODE (*expr_p) != MODIFY_EXPR && TREE_CODE (*expr_p) != INIT_EXPR)
abort ();
#endif
if (TREE_CODE (*expr_p) == INIT_EXPR)
TREE_SET_CODE (*expr_p, MODIFY_EXPR);
ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
if (ret == GS_ERROR)
return ret;
if (TREE_CODE (*from_p) == TARGET_EXPR)
*from_p = TARGET_EXPR_INITIAL (*from_p);
if (TREE_CODE (*from_p) == COND_EXPR
&& TREE_ADDRESSABLE (TREE_TYPE (*from_p)))
{
*expr_p = *from_p;
return gimplify_cond_expr (expr_p, pre_p, *to_p);
}
ret = gimplify_expr (from_p, pre_p, post_p, is_gimple_rhs, fb_rvalue);
if (ret == GS_ERROR)
return ret;
ret = gimplify_init_constructor (expr_p, pre_p, post_p, want_value);
if (ret != GS_UNHANDLED)
return ret;
if (is_gimple_tmp_var (*to_p))
ret = GS_ALL_DONE;
else
{
if (TREE_CODE (*from_p) == CALL_EXPR
|| (flag_non_call_exceptions && tree_could_trap_p (*from_p))
|| (is_gimple_reg_type (TREE_TYPE (*from_p))
&& !is_gimple_reg (*to_p)))
gimplify_expr (from_p, pre_p, post_p, is_gimple_val, fb_rvalue);
if (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (*to_p))) != INTEGER_CST
&& TREE_CODE (*from_p) != VA_ARG_EXPR)
{
tree args, t, dest;
t = TYPE_SIZE_UNIT (TREE_TYPE (*to_p));
t = unshare_expr (t);
args = tree_cons (NULL, t, NULL);
t = build_addr_expr (*from_p);
args = tree_cons (NULL, t, args);
dest = build_addr_expr (*to_p);
args = tree_cons (NULL, dest, args);
t = implicit_built_in_decls[BUILT_IN_MEMCPY];
t = build_function_call_expr (t, args);
if (want_value)
{
t = build1 (NOP_EXPR, TREE_TYPE (dest), t);
t = build1 (INDIRECT_REF, TREE_TYPE (*to_p), t);
}
*expr_p = t;
return GS_OK;
}
ret = want_value ? GS_OK : GS_ALL_DONE;
}
if (want_value)
{
append_to_statement_list (*expr_p, pre_p);
*expr_p = *to_p;
}
return ret;
}
static enum gimplify_status
gimplify_boolean_expr (tree *expr_p)
{
tree type = TREE_TYPE (*expr_p);
*expr_p = build (COND_EXPR, type, *expr_p,
convert (type, boolean_true_node),
convert (type, boolean_false_node));
return GS_OK;
}
static enum gimplify_status
gimplify_compound_expr (tree *expr_p, tree *pre_p, bool want_value)
{
tree t = *expr_p;
do
{
tree *sub_p = &TREE_OPERAND (t, 0);
if (TREE_CODE (*sub_p) == COMPOUND_EXPR)
gimplify_compound_expr (sub_p, pre_p, false);
else
gimplify_stmt (sub_p);
append_to_statement_list (*sub_p, pre_p);
t = TREE_OPERAND (t, 1);
}
while (TREE_CODE (t) == COMPOUND_EXPR);
*expr_p = t;
if (want_value)
return GS_OK;
else
{
gimplify_stmt (expr_p);
return GS_ALL_DONE;
}
}
static enum gimplify_status
gimplify_statement_list (tree *expr_p)
{
tree_stmt_iterator i = tsi_start (*expr_p);
while (!tsi_end_p (i))
{
tree t;
gimplify_stmt (tsi_stmt_ptr (i));
t = tsi_stmt (i);
if (TREE_CODE (t) == STATEMENT_LIST)
{
tsi_link_before (&i, t, TSI_SAME_STMT);
tsi_delink (&i);
}
else
tsi_next (&i);
}
return GS_ALL_DONE;
}
static enum gimplify_status
gimplify_save_expr (tree *expr_p, tree *pre_p, tree *post_p)
{
enum gimplify_status ret = GS_ALL_DONE;
tree val;
#if defined ENABLE_CHECKING
if (TREE_CODE (*expr_p) != SAVE_EXPR)
abort ();
#endif
val = TREE_OPERAND (*expr_p, 0);
if (is_gimple_tmp_var (val))
*expr_p = val;
else if (TREE_TYPE (val) == void_type_node)
{
tree body = TREE_OPERAND (*expr_p, 0);
ret = gimplify_expr (& body, pre_p, post_p, is_gimple_stmt, fb_none);
append_to_statement_list (body, pre_p);
*expr_p = build_empty_stmt ();
}
else
*expr_p = TREE_OPERAND (*expr_p, 0)
= get_initialized_tmp_var (val, pre_p, post_p);
return ret;
}
static enum gimplify_status
gimplify_addr_expr (tree *expr_p, tree *pre_p, tree *post_p)
{
tree expr = *expr_p;
tree op0 = TREE_OPERAND (expr, 0);
enum gimplify_status ret;
switch (TREE_CODE (op0))
{
case INDIRECT_REF:
*expr_p = TREE_OPERAND (op0, 0);
ret = GS_OK;
break;
case ARRAY_REF:
ret = gimplify_array_ref_to_plus (&TREE_OPERAND (expr, 0),
pre_p, post_p);
op0 = TREE_OPERAND (TREE_OPERAND (expr, 0), 0);
*expr_p = op0;
break;
default:
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, post_p,
is_gimple_addr_expr_arg, fb_either);
if (ret != GS_ERROR)
{
TREE_SIDE_EFFECTS (expr) = 0;
recompute_tree_invarant_for_addr_expr (expr);
lang_hooks.mark_addressable (TREE_OPERAND (expr, 0));
}
break;
}
if (DECL_P (TREE_OPERAND (expr, 0)))
TREE_INVARIANT (expr) = 1;
return ret;
}
static enum gimplify_status
gimplify_asm_expr (tree *expr_p, tree *pre_p, tree *post_p)
{
tree expr = *expr_p;
int noutputs = list_length (ASM_OUTPUTS (expr));
const char **oconstraints
= (const char **) alloca ((noutputs) * sizeof (const char *));
int i;
tree link;
const char *constraint;
bool allows_mem, allows_reg, is_inout;
enum gimplify_status ret, tret;
ASM_STRING (expr)
= resolve_asm_operand_names (ASM_STRING (expr), ASM_OUTPUTS (expr),
ASM_INPUTS (expr));
ret = GS_ALL_DONE;
for (i = 0, link = ASM_OUTPUTS (expr); link; ++i, link = TREE_CHAIN (link))
{
oconstraints[i] = constraint
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
parse_output_constraint (&constraint, i, 0, 0,
&allows_mem, &allows_reg, &is_inout);
if (!allows_reg && allows_mem)
lang_hooks.mark_addressable (TREE_VALUE (link));
tret = gimplify_expr (&TREE_VALUE (link), pre_p, post_p,
is_inout ? is_gimple_min_lval : is_gimple_lvalue,
fb_lvalue | fb_mayfail);
if (tret == GS_ERROR)
{
error ("invalid lvalue in asm output %d", i);
ret = tret;
}
if (is_inout)
{
tree input;
char buf[10];
size_t constraint_len = strlen (constraint);
char *p = xstrdup (constraint);
p[0] = '=';
TREE_VALUE (TREE_PURPOSE (link)) = build_string (constraint_len, p);
free (p);
if (allows_reg)
{
sprintf (buf, "%d", i);
input = build_string (strlen (buf), buf);
}
else
input = build_string (constraint_len - 1, constraint + 1);
input = build_tree_list (build_tree_list (NULL_TREE, input),
unshare_expr (TREE_VALUE (link)));
ASM_INPUTS (expr) = chainon (ASM_INPUTS (expr), input);
}
}
for (link = ASM_INPUTS (expr); link; ++i, link = TREE_CHAIN (link))
{
constraint
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
parse_input_constraint (&constraint, 0, 0, noutputs, 0,
oconstraints, &allows_mem, &allows_reg);
if (!allows_reg && allows_mem)
{
lang_hooks.mark_addressable (TREE_VALUE (link));
tret = gimplify_expr (&TREE_VALUE (link), pre_p, post_p,
is_gimple_lvalue, fb_lvalue | fb_mayfail);
if (tret == GS_ERROR)
{
error ("memory input %d is not directly addressable", i);
ret = tret;
}
}
else
{
tret = gimplify_expr (&TREE_VALUE (link), pre_p, post_p,
is_gimple_val, fb_rvalue);
if (tret == GS_ERROR)
ret = tret;
}
}
return ret;
}
static enum gimplify_status
gimplify_cleanup_point_expr (tree *expr_p, tree *pre_p)
{
tree_stmt_iterator iter;
tree body;
tree temp = voidify_wrapper_expr (*expr_p);
int old_conds = gimplify_ctxp->conditions;
gimplify_ctxp->conditions = 0;
body = TREE_OPERAND (*expr_p, 0);
gimplify_to_stmt_list (&body);
gimplify_ctxp->conditions = old_conds;
for (iter = tsi_start (body); !tsi_end_p (iter); )
{
tree *wce_p = tsi_stmt_ptr (iter);
tree wce = *wce_p;
if (TREE_CODE (wce) == WITH_CLEANUP_EXPR)
{
if (tsi_one_before_end_p (iter))
{
tsi_link_before (&iter, TREE_OPERAND (wce, 1), TSI_SAME_STMT);
tsi_delink (&iter);
break;
}
else
{
tree sl, tfe;
sl = tsi_split_statement_list_after (&iter);
tfe = build (TRY_FINALLY_EXPR, void_type_node, sl, NULL_TREE);
append_to_statement_list (TREE_OPERAND (wce, 1),
&TREE_OPERAND (tfe, 1));
*wce_p = tfe;
iter = tsi_start (sl);
}
}
else
tsi_next (&iter);
}
if (temp)
{
*expr_p = temp;
append_to_statement_list (body, pre_p);
return GS_OK;
}
else
{
*expr_p = body;
return GS_ALL_DONE;
}
}
static void
gimple_push_cleanup (tree var, tree cleanup, tree *pre_p)
{
tree wce;
if (errorcount || sorrycount)
return;
if (gimple_conditional_context ())
{
tree flag = create_tmp_var (boolean_type_node, "cleanup");
tree ffalse = build (MODIFY_EXPR, void_type_node, flag,
boolean_false_node);
tree ftrue = build (MODIFY_EXPR, void_type_node, flag,
boolean_true_node);
cleanup = build (COND_EXPR, void_type_node, flag, cleanup,
build_empty_stmt ());
wce = build (WITH_CLEANUP_EXPR, void_type_node, NULL_TREE,
cleanup, NULL_TREE);
append_to_statement_list (ffalse, &gimplify_ctxp->conditional_cleanups);
append_to_statement_list (wce, &gimplify_ctxp->conditional_cleanups);
append_to_statement_list (ftrue, pre_p);
TREE_NO_WARNING (var) = 1;
}
else
{
wce = build (WITH_CLEANUP_EXPR, void_type_node, NULL_TREE,
cleanup, NULL_TREE);
append_to_statement_list (wce, pre_p);
}
gimplify_stmt (&TREE_OPERAND (wce, 1));
}
static enum gimplify_status
gimplify_target_expr (tree *expr_p, tree *pre_p, tree *post_p)
{
tree targ = *expr_p;
tree temp = TARGET_EXPR_SLOT (targ);
tree init = TARGET_EXPR_INITIAL (targ);
enum gimplify_status ret;
if (init)
{
gimple_add_tmp_var (temp);
init = build (MODIFY_EXPR, void_type_node, temp, init);
ret = gimplify_expr (&init, pre_p, post_p, is_gimple_stmt, fb_none);
if (ret == GS_ERROR)
return GS_ERROR;
append_to_statement_list (init, pre_p);
if (TARGET_EXPR_CLEANUP (targ))
{
gimplify_stmt (&TARGET_EXPR_CLEANUP (targ));
gimple_push_cleanup (temp, TARGET_EXPR_CLEANUP (targ), pre_p);
}
TREE_OPERAND (targ, 3) = init;
TARGET_EXPR_INITIAL (targ) = NULL_TREE;
}
else if (!temp->decl.seen_in_bind_expr)
abort ();
*expr_p = temp;
return GS_OK;
}
void
gimplify_stmt (tree *stmt_p)
{
gimplify_expr (stmt_p, NULL, NULL, is_gimple_stmt, fb_none);
if (!*stmt_p)
*stmt_p = alloc_stmt_list ();
}
void
gimplify_to_stmt_list (tree *stmt_p)
{
gimplify_stmt (stmt_p);
if (TREE_CODE (*stmt_p) != STATEMENT_LIST)
{
tree t = *stmt_p;
*stmt_p = NULL;
append_to_statement_list (t, stmt_p);
}
}
enum gimplify_status
gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p,
bool (* gimple_test_f) (tree), fallback_t fallback)
{
tree tmp;
tree internal_pre = NULL_TREE;
tree internal_post = NULL_TREE;
tree save_expr;
int is_statement = (pre_p == NULL);
location_t *locus;
location_t saved_location;
enum gimplify_status ret;
save_expr = *expr_p;
if (save_expr == NULL_TREE)
return GS_ALL_DONE;
if (pre_p == NULL)
pre_p = &internal_pre;
if (post_p == NULL)
post_p = &internal_post;
saved_location = input_location;
if (save_expr == error_mark_node)
locus = NULL;
else
locus = EXPR_LOCUS (save_expr);
if (locus)
input_location = *locus;
do
{
STRIP_MAIN_TYPE_NOPS (*expr_p);
save_expr = *expr_p;
if (save_expr == error_mark_node
|| TREE_TYPE (save_expr) == error_mark_node)
{
ret = GS_ERROR;
break;
}
ret = lang_hooks.gimplify_expr (expr_p, pre_p, post_p);
if (ret == GS_OK)
{
if (*expr_p == NULL_TREE)
break;
if (*expr_p != save_expr)
continue;
}
else if (ret != GS_UNHANDLED)
break;
ret = GS_OK;
switch (TREE_CODE (*expr_p))
{
case POSTINCREMENT_EXPR:
case POSTDECREMENT_EXPR:
case PREINCREMENT_EXPR:
case PREDECREMENT_EXPR:
ret = gimplify_self_mod_expr (expr_p, pre_p, post_p,
fallback != fb_none);
break;
case ARRAY_REF:
ret = gimplify_array_ref (expr_p, pre_p, post_p,
fallback & fb_lvalue);
break;
case COMPONENT_REF:
ret = gimplify_compound_lval (expr_p, pre_p, post_p,
fallback & fb_lvalue);
break;
case COND_EXPR:
ret = gimplify_cond_expr (expr_p, pre_p, NULL_TREE);
break;
case CALL_EXPR:
ret = gimplify_call_expr (expr_p, pre_p, gimple_test_f);
break;
case TREE_LIST:
abort ();
case COMPOUND_EXPR:
ret = gimplify_compound_expr (expr_p, pre_p, fallback != fb_none);
break;
case REALPART_EXPR:
case IMAGPART_EXPR:
ret = gimplify_compound_lval (expr_p, pre_p, post_p,
fallback & fb_lvalue);
break;
case MODIFY_EXPR:
case INIT_EXPR:
ret = gimplify_modify_expr (expr_p, pre_p, post_p,
fallback != fb_none);
break;
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
ret = gimplify_boolean_expr (expr_p);
break;
case TRUTH_NOT_EXPR:
TREE_OPERAND (*expr_p, 0)
= gimple_boolify (TREE_OPERAND (*expr_p, 0));
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_val, fb_rvalue);
recalculate_side_effects (*expr_p);
break;
case ADDR_EXPR:
ret = gimplify_addr_expr (expr_p, pre_p, post_p);
break;
case VA_ARG_EXPR:
walk_tree (&TREE_OPERAND (*expr_p, 0), mark_decls_volatile_r,
NULL, NULL);
ret = GS_ALL_DONE;
break;
case CONVERT_EXPR:
case NOP_EXPR:
if (IS_EMPTY_STMT (*expr_p))
{
ret = GS_ALL_DONE;
break;
}
if (VOID_TYPE_P (TREE_TYPE (*expr_p))
|| fallback == fb_none)
{
*expr_p = TREE_OPERAND (*expr_p, 0);
break;
}
ret = gimplify_conversion (expr_p);
if (ret == GS_ERROR)
break;
if (*expr_p != save_expr)
break;
case FIX_TRUNC_EXPR:
case FIX_CEIL_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_val, fb_rvalue);
recalculate_side_effects (*expr_p);
break;
case INDIRECT_REF:
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_reg, fb_rvalue);
recalculate_side_effects (*expr_p);
break;
case INTEGER_CST:
case REAL_CST:
case STRING_CST:
case COMPLEX_CST:
case VECTOR_CST:
ret = GS_ALL_DONE;
break;
case CONST_DECL:
*expr_p = DECL_INITIAL (*expr_p);
break;
case EXC_PTR_EXPR:
ret = GS_ALL_DONE;
break;
case BIND_EXPR:
ret = gimplify_bind_expr (expr_p, pre_p);
break;
case LOOP_EXPR:
ret = gimplify_loop_expr (expr_p, pre_p);
break;
case SWITCH_EXPR:
ret = gimplify_switch_expr (expr_p, pre_p);
break;
case LABELED_BLOCK_EXPR:
ret = gimplify_labeled_block_expr (expr_p);
break;
case EXIT_BLOCK_EXPR:
ret = gimplify_exit_block_expr (expr_p);
break;
case EXIT_EXPR:
ret = gimplify_exit_expr (expr_p);
break;
case GOTO_EXPR:
if (TREE_CODE (GOTO_DESTINATION (*expr_p)) != LABEL_DECL)
ret = gimplify_expr (&GOTO_DESTINATION (*expr_p), pre_p,
NULL, is_gimple_val, fb_rvalue);
break;
case LABEL_EXPR:
ret = GS_ALL_DONE;
#ifdef ENABLE_CHECKING
if (decl_function_context (LABEL_EXPR_LABEL (*expr_p)) != current_function_decl)
abort ();
#endif
break;
case CASE_LABEL_EXPR:
ret = gimplify_case_label_expr (expr_p);
break;
case RETURN_EXPR:
ret = gimplify_return_expr (*expr_p, pre_p);
break;
case CONSTRUCTOR:
ret = GS_ALL_DONE;
break;
case SAVE_EXPR:
ret = gimplify_save_expr (expr_p, pre_p, post_p);
break;
case BIT_FIELD_REF:
{
enum gimplify_status r0, r1, r2;
r0 = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_min_lval, fb_either);
r1 = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
is_gimple_val, fb_rvalue);
r2 = gimplify_expr (&TREE_OPERAND (*expr_p, 2), pre_p, post_p,
is_gimple_val, fb_rvalue);
recalculate_side_effects (*expr_p);
ret = MIN (r0, MIN (r1, r2));
}
break;
case NON_LVALUE_EXPR:
abort ();
break;
case ASM_EXPR:
ret = gimplify_asm_expr (expr_p, pre_p, post_p);
break;
case TRY_FINALLY_EXPR:
case TRY_CATCH_EXPR:
gimplify_to_stmt_list (&TREE_OPERAND (*expr_p, 0));
gimplify_to_stmt_list (&TREE_OPERAND (*expr_p, 1));
ret = GS_ALL_DONE;
break;
case CLEANUP_POINT_EXPR:
ret = gimplify_cleanup_point_expr (expr_p, pre_p);
break;
case TARGET_EXPR:
ret = gimplify_target_expr (expr_p, pre_p, post_p);
break;
case CATCH_EXPR:
gimplify_to_stmt_list (&CATCH_BODY (*expr_p));
ret = GS_ALL_DONE;
break;
case EH_FILTER_EXPR:
gimplify_to_stmt_list (&EH_FILTER_FAILURE (*expr_p));
ret = GS_ALL_DONE;
break;
case VTABLE_REF:
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_min_lval, fb_lvalue);
break;
case MIN_EXPR:
case MAX_EXPR:
ret = gimplify_minimax_expr (expr_p, pre_p, post_p);
break;
case LABEL_DECL:
FORCED_LABEL (*expr_p) = 1;
ret = GS_ALL_DONE;
break;
case STATEMENT_LIST:
ret = gimplify_statement_list (expr_p);
break;
case VAR_DECL:
tmp = *expr_p;
if (!TREE_STATIC (tmp) && !DECL_EXTERNAL (tmp)
&& decl_function_context (tmp) == current_function_decl
&& !tmp->decl.seen_in_bind_expr)
{
#ifdef ENABLE_CHECKING
if (!errorcount && !sorrycount)
abort ();
#endif
ret = GS_ERROR;
}
else
ret = GS_ALL_DONE;
break;
default:
if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '1')
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
post_p, is_gimple_val, fb_rvalue);
else if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '2'
|| TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '<'
|| TREE_CODE (*expr_p) == TRUTH_AND_EXPR
|| TREE_CODE (*expr_p) == TRUTH_OR_EXPR
|| TREE_CODE (*expr_p) == TRUTH_XOR_EXPR)
{
enum gimplify_status r0, r1;
r0 = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
post_p, is_gimple_val, fb_rvalue);
r1 = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p,
post_p, is_gimple_val, fb_rvalue);
ret = MIN (r0, r1);
}
else if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == 'd'
|| TREE_CODE_CLASS (TREE_CODE (*expr_p)) == 'c')
{
ret = GS_ALL_DONE;
break;
}
else
abort ();
recalculate_side_effects (*expr_p);
break;
}
if (ret == GS_OK && (*expr_p == NULL || *expr_p == save_expr))
ret = GS_ALL_DONE;
}
while (ret == GS_OK);
if (ret == GS_ERROR)
{
if (is_statement)
*expr_p = build_empty_stmt ();
goto out;
}
#ifdef ENABLE_CHECKING
if (ret == GS_UNHANDLED)
abort ();
#endif
if (!*expr_p)
*expr_p = build_empty_stmt ();
if (fallback == fb_none && !is_gimple_stmt (*expr_p))
{
if (!TREE_SIDE_EFFECTS (*expr_p))
*expr_p = build_empty_stmt ();
else if (!TREE_THIS_VOLATILE (*expr_p))
abort ();
else if (COMPLETE_TYPE_P (TREE_TYPE (*expr_p)))
{
tree tmp = create_tmp_var (TREE_TYPE (*expr_p), "vol");
*expr_p = build (MODIFY_EXPR, TREE_TYPE (tmp), tmp, *expr_p);
}
else
*expr_p = build_empty_stmt ();
}
if (is_statement)
{
append_to_statement_list (*expr_p, &internal_pre);
append_to_statement_list (internal_post, &internal_pre);
annotate_all_with_locus (&internal_pre, input_location);
*expr_p = internal_pre;
goto out;
}
if (!internal_post && (*gimple_test_f) (*expr_p))
goto out;
if ((fallback & fb_lvalue) && !internal_post
&& is_gimple_addr_expr_arg (*expr_p))
{
tmp = build_addr_expr (*expr_p);
gimplify_expr (&tmp, pre_p, post_p, is_gimple_reg, fb_rvalue);
*expr_p = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (tmp)), tmp);
}
else if ((fallback & fb_rvalue) && is_gimple_rhs (*expr_p))
{
#if defined ENABLE_CHECKING
if (VOID_TYPE_P (TREE_TYPE (*expr_p)))
abort ();
#endif
if (internal_post || (fallback & fb_lvalue))
*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
else
*expr_p = get_formal_tmp_var (*expr_p, pre_p);
}
else if (fallback & fb_mayfail)
{
ret = GS_ERROR;
goto out;
}
else
{
fprintf (stderr, "gimplification failed:\n");
print_generic_expr (stderr, *expr_p, 0);
debug_tree (*expr_p);
abort ();
}
#if defined ENABLE_CHECKING
if (!(*gimple_test_f) (*expr_p))
abort ();
#endif
if (internal_post)
{
annotate_all_with_locus (&internal_post, input_location);
append_to_statement_list (internal_post, pre_p);
}
out:
input_location = saved_location;
return ret;
}
#ifdef ENABLE_CHECKING
static bool
cpt_same_type (tree a, tree b)
{
if (lang_hooks.types_compatible_p (a, b))
return true;
if ((TREE_CODE (a) == FUNCTION_TYPE || TREE_CODE (a) == METHOD_TYPE)
&& (TREE_CODE (b) == FUNCTION_TYPE || TREE_CODE (b) == METHOD_TYPE))
return true;
if (TREE_CODE (a) == ARRAY_TYPE && TREE_CODE (b) == ARRAY_TYPE)
return cpt_same_type (TREE_TYPE (a), TREE_TYPE (b));
if (POINTER_TYPE_P (a) && POINTER_TYPE_P (b))
return cpt_same_type (TREE_TYPE (a), TREE_TYPE (b));
return false;
}
static tree
check_pointer_types_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
tree t = *tp;
tree ptype, otype, dtype;
switch (TREE_CODE (t))
{
case INDIRECT_REF:
case ARRAY_REF:
otype = TREE_TYPE (t);
ptype = TREE_TYPE (TREE_OPERAND (t, 0));
dtype = TREE_TYPE (ptype);
if (!cpt_same_type (otype, dtype))
abort ();
break;
case ADDR_EXPR:
ptype = TREE_TYPE (t);
otype = TREE_TYPE (TREE_OPERAND (t, 0));
dtype = TREE_TYPE (ptype);
if (!cpt_same_type (otype, dtype))
{
if (TREE_CODE (otype) == ARRAY_TYPE
&& POINTER_TYPE_P (ptype)
&& cpt_same_type (TREE_TYPE (otype), dtype))
break;
abort ();
}
break;
default:
return NULL_TREE;
}
return NULL_TREE;
}
#endif
void
gimplify_body (tree *body_p, tree fndecl)
{
location_t saved_location = input_location;
tree body;
timevar_push (TV_TREE_GIMPLIFY);
push_gimplify_context ();
unshare_all_trees (*body_p);
input_location = DECL_SOURCE_LOCATION (fndecl);
gimplify_stmt (body_p);
body = *body_p;
unshare_all_trees (body);
if (TREE_CODE (body) == STATEMENT_LIST)
{
tree t = expr_only (*body_p);
if (t)
body = t;
}
if (TREE_CODE (body) != BIND_EXPR)
{
tree b = build (BIND_EXPR, void_type_node, NULL_TREE,
NULL_TREE, NULL_TREE);
TREE_SIDE_EFFECTS (b) = 1;
append_to_statement_list (body, &BIND_EXPR_BODY (b));
body = b;
}
*body_p = body;
pop_gimplify_context (body);
#ifdef ENABLE_CHECKING
walk_tree (body_p, check_pointer_types_r, NULL, NULL);
#endif
timevar_pop (TV_TREE_GIMPLIFY);
input_location = saved_location;
}
void
gimplify_function_tree (tree fndecl)
{
tree oldfn;
oldfn = current_function_decl;
current_function_decl = fndecl;
gimplify_body (&DECL_SAVED_TREE (fndecl), fndecl);
if (flag_instrument_function_entry_exit
&& ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl))
{
tree tf, x, bind;
tf = build (TRY_FINALLY_EXPR, void_type_node, NULL, NULL);
TREE_SIDE_EFFECTS (tf) = 1;
x = DECL_SAVED_TREE (fndecl);
append_to_statement_list (x, &TREE_OPERAND (tf, 0));
x = implicit_built_in_decls[BUILT_IN_PROFILE_FUNC_EXIT];
x = build_function_call_expr (x, NULL);
append_to_statement_list (x, &TREE_OPERAND (tf, 1));
bind = build (BIND_EXPR, void_type_node, NULL, NULL, NULL);
TREE_SIDE_EFFECTS (bind) = 1;
x = implicit_built_in_decls[BUILT_IN_PROFILE_FUNC_ENTER];
x = build_function_call_expr (x, NULL);
append_to_statement_list (x, &BIND_EXPR_BODY (bind));
append_to_statement_list (tf, &BIND_EXPR_BODY (bind));
DECL_SAVED_TREE (fndecl) = bind;
}
current_function_decl = oldfn;
}
#include "gt-gimplify.h"