+#include "tree-inline.h"
+#include "value-prof.h"
+#include "target.h"
+
+
+/* Return an expression tree corresponding to the RHS of GIMPLE
+ statement STMT. */
+
+tree
+gimple_assign_rhs_to_tree (gimple stmt)
+{
+ tree t;
+ enum gimple_rhs_class grhs_class;
+
+ grhs_class = get_gimple_rhs_class (gimple_expr_code (stmt));
+
+ if (grhs_class == GIMPLE_BINARY_RHS)
+ t = build2 (gimple_assign_rhs_code (stmt),
+ TREE_TYPE (gimple_assign_lhs (stmt)),
+ gimple_assign_rhs1 (stmt),
+ gimple_assign_rhs2 (stmt));
+ else if (grhs_class == GIMPLE_UNARY_RHS)
+ t = build1 (gimple_assign_rhs_code (stmt),
+ TREE_TYPE (gimple_assign_lhs (stmt)),
+ gimple_assign_rhs1 (stmt));
+ else if (grhs_class == GIMPLE_SINGLE_RHS)
+ t = gimple_assign_rhs1 (stmt);
+ else
+ gcc_unreachable ();
+
+ return t;
+}
+
+/* Return an expression tree corresponding to the PREDICATE of GIMPLE_COND
+ statement STMT. */
+
+static tree
+gimple_cond_pred_to_tree (gimple stmt)
+{
+ return build2 (gimple_cond_code (stmt), boolean_type_node,
+ gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
+}
+
+/* Helper for gimple_to_tree. Set EXPR_LOCATION for every expression
+ inside *TP. DATA is the location to set. */
+
+static tree
+set_expr_location_r (tree *tp, int *ws ATTRIBUTE_UNUSED, void *data)
+{
+ location_t *loc = (location_t *) data;
+ if (EXPR_P (*tp))
+ SET_EXPR_LOCATION (*tp, *loc);
+
+ return NULL_TREE;
+}
+
+
+/* RTL expansion has traditionally been done on trees, so the
+ transition to doing it on GIMPLE tuples is very invasive to the RTL
+ expander. To facilitate the transition, this function takes a
+ GIMPLE tuple STMT and returns the same statement in the form of a
+ tree. */
+
+static tree
+gimple_to_tree (gimple stmt)
+{
+ tree t;
+ int rn;
+ tree_ann_common_t ann;
+ location_t loc;
+
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_ASSIGN:
+ {
+ tree lhs = gimple_assign_lhs (stmt);
+
+ t = gimple_assign_rhs_to_tree (stmt);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, t);
+ if (gimple_assign_nontemporal_move_p (stmt))
+ MOVE_NONTEMPORAL (t) = true;
+ }
+ break;
+
+ case GIMPLE_COND:
+ t = gimple_cond_pred_to_tree (stmt);
+ t = build3 (COND_EXPR, void_type_node, t, NULL_TREE, NULL_TREE);
+ break;
+
+ case GIMPLE_GOTO:
+ t = build1 (GOTO_EXPR, void_type_node, gimple_goto_dest (stmt));
+ break;
+
+ case GIMPLE_LABEL:
+ t = build1 (LABEL_EXPR, void_type_node, gimple_label_label (stmt));
+ break;
+
+ case GIMPLE_RETURN:
+ {
+ tree retval = gimple_return_retval (stmt);
+
+ if (retval && retval != error_mark_node)
+ {
+ tree result = DECL_RESULT (current_function_decl);
+
+ /* If we are not returning the current function's RESULT_DECL,
+ build an assignment to it. */
+ if (retval != result)
+ {
+ /* I believe that a function's RESULT_DECL is unique. */
+ gcc_assert (TREE_CODE (retval) != RESULT_DECL);
+
+ retval = build2 (MODIFY_EXPR, TREE_TYPE (result),
+ result, retval);
+ }
+ }
+ t = build1 (RETURN_EXPR, void_type_node, retval);
+ }
+ break;
+
+ case GIMPLE_ASM:
+ {
+ size_t i, n;
+ tree out, in, cl;
+ const char *s;
+
+ out = NULL_TREE;
+ n = gimple_asm_noutputs (stmt);
+ if (n > 0)
+ {
+ t = out = gimple_asm_output_op (stmt, 0);
+ for (i = 1; i < n; i++)
+ {
+ TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
+ t = gimple_asm_output_op (stmt, i);
+ }
+ }
+
+ in = NULL_TREE;
+ n = gimple_asm_ninputs (stmt);
+ if (n > 0)
+ {
+ t = in = gimple_asm_input_op (stmt, 0);
+ for (i = 1; i < n; i++)
+ {
+ TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
+ t = gimple_asm_input_op (stmt, i);
+ }
+ }
+
+ cl = NULL_TREE;
+ n = gimple_asm_nclobbers (stmt);
+ if (n > 0)
+ {
+ t = cl = gimple_asm_clobber_op (stmt, 0);
+ for (i = 1; i < n; i++)
+ {
+ TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
+ t = gimple_asm_clobber_op (stmt, i);
+ }
+ }
+
+ s = gimple_asm_string (stmt);
+ t = build4 (ASM_EXPR, void_type_node, build_string (strlen (s), s),
+ out, in, cl);
+ ASM_VOLATILE_P (t) = gimple_asm_volatile_p (stmt);
+ ASM_INPUT_P (t) = gimple_asm_input_p (stmt);
+ }
+ break;
+
+ case GIMPLE_CALL:
+ {
+ size_t i;
+ tree fn;
+ tree_ann_common_t ann;
+
+ t = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3);
+
+ CALL_EXPR_FN (t) = gimple_call_fn (stmt);
+ TREE_TYPE (t) = gimple_call_return_type (stmt);
+ CALL_EXPR_STATIC_CHAIN (t) = gimple_call_chain (stmt);
+
+ for (i = 0; i < gimple_call_num_args (stmt); i++)
+ CALL_EXPR_ARG (t, i) = gimple_call_arg (stmt, i);
+
+ if (!(gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE)))
+ TREE_SIDE_EFFECTS (t) = 1;
+
+ if (gimple_call_flags (stmt) & ECF_NOTHROW)
+ TREE_NOTHROW (t) = 1;
+
+ CALL_EXPR_TAILCALL (t) = gimple_call_tail_p (stmt);
+ CALL_EXPR_RETURN_SLOT_OPT (t) = gimple_call_return_slot_opt_p (stmt);
+ CALL_FROM_THUNK_P (t) = gimple_call_from_thunk_p (stmt);
+ CALL_CANNOT_INLINE_P (t) = gimple_call_cannot_inline_p (stmt);
+ CALL_EXPR_VA_ARG_PACK (t) = gimple_call_va_arg_pack_p (stmt);
+
+ /* If the call has a LHS then create a MODIFY_EXPR to hold it. */
+ {
+ tree lhs = gimple_call_lhs (stmt);
+
+ if (lhs)
+ t = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, t);
+ }
+
+ /* Record the original call statement, as it may be used
+ to retrieve profile information during expansion. */
+
+ if ((fn = gimple_call_fndecl (stmt)) != NULL_TREE
+ && DECL_BUILT_IN (fn))
+ {
+ ann = get_tree_common_ann (t);
+ ann->stmt = stmt;
+ }
+ }
+ break;
+
+ case GIMPLE_SWITCH:
+ {
+ tree label_vec;
+ size_t i;
+ tree elt = gimple_switch_label (stmt, 0);
+
+ label_vec = make_tree_vec (gimple_switch_num_labels (stmt));
+
+ if (!CASE_LOW (elt) && !CASE_HIGH (elt))
+ {
+ for (i = 1; i < gimple_switch_num_labels (stmt); i++)
+ TREE_VEC_ELT (label_vec, i - 1) = gimple_switch_label (stmt, i);
+
+ /* The default case in a SWITCH_EXPR must be at the end of
+ the label vector. */
+ TREE_VEC_ELT (label_vec, i - 1) = gimple_switch_label (stmt, 0);
+ }
+ else
+ {
+ for (i = 0; i < gimple_switch_num_labels (stmt); i++)
+ TREE_VEC_ELT (label_vec, i) = gimple_switch_label (stmt, i);
+ }
+
+ t = build3 (SWITCH_EXPR, void_type_node, gimple_switch_index (stmt),
+ NULL, label_vec);
+ }
+ break;
+
+ case GIMPLE_NOP:
+ case GIMPLE_PREDICT:
+ t = build1 (NOP_EXPR, void_type_node, size_zero_node);
+ break;
+
+ case GIMPLE_RESX:
+ t = build_resx (gimple_resx_region (stmt));
+ break;
+
+ default:
+ if (errorcount == 0)
+ {
+ error ("Unrecognized GIMPLE statement during RTL expansion");
+ print_gimple_stmt (stderr, stmt, 4, 0);
+ gcc_unreachable ();
+ }
+ else
+ {
+ /* Ignore any bad gimple codes if we're going to die anyhow,
+ so we can at least set TREE_ASM_WRITTEN and have the rest
+ of compilation advance without sudden ICE death. */
+ t = build1 (NOP_EXPR, void_type_node, size_zero_node);
+ break;
+ }
+ }
+
+ /* If STMT is inside an exception region, record it in the generated
+ expression. */
+ rn = lookup_stmt_eh_region (stmt);
+ if (rn >= 0)
+ {
+ tree call = get_call_expr_in (t);
+
+ ann = get_tree_common_ann (t);
+ ann->rn = rn;
+
+ /* For a CALL_EXPR on the RHS of an assignment, calls.c looks up
+ the CALL_EXPR not the assignment statment for EH region number. */
+ if (call && call != t)
+ {
+ ann = get_tree_common_ann (call);
+ ann->rn = rn;
+ }
+ }
+
+ /* Set EXPR_LOCATION in all the embedded expressions. */
+ loc = gimple_location (stmt);
+ walk_tree (&t, set_expr_location_r, (void *) &loc, NULL);
+
+ TREE_BLOCK (t) = gimple_block (stmt);
+
+ return t;
+}
+
+
+/* Release back to GC memory allocated by gimple_to_tree. */
+
+static void
+release_stmt_tree (gimple stmt, tree stmt_tree)
+{
+ tree_ann_common_t ann;
+
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_ASSIGN:
+ if (get_gimple_rhs_class (gimple_expr_code (stmt)) != GIMPLE_SINGLE_RHS)
+ ggc_free (TREE_OPERAND (stmt_tree, 1));
+ break;
+ case GIMPLE_COND:
+ ggc_free (COND_EXPR_COND (stmt_tree));
+ break;
+ case GIMPLE_RETURN:
+ if (TREE_OPERAND (stmt_tree, 0)
+ && TREE_CODE (TREE_OPERAND (stmt_tree, 0)) == MODIFY_EXPR)
+ ggc_free (TREE_OPERAND (stmt_tree, 0));
+ break;
+ case GIMPLE_CALL:
+ if (gimple_call_lhs (stmt))
+ {
+ ann = tree_common_ann (TREE_OPERAND (stmt_tree, 1));
+ if (ann)
+ ggc_free (ann);
+ ggc_free (TREE_OPERAND (stmt_tree, 1));
+ }
+ break;
+ default:
+ break;
+ }
+ ann = tree_common_ann (stmt_tree);
+ if (ann)
+ ggc_free (ann);
+ ggc_free (stmt_tree);
+}
+