| /* This file contains subroutine used by the C front-end to construct GENERIC. |
| Copyright (C) 2000-2022 Free Software Foundation, Inc. |
| Written by Benjamin Chelf (chelf@codesourcery.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 "c-common.h" |
| #include "tree-iterator.h" |
| |
| /* Create an empty statement tree rooted at T. */ |
| |
| tree |
| push_stmt_list (void) |
| { |
| tree t; |
| t = alloc_stmt_list (); |
| vec_safe_push (stmt_list_stack, t); |
| return t; |
| } |
| |
| /* Return TRUE if, after I, there are any nondebug stmts. */ |
| |
| static inline bool |
| only_debug_stmts_after_p (tree_stmt_iterator i) |
| { |
| for (tsi_next (&i); !tsi_end_p (i); tsi_next (&i)) |
| if (TREE_CODE (tsi_stmt (i)) != DEBUG_BEGIN_STMT) |
| return false; |
| return true; |
| } |
| |
| /* Finish the statement tree rooted at T. */ |
| |
| tree |
| pop_stmt_list (tree t) |
| { |
| tree u = NULL_TREE; |
| |
| /* Pop statement lists until we reach the target level. The extra |
| nestings will be due to outstanding cleanups. */ |
| while (1) |
| { |
| u = stmt_list_stack->pop (); |
| if (!stmt_list_stack->is_empty ()) |
| { |
| tree x = stmt_list_stack->last (); |
| STATEMENT_LIST_HAS_LABEL (x) |= STATEMENT_LIST_HAS_LABEL (u); |
| } |
| if (t == u) |
| break; |
| } |
| |
| gcc_assert (u != NULL_TREE); |
| |
| /* If the statement list is completely empty, just return it. This is |
| just as good small as build_empty_stmt, with the advantage that |
| statement lists are merged when they appended to one another. So |
| using the STATEMENT_LIST avoids pathological buildup of EMPTY_STMT_P |
| statements. */ |
| if (TREE_SIDE_EFFECTS (t)) |
| { |
| tree_stmt_iterator i = tsi_start (t); |
| |
| /* If the statement list contained exactly one statement, then |
| extract it immediately. */ |
| if (tsi_one_before_end_p (i)) |
| { |
| u = tsi_stmt (i); |
| tsi_delink (&i); |
| free_stmt_list (t); |
| t = u; |
| } |
| /* If the statement list contained a debug begin stmt and a |
| statement list, move the debug begin stmt into the statement |
| list and return it. */ |
| else if (!tsi_end_p (i) |
| && TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT) |
| { |
| u = tsi_stmt (i); |
| tsi_next (&i); |
| if (tsi_one_before_end_p (i) |
| && TREE_CODE (tsi_stmt (i)) == STATEMENT_LIST) |
| { |
| tree l = tsi_stmt (i); |
| tsi_prev (&i); |
| tsi_delink (&i); |
| tsi_delink (&i); |
| i = tsi_start (l); |
| free_stmt_list (t); |
| t = l; |
| tsi_link_before (&i, u, TSI_SAME_STMT); |
| } |
| while (!tsi_end_p (i) |
| && TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT) |
| tsi_next (&i); |
| /* If there are only debug stmts in the list, without them |
| we'd have an empty stmt without side effects. If there's |
| only one nondebug stmt, we'd have extracted the stmt and |
| dropped the list, and we'd take TREE_SIDE_EFFECTS from |
| that statement. In either case, keep the list's |
| TREE_SIDE_EFFECTS in sync. */ |
| if (tsi_end_p (i)) |
| TREE_SIDE_EFFECTS (t) = 0; |
| else if (only_debug_stmts_after_p (i)) |
| TREE_SIDE_EFFECTS (t) = TREE_SIDE_EFFECTS (tsi_stmt (i)); |
| } |
| } |
| |
| return t; |
| } |
| |
| /* Build a generic statement based on the given type of node and |
| arguments. Similar to `build_nt', except that we set |
| EXPR_LOCATION to LOC. */ |
| /* ??? This should be obsolete with the lineno_stmt productions |
| in the grammar. */ |
| |
| tree |
| build_stmt (location_t loc, enum tree_code code, ...) |
| { |
| tree ret; |
| int length, i; |
| va_list p; |
| bool side_effects; |
| |
| /* This function cannot be used to construct variably-sized nodes. */ |
| gcc_assert (TREE_CODE_CLASS (code) != tcc_vl_exp); |
| |
| va_start (p, code); |
| |
| ret = make_node (code); |
| TREE_TYPE (ret) = void_type_node; |
| length = TREE_CODE_LENGTH (code); |
| SET_EXPR_LOCATION (ret, loc); |
| |
| /* TREE_SIDE_EFFECTS will already be set for statements with |
| implicit side effects. Here we make sure it is set for other |
| expressions by checking whether the parameters have side |
| effects. */ |
| |
| side_effects = false; |
| for (i = 0; i < length; i++) |
| { |
| tree t = va_arg (p, tree); |
| if (t && !TYPE_P (t)) |
| side_effects |= TREE_SIDE_EFFECTS (t); |
| TREE_OPERAND (ret, i) = t; |
| } |
| |
| TREE_SIDE_EFFECTS (ret) |= side_effects; |
| |
| va_end (p); |
| return ret; |
| } |
| |
| /* Build a REALPART_EXPR or IMAGPART_EXPR, according to CODE, from ARG. */ |
| |
| tree |
| build_real_imag_expr (location_t location, enum tree_code code, tree arg) |
| { |
| tree ret; |
| tree arg_type = TREE_TYPE (arg); |
| |
| gcc_assert (code == REALPART_EXPR || code == IMAGPART_EXPR); |
| |
| if (TREE_CODE (arg_type) == COMPLEX_TYPE) |
| { |
| ret = build1 (code, TREE_TYPE (TREE_TYPE (arg)), arg); |
| SET_EXPR_LOCATION (ret, location); |
| } |
| else if (INTEGRAL_TYPE_P (arg_type) || SCALAR_FLOAT_TYPE_P (arg_type)) |
| { |
| ret = (code == REALPART_EXPR |
| ? arg |
| : omit_one_operand_loc (location, arg_type, |
| integer_zero_node, arg)); |
| } |
| else |
| { |
| error_at (location, "wrong type argument to %s", |
| code == REALPART_EXPR ? "__real" : "__imag"); |
| ret = error_mark_node; |
| } |
| |
| return ret; |
| } |