| /* 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-2018 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 |
| && is_gimple_assign (stmt) |
| && num_ops == 2 |
| && get_gimple_rhs_class (gimple_expr_code (stmt)) |
| == GIMPLE_SINGLE_RHS) |
| 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 (is_gimple_assign (stmt) |
| && num_ops == 2 |
| && get_gimple_rhs_class (gimple_expr_code (stmt)) |
| == GIMPLE_SINGLE_RHS) |
| 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 (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); |
| } |
| |
| |