| /* Convert language-specific tree expression to rtl instructions, |
| for GNU compiler. |
| Copyright (C) 1988-2021 Free Software Foundation, Inc. |
| |
| 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 "cp-tree.h" |
| |
| /* Expand C++-specific constants. Currently, this means PTRMEM_CST. */ |
| |
| tree |
| cplus_expand_constant (tree cst) |
| { |
| switch (TREE_CODE (cst)) |
| { |
| case PTRMEM_CST: |
| { |
| tree type = TREE_TYPE (cst); |
| tree member; |
| |
| /* Find the member. */ |
| member = PTRMEM_CST_MEMBER (cst); |
| |
| /* We can't lower this until the class is complete. */ |
| if (!COMPLETE_TYPE_P (DECL_CONTEXT (member))) |
| return cst; |
| |
| if (TREE_CODE (member) == FIELD_DECL) |
| { |
| /* Find the offset for the field. */ |
| cst = byte_position (member); |
| while (!same_type_p (DECL_CONTEXT (member), |
| TYPE_PTRMEM_CLASS_TYPE (type))) |
| { |
| /* The MEMBER must have been nestled within an |
| anonymous aggregate contained in TYPE. Find the |
| anonymous aggregate. */ |
| member = lookup_anon_field (TYPE_PTRMEM_CLASS_TYPE (type), |
| DECL_CONTEXT (member)); |
| cst = size_binop (PLUS_EXPR, cst, byte_position (member)); |
| } |
| cst = fold (build_nop (type, cst)); |
| } |
| else |
| { |
| tree delta; |
| tree pfn; |
| |
| expand_ptrmemfunc_cst (cst, &delta, &pfn); |
| cst = build_ptrmemfunc1 (type, delta, pfn); |
| } |
| } |
| break; |
| |
| case CONSTRUCTOR: |
| { |
| constructor_elt *elt; |
| unsigned HOST_WIDE_INT idx; |
| FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (cst), idx, elt) |
| elt->value = cplus_expand_constant (elt->value); |
| } |
| |
| default: |
| /* There's nothing to do. */ |
| break; |
| } |
| |
| return cst; |
| } |
| |
| /* We've seen an actual use of EXPR. Possibly replace an outer variable |
| reference inside with its constant value or a lambda capture. */ |
| |
| tree |
| mark_use (tree expr, bool rvalue_p, bool read_p, |
| location_t loc /* = UNKNOWN_LOCATION */, |
| bool reject_builtin /* = true */) |
| { |
| #define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin) |
| |
| if (expr == NULL_TREE || error_operand_p (expr)) |
| return expr; |
| |
| if (reject_builtin && reject_gcc_builtin (expr, loc)) |
| return error_mark_node; |
| |
| if (read_p) |
| mark_exp_read (expr); |
| |
| tree oexpr = expr; |
| bool recurse_op[3] = { false, false, false }; |
| switch (TREE_CODE (expr)) |
| { |
| case VAR_DECL: |
| case PARM_DECL: |
| if (rvalue_p && is_normal_capture_proxy (expr)) |
| { |
| /* Look through capture by copy. */ |
| tree cap = DECL_CAPTURED_VARIABLE (expr); |
| if (TREE_CODE (TREE_TYPE (cap)) == TREE_CODE (TREE_TYPE (expr)) |
| && decl_constant_var_p (cap)) |
| { |
| tree val = RECUR (cap); |
| if (!is_capture_proxy (val)) |
| { |
| tree l = current_lambda_expr (); |
| LAMBDA_EXPR_CAPTURE_OPTIMIZED (l) = true; |
| } |
| return val; |
| } |
| } |
| if (outer_automatic_var_p (expr) |
| && decl_constant_var_p (expr)) |
| { |
| if (rvalue_p) |
| { |
| tree t = maybe_constant_value (expr); |
| if (TREE_CONSTANT (t)) |
| { |
| expr = t; |
| break; |
| } |
| } |
| iloc_sentinel l (loc); |
| expr = process_outer_var_ref (expr, tf_warning_or_error, true); |
| if (!(TREE_TYPE (oexpr) |
| && TYPE_REF_P (TREE_TYPE (oexpr)))) |
| expr = convert_from_reference (expr); |
| } |
| break; |
| case COMPONENT_REF: |
| case NON_DEPENDENT_EXPR: |
| recurse_op[0] = true; |
| break; |
| case COMPOUND_EXPR: |
| recurse_op[1] = true; |
| break; |
| case COND_EXPR: |
| recurse_op[2] = true; |
| if (TREE_OPERAND (expr, 1)) |
| recurse_op[1] = true; |
| break; |
| case INDIRECT_REF: |
| if (REFERENCE_REF_P (expr)) |
| { |
| /* Try to look through the reference. */ |
| tree ref = TREE_OPERAND (expr, 0); |
| if (rvalue_p && is_normal_capture_proxy (ref)) |
| { |
| /* Look through capture by reference. */ |
| tree cap = DECL_CAPTURED_VARIABLE (ref); |
| if (!TYPE_REF_P (TREE_TYPE (cap)) |
| && decl_constant_var_p (cap)) |
| { |
| tree val = RECUR (cap); |
| if (!is_capture_proxy (val)) |
| { |
| tree l = current_lambda_expr (); |
| LAMBDA_EXPR_CAPTURE_OPTIMIZED (l) = true; |
| } |
| return val; |
| } |
| } |
| tree r = mark_rvalue_use (ref, loc, reject_builtin); |
| if (r != ref) |
| expr = convert_from_reference (r); |
| } |
| break; |
| |
| case VIEW_CONVERT_EXPR: |
| if (location_wrapper_p (expr)) |
| { |
| loc = EXPR_LOCATION (expr); |
| tree op = TREE_OPERAND (expr, 0); |
| tree nop = RECUR (op); |
| if (nop == error_mark_node) |
| return error_mark_node; |
| else if (op == nop) |
| /* No change. */; |
| else if (DECL_P (nop) || CONSTANT_CLASS_P (nop)) |
| { |
| /* Reuse the location wrapper. */ |
| TREE_OPERAND (expr, 0) = nop; |
| /* If we're replacing a DECL with a constant, we also need to |
| change the TREE_CODE of the location wrapper. */ |
| if (rvalue_p) |
| TREE_SET_CODE (expr, NON_LVALUE_EXPR); |
| } |
| else |
| { |
| /* Drop the location wrapper. */ |
| expr = nop; |
| protected_set_expr_location (expr, loc); |
| } |
| return expr; |
| } |
| gcc_fallthrough(); |
| CASE_CONVERT: |
| recurse_op[0] = true; |
| break; |
| |
| case MODIFY_EXPR: |
| { |
| tree lhs = TREE_OPERAND (expr, 0); |
| /* [expr.ass] "A simple assignment whose left operand is of |
| a volatile-qualified type is deprecated unless the assignment |
| is either a discarded-value expression or appears in an |
| unevaluated context." */ |
| if (!cp_unevaluated_operand |
| && (TREE_THIS_VOLATILE (lhs) |
| || CP_TYPE_VOLATILE_P (TREE_TYPE (lhs))) |
| && !TREE_THIS_VOLATILE (expr)) |
| { |
| if (warning_at (location_of (expr), OPT_Wvolatile, |
| "using value of simple assignment with " |
| "%<volatile%>-qualified left operand is " |
| "deprecated")) |
| /* Make sure not to warn about this assignment again. */ |
| TREE_THIS_VOLATILE (expr) = true; |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| for (int i = 0; i < 3; ++i) |
| if (recurse_op[i]) |
| { |
| tree op = TREE_OPERAND (expr, i); |
| op = RECUR (op); |
| if (op == error_mark_node) |
| return error_mark_node; |
| TREE_OPERAND (expr, i) = op; |
| } |
| |
| return expr; |
| #undef RECUR |
| } |
| |
| /* Called whenever the expression EXPR is used in an rvalue context. |
| When REJECT_BUILTIN is true the expression is checked to make sure |
| it doesn't make it possible to obtain the address of a GCC built-in |
| function with no library fallback (or any of its bits, such as in |
| a conversion to bool). */ |
| |
| tree |
| mark_rvalue_use (tree e, |
| location_t loc /* = UNKNOWN_LOCATION */, |
| bool reject_builtin /* = true */) |
| { |
| return mark_use (e, true, true, loc, reject_builtin); |
| } |
| |
| /* Called whenever an expression is used in an lvalue context. */ |
| |
| tree |
| mark_lvalue_use (tree expr) |
| { |
| return mark_use (expr, false, true, input_location, false); |
| } |
| |
| /* As above, but don't consider this use a read. */ |
| |
| tree |
| mark_lvalue_use_nonread (tree expr) |
| { |
| return mark_use (expr, false, false, input_location, false); |
| } |
| |
| /* Called when expr appears as a discarded-value expression. */ |
| |
| tree |
| mark_discarded_use (tree expr) |
| { |
| /* The lvalue-to-rvalue conversion (7.1) is applied if and only if the |
| expression is a glvalue of volatile-qualified type and it is one of the |
| following: |
| * ( expression ), where expression is one of these expressions, |
| * id-expression (8.1.4), |
| * subscripting (8.2.1), |
| * class member access (8.2.5), |
| * indirection (8.3.1), |
| * pointer-to-member operation (8.5), |
| * conditional expression (8.16) where both the second and the third |
| operands are one of these expressions, or |
| * comma expression (8.19) where the right operand is one of these |
| expressions. */ |
| if (expr == NULL_TREE) |
| return expr; |
| |
| STRIP_ANY_LOCATION_WRAPPER (expr); |
| |
| switch (TREE_CODE (expr)) |
| { |
| case COND_EXPR: |
| TREE_OPERAND (expr, 2) = mark_discarded_use (TREE_OPERAND (expr, 2)); |
| gcc_fallthrough (); |
| case COMPOUND_EXPR: |
| TREE_OPERAND (expr, 1) = mark_discarded_use (TREE_OPERAND (expr, 1)); |
| return expr; |
| |
| case COMPONENT_REF: |
| case ARRAY_REF: |
| case INDIRECT_REF: |
| case MEMBER_REF: |
| break; |
| default: |
| if (DECL_P (expr)) |
| break; |
| else |
| return expr; |
| } |
| |
| /* Like mark_rvalue_use, but don't reject built-ins. */ |
| return mark_use (expr, true, true, input_location, false); |
| } |
| |
| /* Called whenever an expression is used in a type use context. */ |
| |
| tree |
| mark_type_use (tree expr) |
| { |
| mark_exp_read (expr); |
| return expr; |
| } |
| |
| /* Mark EXP as read, not just set, for set but not used -Wunused |
| warning purposes. */ |
| |
| void |
| mark_exp_read (tree exp) |
| { |
| if (exp == NULL) |
| return; |
| |
| switch (TREE_CODE (exp)) |
| { |
| case VAR_DECL: |
| if (DECL_DECOMPOSITION_P (exp)) |
| mark_exp_read (DECL_DECOMP_BASE (exp)); |
| gcc_fallthrough (); |
| case PARM_DECL: |
| DECL_READ_P (exp) = 1; |
| break; |
| case ARRAY_REF: |
| case COMPONENT_REF: |
| case MODIFY_EXPR: |
| case REALPART_EXPR: |
| case IMAGPART_EXPR: |
| CASE_CONVERT: |
| case ADDR_EXPR: |
| case INDIRECT_REF: |
| case FLOAT_EXPR: |
| case NON_DEPENDENT_EXPR: |
| case VIEW_CONVERT_EXPR: |
| mark_exp_read (TREE_OPERAND (exp, 0)); |
| break; |
| case COMPOUND_EXPR: |
| mark_exp_read (TREE_OPERAND (exp, 1)); |
| break; |
| case COND_EXPR: |
| if (TREE_OPERAND (exp, 1)) |
| mark_exp_read (TREE_OPERAND (exp, 1)); |
| if (TREE_OPERAND (exp, 2)) |
| mark_exp_read (TREE_OPERAND (exp, 2)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* Fold X for consideration by one of the warning functions when checking |
| whether an expression has a constant value. */ |
| |
| tree |
| fold_for_warn (tree x) |
| { |
| /* C++ implementation. */ |
| |
| /* Prevent warning-dependent constexpr evaluation from changing |
| DECL_UID (which breaks -fcompare-debug) and from instantiating |
| templates. */ |
| uid_sensitive_constexpr_evaluation_sentinel s; |
| |
| /* It's not generally safe to fully fold inside of a template, so |
| call fold_non_dependent_expr instead. */ |
| if (processing_template_decl) |
| { |
| tree f = fold_non_dependent_expr (x, tf_none); |
| if (f == error_mark_node) |
| return x; |
| else |
| return f; |
| } |
| else if (cxx_dialect >= cxx11) |
| x = maybe_constant_value (x); |
| |
| return c_fully_fold (x, /*for_init*/false, /*maybe_constp*/NULL); |
| } |