From 3e60ddeb8220ed388819bb3f14e8caa9309fd3c2 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 11 Dec 2020 11:10:17 +0100 Subject: expansion: Sign or zero extend on MEM_REF stores into SUBREG with SUBREG_PROMOTED_VAR_P [PR98190] Some targets decide to promote certain scalar variables to wider mode, so their DECL_RTL is a SUBREG with SUBREG_PROMOTED_VAR_P. When storing to such vars, store_expr takes care of sign or zero extending, but if we store e.g. through MEM_REF into them, no sign or zero extension happens and that leads to wrong-code e.g. on the following testcase on aarch64-linux. The following patch uses store_expr if we overwrite all the bits and it is not reversed storage order, i.e. something that store_expr handles normally, and otherwise (if the most significant bit is (or for pdp11 might be, but pdp11 doesn't promote) being modified), the code extends manually. 2020-12-11 Jakub Jelinek PR middle-end/98190 * expr.c (expand_assignment): If to_rtx is a promoted SUBREG, ensure sign or zero extension either through use of store_expr or by extending manually. * gcc.dg/pr98190.c: New test. --- gcc/expr.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'gcc/expr.c') diff --git a/gcc/expr.c b/gcc/expr.c index 798285e..a56594e 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -5451,6 +5451,30 @@ expand_assignment (tree to, tree from, bool nontemporal) mode1, to_rtx, to, from, reversep)) result = NULL; + else if (SUBREG_P (to_rtx) + && SUBREG_PROMOTED_VAR_P (to_rtx)) + { + /* If to_rtx is a promoted subreg, we need to zero or sign + extend the value afterwards. */ + if (TREE_CODE (to) == MEM_REF + && !REF_REVERSE_STORAGE_ORDER (to) + && known_eq (bitpos, 0) + && known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (to_rtx)))) + result = store_expr (from, to_rtx, 0, nontemporal, false); + else + { + rtx to_rtx1 + = lowpart_subreg (subreg_unpromoted_mode (to_rtx), + SUBREG_REG (to_rtx), + subreg_promoted_mode (to_rtx)); + result = store_field (to_rtx1, bitsize, bitpos, + bitregion_start, bitregion_end, + mode1, from, get_alias_set (to), + nontemporal, reversep); + convert_move (SUBREG_REG (to_rtx), to_rtx1, + SUBREG_PROMOTED_SIGN (to_rtx)); + } + } else result = store_field (to_rtx, bitsize, bitpos, bitregion_start, bitregion_end, -- cgit v1.1 From 69165332a914f1167c3077fa1f57afc64fd8a667 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Sat, 19 Dec 2020 22:24:10 +0100 Subject: expr: Fix up constant_byte_string bitfield handling [PR98366] constant_byte_string now uses a convert_to_bytes function, which doesn't handle bitfields at all (don't punt on them, just puts them into wrong bits or bytes). Furthermore, I don't see a reason why that function should exist at all, it duplicates native_encode_initializer functionality. Except that native_encode_initializer punted on flexible array members and 2 tests in the testsuite relied on constant_byte_string handling those. So, this patch throws away convert_to_bytes, uses native_encode_initializer instead, but teaches it to handle flexible array members (only in the non-mask mode with off == -1 for now), furthermore, it adds various corner case checks that the old implementation was missing (like that STRING_CSTs use int as length and therefore we shouldn't try to build larger than that strings, or that native_encode*/native_interpret* APIs require sane host and target bytes (8-bit on both). 2020-12-19 Jakub Jelinek PR middle-end/98366 * fold-const.c (native_encode_initializer): Don't try to memset more than total_bytes with off == -1 even if len is large. Handle flexible array member initializers if off == -1 and mask is NULL. * expr.c (convert_to_bytes): Remove. (constant_byte_string): Use native_encode_initializer instead of convert_to_bytes. Remove extraneous semicolon. Punt on various corner-cases the APIs don't handle, like sizes > INT_MAX, BITS_PER_UNIT != 8, CHAR_BIT != 8. * gcc.c-torture/execute/pr98366.c: New test. --- gcc/expr.c | 150 ++++++++++++++++--------------------------------------------- 1 file changed, 38 insertions(+), 112 deletions(-) (limited to 'gcc/expr.c') diff --git a/gcc/expr.c b/gcc/expr.c index a56594e..33934d6 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -11631,111 +11631,6 @@ is_aligning_offset (const_tree offset, const_tree exp) return TREE_CODE (offset) == ADDR_EXPR && TREE_OPERAND (offset, 0) == exp; } -/* If EXPR is a constant initializer (either an expression or CONSTRUCTOR), - attempt to obtain its native representation as an array of nonzero BYTES. - Return true on success and false on failure (the latter without modifying - BYTES). */ - -static bool -convert_to_bytes (tree type, tree expr, vec *bytes) -{ - if (TREE_CODE (expr) == CONSTRUCTOR) - { - /* Set to the size of the CONSTRUCTOR elements. */ - unsigned HOST_WIDE_INT ctor_size = bytes->length (); - - if (TREE_CODE (type) == ARRAY_TYPE) - { - tree val, idx; - tree eltype = TREE_TYPE (type); - unsigned HOST_WIDE_INT elsize = - tree_to_uhwi (TYPE_SIZE_UNIT (eltype)); - - /* Jump through hoops to determine the lower bound for languages - like Ada that can set it to an (almost) arbitrary value. */ - tree dom = TYPE_DOMAIN (type); - if (!dom) - return false; - tree min = TYPE_MIN_VALUE (dom); - if (!min || !tree_fits_uhwi_p (min)) - return false; - unsigned HOST_WIDE_INT i, last_idx = tree_to_uhwi (min) - 1; - FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, idx, val) - { - /* Append zeros for elements with no initializers. */ - if (!tree_fits_uhwi_p (idx)) - return false; - unsigned HOST_WIDE_INT cur_idx = tree_to_uhwi (idx); - if (unsigned HOST_WIDE_INT size = cur_idx - (last_idx + 1)) - { - size = size * elsize + bytes->length (); - bytes->safe_grow_cleared (size, true); - } - - if (!convert_to_bytes (eltype, val, bytes)) - return false; - - last_idx = cur_idx; - } - } - else if (TREE_CODE (type) == RECORD_TYPE) - { - tree val, fld; - unsigned HOST_WIDE_INT i; - FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, fld, val) - { - /* Append zeros for members with no initializers and - any padding. */ - unsigned HOST_WIDE_INT cur_off = int_byte_position (fld); - if (bytes->length () < cur_off) - bytes->safe_grow_cleared (cur_off, true); - - if (!convert_to_bytes (TREE_TYPE (val), val, bytes)) - return false; - } - } - else - return false; - - /* Compute the size of the COSNTRUCTOR elements. */ - ctor_size = bytes->length () - ctor_size; - - /* Append zeros to the byte vector to the full size of the type. - The type size can be less than the size of the CONSTRUCTOR - if the latter contains initializers for a flexible array - member. */ - tree size = TYPE_SIZE_UNIT (type); - unsigned HOST_WIDE_INT type_size = tree_to_uhwi (size); - if (ctor_size < type_size) - if (unsigned HOST_WIDE_INT size_grow = type_size - ctor_size) - bytes->safe_grow_cleared (bytes->length () + size_grow, true); - - return true; - } - - /* Except for RECORD_TYPE which may have an initialized flexible array - member, the size of a type is the same as the size of the initializer - (including any implicitly zeroed out members and padding). Allocate - just enough for that many bytes. */ - tree expr_size = TYPE_SIZE_UNIT (TREE_TYPE (expr)); - if (!expr_size || !tree_fits_uhwi_p (expr_size)) - return false; - const unsigned HOST_WIDE_INT expr_bytes = tree_to_uhwi (expr_size); - const unsigned bytes_sofar = bytes->length (); - /* native_encode_expr can convert at most INT_MAX bytes. vec is limited - to at most UINT_MAX. */ - if (bytes_sofar + expr_bytes > INT_MAX) - return false; - - /* Unlike for RECORD_TYPE, there is no need to clear the memory since - it's completely overwritten by native_encode_expr. */ - bytes->safe_grow (bytes_sofar + expr_bytes, true); - unsigned char *pnext = bytes->begin () + bytes_sofar; - int nbytes = native_encode_expr (expr, pnext, expr_bytes, 0); - /* NBYTES is zero on failure. Otherwise it should equal EXPR_BYTES. */ - return (unsigned HOST_WIDE_INT) nbytes == expr_bytes; -} - /* Return a STRING_CST corresponding to ARG's constant initializer either if it's a string constant, or, when VALREP is set, any other constant, or null otherwise. @@ -11748,7 +11643,7 @@ static tree constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl, bool valrep = false) { - tree dummy = NULL_TREE;; + tree dummy = NULL_TREE; if (!mem_size) mem_size = &dummy; @@ -11903,18 +11798,42 @@ constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl, if (!base_off.is_constant (&cstoff)) return NULL_TREE; + /* Check that the host and target are sane. */ + if (CHAR_BIT != 8 || BITS_PER_UNIT != 8) + return NULL_TREE; + + HOST_WIDE_INT typesz = int_size_in_bytes (TREE_TYPE (init)); + if (typesz <= 0 || (int) typesz != typesz) + return NULL_TREE; + + HOST_WIDE_INT size = typesz; + if (VAR_P (array) + && DECL_SIZE_UNIT (array) + && tree_fits_shwi_p (DECL_SIZE_UNIT (array))) + { + size = tree_to_shwi (DECL_SIZE_UNIT (array)); + gcc_checking_assert (size >= typesz); + } + /* If value representation was requested convert the initializer for the whole array or object into a string of bytes forming its value representation and return it. */ - auto_vec bytes; - if (!convert_to_bytes (TREE_TYPE (init), init, &bytes)) - return NULL_TREE; + unsigned char *bytes = XNEWVEC (unsigned char, size); + int r = native_encode_initializer (init, bytes, size); + if (r < typesz) + { + XDELETEVEC (bytes); + return NULL_TREE; + } + + if (r < size) + memset (bytes + r, '\0', size - r); - unsigned n = bytes.length (); - const char *p = reinterpret_cast(bytes.address ()); - init = build_string_literal (n, p, char_type_node); + const char *p = reinterpret_cast(bytes); + init = build_string_literal (size, p, char_type_node); init = TREE_OPERAND (init, 0); init = TREE_OPERAND (init, 0); + XDELETE (bytes); *mem_size = size_int (TREE_STRING_LENGTH (init)); *ptr_offset = wide_int_to_tree (ssizetype, base_off); @@ -11965,6 +11884,10 @@ constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl, && (TREE_CODE (TREE_TYPE (array)) == INTEGER_TYPE || TYPE_MAIN_VARIANT (inittype) == char_type_node)) { + /* Check that the host and target are sane. */ + if (CHAR_BIT != 8 || BITS_PER_UNIT != 8) + return NULL_TREE; + /* For a reference to (address of) a single constant character, store the native representation of the character in CHARBUF. If the reference is to an element of an array or a member @@ -12007,6 +11930,9 @@ constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl, initsize = integer_zero_node; unsigned HOST_WIDE_INT size = tree_to_uhwi (initsize); + if (size > (unsigned HOST_WIDE_INT) INT_MAX) + return NULL_TREE; + init = build_string_literal (size, NULL, chartype, size); init = TREE_OPERAND (init, 0); init = TREE_OPERAND (init, 0); -- cgit v1.1 From 99dee82307f1e163e150c9c810452979994047ce Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Mon, 4 Jan 2021 10:26:59 +0100 Subject: Update copyright years. --- gcc/expr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gcc/expr.c') diff --git a/gcc/expr.c b/gcc/expr.c index 33934d6..d472eea 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -1,5 +1,5 @@ /* Convert tree expression to rtl instructions, for GNU compiler. - Copyright (C) 1988-2020 Free Software Foundation, Inc. + Copyright (C) 1988-2021 Free Software Foundation, Inc. This file is part of GCC. -- cgit v1.1 From 5de7bf5bc98ec9edc6838a443521204d0eca7605 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 5 Jan 2021 19:13:29 +0100 Subject: expand: Fold x - y < 0 to x < y during expansion [PR94802] My earlier patch to simplify x - y < 0 etc. for signed subtraction with undefined overflow into x < y in match.pd regressed some tests, even when it was guarded to be post-IPA, the following patch thus attempts to optimize that during expansion instead (which is the last time we can do it, afterwards we lose the information whether it was x - y < 0 or (int) ((unsigned) x - y) < 0 for which we couldn't optimize it. 2021-01-05 Jakub Jelinek PR tree-optimization/94802 * expr.h (maybe_optimize_sub_cmp_0): Declare. * expr.c: Include tree-pretty-print.h and flags.h. (maybe_optimize_sub_cmp_0): New function. (do_store_flag): Use it. * cfgexpand.c (expand_gimple_cond): Likewise. * gcc.target/i386/pr94802.c: New test. * gcc.dg/Wstrict-overflow-25.c: Remove xfail. --- gcc/expr.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'gcc/expr.c') diff --git a/gcc/expr.c b/gcc/expr.c index d472eea..04ef5ad 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -62,6 +62,8 @@ along with GCC; see the file COPYING3. If not see #include "ccmp.h" #include "gimple-fold.h" #include "rtx-vector-builder.h" +#include "tree-pretty-print.h" +#include "flags.h" /* If this is nonzero, we do not bother generating VOLATILE @@ -12275,6 +12277,37 @@ maybe_optimize_mod_cmp (enum tree_code code, tree *arg0, tree *arg1) *arg1 = c4; return code == EQ_EXPR ? LE_EXPR : GT_EXPR; } + +/* Optimize x - y < 0 into x < 0 if x - y has undefined overflow. */ + +void +maybe_optimize_sub_cmp_0 (enum tree_code code, tree *arg0, tree *arg1) +{ + gcc_checking_assert (code == GT_EXPR || code == GE_EXPR + || code == LT_EXPR || code == LE_EXPR); + gcc_checking_assert (integer_zerop (*arg1)); + + if (!optimize) + return; + + gimple *stmt = get_def_for_expr (*arg0, MINUS_EXPR); + if (stmt == NULL) + return; + + tree treeop0 = gimple_assign_rhs1 (stmt); + tree treeop1 = gimple_assign_rhs2 (stmt); + if (!TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (treeop0))) + return; + + if (issue_strict_overflow_warning (WARN_STRICT_OVERFLOW_COMPARISON)) + warning_at (gimple_location (stmt), OPT_Wstrict_overflow, + "assuming signed overflow does not occur when " + "simplifying % to %", + op_symbol_code (code), op_symbol_code (code)); + + *arg0 = treeop0; + *arg1 = treeop1; +} /* Generate code to calculate OPS, and exploded expression using a store-flag instruction and return an rtx for the result. @@ -12363,6 +12396,14 @@ do_store_flag (sepops ops, rtx target, machine_mode mode) } } + /* Optimize (x - y) < 0 into x < y if x - y has undefined overflow. */ + if (!unsignedp + && (ops->code == LT_EXPR || ops->code == LE_EXPR + || ops->code == GT_EXPR || ops->code == GE_EXPR) + && integer_zerop (arg1) + && TREE_CODE (arg0) == SSA_NAME) + maybe_optimize_sub_cmp_0 (ops->code, &arg0, &arg1); + /* Get the rtx comparison code to use. We know that EXP is a comparison operation of some type. Some comparisons against 1 and -1 can be converted to comparisons with zero. Do so here so that the tests -- cgit v1.1 From bc7c2b34c34f21ea58198ba58a48eb065bdda25d Mon Sep 17 00:00:00 2001 From: Kito Cheng Date: Fri, 22 Jan 2021 16:29:09 +0800 Subject: PR target/98743: Fix ICE in convert_move for RISC-V - Check `from` mode is not BLMmode before call store_expr, calling store_expr with BLKmode will cause ICE. - Verified with riscv64, x86_64 and aarch64, no introduce new regression. Note: Those logic was introduced by 3e60ddeb8220ed388819bb3f14e8caa9309fd3c2, so I cc Jakub for reivew. Changes for V2: - Checking mode of `from` rather than mode of `to`. - Verified on riscv64, x86_64 and aarch64 again. gcc/ChangeLog: PR target/98743 * expr.c: Check mode before calling store_expr. gcc/testsuite/ChangeLog: PR target/98743 * g++.dg/opt/pr98743.C: New. --- gcc/expr.c | 1 + 1 file changed, 1 insertion(+) (limited to 'gcc/expr.c') diff --git a/gcc/expr.c b/gcc/expr.c index 04ef5ad..86dc1b6 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -5459,6 +5459,7 @@ expand_assignment (tree to, tree from, bool nontemporal) /* If to_rtx is a promoted subreg, we need to zero or sign extend the value afterwards. */ if (TREE_CODE (to) == MEM_REF + && TYPE_MODE (TREE_TYPE (from)) != BLKmode && !REF_REVERSE_STORAGE_ORDER (to) && known_eq (bitpos, 0) && known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (to_rtx)))) -- cgit v1.1