diff options
Diffstat (limited to 'gcc/gimplify-me.cc')
-rw-r--r-- | gcc/gimplify-me.cc | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/gcc/gimplify-me.cc b/gcc/gimplify-me.cc new file mode 100644 index 0000000..24f6248 --- /dev/null +++ b/gcc/gimplify-me.cc @@ -0,0 +1,318 @@ +/* Tree lowering to gimple for middle end use only. + This converts the GENERIC functions-as-trees tree representation into + the GIMPLE form. + Copyright (C) 2013-2022 Free Software Foundation, Inc. + Major work done by Sebastian Pop <s.pop@laposte.net>, + Diego Novillo <dnovillo@redhat.com> and Jason Merrill <jason@redhat.com>. + +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 +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "ssa.h" +#include "stmt.h" +#include "stor-layout.h" +#include "tree-eh.h" +#include "gimple-iterator.h" +#include "gimplify.h" +#include "gimplify-me.h" + + +/* Expand EXPR to list of gimple statements STMTS. GIMPLE_TEST_F specifies + the predicate that will hold for the result. If VAR is not NULL, make the + base variable of the final destination be VAR if suitable. */ + +tree +force_gimple_operand_1 (tree expr, gimple_seq *stmts, + gimple_predicate gimple_test_f, tree var) +{ + enum gimplify_status ret; + location_t saved_location; + + *stmts = NULL; + + /* gimple_test_f might be more strict than is_gimple_val, make + sure we pass both. Just checking gimple_test_f doesn't work + because most gimple predicates do not work recursively. */ + if (is_gimple_val (expr) + && (*gimple_test_f) (expr)) + return expr; + + push_gimplify_context (gimple_in_ssa_p (cfun), true); + saved_location = input_location; + input_location = UNKNOWN_LOCATION; + + if (var) + { + if (gimple_in_ssa_p (cfun) && is_gimple_reg (var)) + var = make_ssa_name (var); + expr = build2 (MODIFY_EXPR, TREE_TYPE (var), var, expr); + } + + if (TREE_CODE (expr) != MODIFY_EXPR + && TREE_TYPE (expr) == void_type_node) + { + gimplify_and_add (expr, stmts); + expr = NULL_TREE; + } + else + { + ret = gimplify_expr (&expr, stmts, NULL, gimple_test_f, fb_rvalue); + gcc_assert (ret != GS_ERROR); + } + + input_location = saved_location; + pop_gimplify_context (NULL); + + return expr; +} + +/* Expand EXPR to list of gimple statements STMTS. If SIMPLE is true, + force the result to be either ssa_name or an invariant, otherwise + just force it to be a rhs expression. If VAR is not NULL, make the + base variable of the final destination be VAR if suitable. */ + +tree +force_gimple_operand (tree expr, gimple_seq *stmts, bool simple, tree var) +{ + return force_gimple_operand_1 (expr, stmts, + simple ? is_gimple_val : is_gimple_reg_rhs, + var); +} + +/* Invoke force_gimple_operand_1 for EXPR with parameters GIMPLE_TEST_F + and VAR. If some statements are produced, emits them at GSI. + If BEFORE is true. the statements are appended before GSI, otherwise + they are appended after it. M specifies the way GSI moves after + insertion (GSI_SAME_STMT or GSI_CONTINUE_LINKING are the usual values). */ + +tree +force_gimple_operand_gsi_1 (gimple_stmt_iterator *gsi, tree expr, + gimple_predicate gimple_test_f, + tree var, bool before, + enum gsi_iterator_update m) +{ + gimple_seq stmts; + + expr = force_gimple_operand_1 (expr, &stmts, gimple_test_f, var); + + if (!gimple_seq_empty_p (stmts)) + { + if (before) + gsi_insert_seq_before (gsi, stmts, m); + else + gsi_insert_seq_after (gsi, stmts, m); + } + + return expr; +} + +/* Invoke force_gimple_operand_1 for EXPR with parameter VAR. + If SIMPLE is true, force the result to be either ssa_name or an invariant, + otherwise just force it to be a rhs expression. If some statements are + produced, emits them at GSI. If BEFORE is true, the statements are + appended before GSI, otherwise they are appended after it. M specifies + the way GSI moves after insertion (GSI_SAME_STMT or GSI_CONTINUE_LINKING + are the usual values). */ + +tree +force_gimple_operand_gsi (gimple_stmt_iterator *gsi, tree expr, + bool simple_p, tree var, bool before, + enum gsi_iterator_update m) +{ + return force_gimple_operand_gsi_1 (gsi, expr, + simple_p + ? is_gimple_val : is_gimple_reg_rhs, + var, before, m); +} + +/* Some transformations like inlining may invalidate the GIMPLE form + for operands. This function traverses all the operands in STMT and + gimplifies anything that is not a valid gimple operand. Any new + GIMPLE statements are inserted before *GSI_P. */ + +void +gimple_regimplify_operands (gimple *stmt, gimple_stmt_iterator *gsi_p) +{ + size_t i, num_ops; + tree lhs; + gimple_seq pre = NULL; + gimple *post_stmt = NULL; + + push_gimplify_context (gimple_in_ssa_p (cfun)); + + switch (gimple_code (stmt)) + { + case GIMPLE_COND: + { + gcond *cond_stmt = as_a <gcond *> (stmt); + gimplify_expr (gimple_cond_lhs_ptr (cond_stmt), &pre, NULL, + is_gimple_val, fb_rvalue); + gimplify_expr (gimple_cond_rhs_ptr (cond_stmt), &pre, NULL, + is_gimple_val, fb_rvalue); + } + break; + case GIMPLE_SWITCH: + gimplify_expr (gimple_switch_index_ptr (as_a <gswitch *> (stmt)), + &pre, NULL, is_gimple_val, fb_rvalue); + break; + case GIMPLE_OMP_ATOMIC_LOAD: + gimplify_expr (gimple_omp_atomic_load_rhs_ptr ( + as_a <gomp_atomic_load *> (stmt)), + &pre, NULL, is_gimple_val, fb_rvalue); + break; + case GIMPLE_ASM: + { + gasm *asm_stmt = as_a <gasm *> (stmt); + size_t i, noutputs = gimple_asm_noutputs (asm_stmt); + const char *constraint, **oconstraints; + bool allows_mem, allows_reg, is_inout; + + oconstraints + = (const char **) alloca ((noutputs) * sizeof (const char *)); + for (i = 0; i < noutputs; i++) + { + tree op = gimple_asm_output_op (asm_stmt, i); + constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op))); + oconstraints[i] = constraint; + parse_output_constraint (&constraint, i, 0, 0, &allows_mem, + &allows_reg, &is_inout); + gimplify_expr (&TREE_VALUE (op), &pre, NULL, + is_inout ? is_gimple_min_lval : is_gimple_lvalue, + fb_lvalue | fb_mayfail); + } + for (i = 0; i < gimple_asm_ninputs (asm_stmt); i++) + { + tree op = gimple_asm_input_op (asm_stmt, i); + constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op))); + parse_input_constraint (&constraint, 0, 0, noutputs, 0, + oconstraints, &allows_mem, &allows_reg); + if (TREE_ADDRESSABLE (TREE_TYPE (TREE_VALUE (op))) && allows_mem) + allows_reg = 0; + if (!allows_reg && allows_mem) + gimplify_expr (&TREE_VALUE (op), &pre, NULL, + is_gimple_lvalue, fb_lvalue | fb_mayfail); + else + gimplify_expr (&TREE_VALUE (op), &pre, NULL, + is_gimple_asm_val, fb_rvalue); + } + } + break; + default: + /* NOTE: We start gimplifying operands from last to first to + make sure that side-effects on the RHS of calls, assignments + and ASMs are executed before the LHS. The ordering is not + important for other statements. */ + num_ops = gimple_num_ops (stmt); + for (i = num_ops; i > 0; i--) + { + tree op = gimple_op (stmt, i - 1); + if (op == NULL_TREE) + continue; + if (i == 1 && (is_gimple_call (stmt) || is_gimple_assign (stmt))) + gimplify_expr (&op, &pre, NULL, is_gimple_lvalue, fb_lvalue); + else if (i == 2 + && gimple_assign_single_p (stmt) + && num_ops == 2) + gimplify_expr (&op, &pre, NULL, + rhs_predicate_for (gimple_assign_lhs (stmt)), + fb_rvalue); + else if (i == 2 && is_gimple_call (stmt)) + { + if (TREE_CODE (op) == FUNCTION_DECL) + continue; + gimplify_expr (&op, &pre, NULL, is_gimple_call_addr, fb_rvalue); + } + else + gimplify_expr (&op, &pre, NULL, is_gimple_val, fb_rvalue); + gimple_set_op (stmt, i - 1, op); + } + + lhs = gimple_get_lhs (stmt); + /* If the LHS changed it in a way that requires a simple RHS, + create temporary. */ + if (lhs && !is_gimple_reg (lhs)) + { + bool need_temp = false; + + if (gimple_assign_single_p (stmt) + && num_ops == 2) + gimplify_expr (gimple_assign_rhs1_ptr (stmt), &pre, NULL, + rhs_predicate_for (gimple_assign_lhs (stmt)), + fb_rvalue); + else if (is_gimple_reg (lhs)) + { + if (is_gimple_reg_type (TREE_TYPE (lhs))) + { + if (is_gimple_call (stmt)) + { + i = gimple_call_flags (stmt); + if ((i & ECF_LOOPING_CONST_OR_PURE) + || !(i & (ECF_CONST | ECF_PURE))) + need_temp = true; + } + if (stmt_can_throw_internal (cfun, stmt)) + need_temp = true; + } + } + else + { + if (is_gimple_reg_type (TREE_TYPE (lhs))) + need_temp = true; + else if (TYPE_MODE (TREE_TYPE (lhs)) != BLKmode) + { + if (is_gimple_call (stmt)) + { + tree fndecl = gimple_call_fndecl (stmt); + + if (!aggregate_value_p (TREE_TYPE (lhs), fndecl) + && !(fndecl && DECL_RESULT (fndecl) + && DECL_BY_REFERENCE (DECL_RESULT (fndecl)))) + need_temp = true; + } + else + need_temp = true; + } + } + if (need_temp) + { + tree temp = create_tmp_reg (TREE_TYPE (lhs)); + if (gimple_in_ssa_p (cfun) + && is_gimple_reg_type (TREE_TYPE (lhs))) + temp = make_ssa_name (temp); + gimple_set_lhs (stmt, temp); + post_stmt = gimple_build_assign (lhs, temp); + } + } + break; + } + + if (!gimple_seq_empty_p (pre)) + gsi_insert_seq_before (gsi_p, pre, GSI_SAME_STMT); + if (post_stmt) + gsi_insert_after (gsi_p, post_stmt, GSI_NEW_STMT); + + pop_gimplify_context (NULL); + + update_stmt (stmt); +} + + |