Whilst working on a backend patch, I noticed that the middle-end's RTL optimizers weren't simplifying a truncation of a paradoxical subreg extension, though it does transform closely related (more complex) expressions. The main (first) part of this patch implements this simplification, reusing much of the logic already in place.
I briefly considered suggesting that it's difficult to provide a new testcase for this change, but then realized the reviewer's response would be that this type of transformation should be self-tested in simplify-rtx, so this patch adds a bunch of tests that integer extensions and truncations are simplified as expected. No good deed goes unpunished and I was equally surprised to see that we don't currently simplify/check/defend (zero_extend:SI (reg:SI)), i.e. useless no-op extensions to the same mode. So I've added some logic to simplify (or more accurately prevent us generating dubious RTL for) those. This patch has been tested on x86_64-pc-linux-gnu with make bootstrap and "make -k check" with no new failures. Ok for mainline? 2021-08-20 Roger Sayle <ro...@nextmovesoftware.com> gcc/ChangeLog * simplify-rtx.c (simplify_truncation): Generalize simplification of (truncate:A (subreg:B X)). (simplify_unary_operation_1) [FLOAT_TRUNCATE, FLOAT_EXTEND, SIGN_EXTEND, ZERO_EXTEND]: Handle cases where the operand already has the desired machine mode. (test_scalar_int_ops): Add tests that useless extensions and truncations are optimized away. (test_scalar_int_ext_ops): New self-test function to confirm that truncations of extensions are correctly simplified. (test_scalar_int_ext_ops2): New self-test function to check truncations of truncations, extensions of extensions, and truncations of extensions. (test_scalar_ops): Call the above two functions with a representative sampling of integer machine modes. Roger -- Roger Sayle NextMove Software Cambridge, UK
diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index a719f57..f3df614 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -813,23 +813,49 @@ simplify_context::simplify_truncation (machine_mode mode, rtx op, return simplify_gen_unary (GET_CODE (op), mode, XEXP (XEXP (op, 0), 0), mode); - /* (truncate:A (subreg:B (truncate:C X) 0)) is - (truncate:A X). */ + /* Simplifications of (truncate:A (subreg:B X 0)). */ if (GET_CODE (op) == SUBREG && is_a <scalar_int_mode> (mode, &int_mode) && SCALAR_INT_MODE_P (op_mode) && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op)), &subreg_mode) - && GET_CODE (SUBREG_REG (op)) == TRUNCATE && subreg_lowpart_p (op)) { - rtx inner = XEXP (SUBREG_REG (op), 0); - if (GET_MODE_PRECISION (int_mode) <= GET_MODE_PRECISION (subreg_mode)) - return simplify_gen_unary (TRUNCATE, int_mode, inner, - GET_MODE (inner)); - else - /* If subreg above is paradoxical and C is narrower - than A, return (subreg:A (truncate:C X) 0). */ - return simplify_gen_subreg (int_mode, SUBREG_REG (op), subreg_mode, 0); + /* (truncate:A (subreg:B (truncate:C X) 0)) is (truncate:A X). */ + if (GET_CODE (SUBREG_REG (op)) == TRUNCATE) + { + rtx inner = XEXP (SUBREG_REG (op), 0); + if (GET_MODE_PRECISION (int_mode) + <= GET_MODE_PRECISION (subreg_mode)) + return simplify_gen_unary (TRUNCATE, int_mode, inner, + GET_MODE (inner)); + else + /* If subreg above is paradoxical and C is narrower + than A, return (subreg:A (truncate:C X) 0). */ + return simplify_gen_subreg (int_mode, SUBREG_REG (op), + subreg_mode, 0); + } + + /* Simplifications of (truncate:A (subreg:B X:C 0)) with + paradoxical subregs (B is wider than C). */ + if (is_a <scalar_int_mode> (op_mode, &int_op_mode)) + { + unsigned int int_op_prec = GET_MODE_PRECISION (int_op_mode); + unsigned int subreg_prec = GET_MODE_PRECISION (subreg_mode); + if (int_op_prec > subreg_mode) + { + if (int_mode == subreg_mode) + return SUBREG_REG (op); + if (GET_MODE_PRECISION (int_mode) < subreg_prec) + return simplify_gen_unary (TRUNCATE, int_mode, + SUBREG_REG (op), subreg_mode); + } + /* Simplification of (truncate:A (subreg:B X:C 0)) where + A is narrower than B and B is narrower than C. */ + else if (int_op_prec < subreg_mode + && GET_MODE_PRECISION (int_mode) < int_op_prec) + return simplify_gen_unary (TRUNCATE, int_mode, + SUBREG_REG (op), subreg_mode); + } } /* (truncate:A (truncate:B X)) is (truncate:A X). */ @@ -1245,6 +1271,10 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, break; case FLOAT_TRUNCATE: + /* Check for useless truncation. */ + if (GET_MODE (op) == mode) + return op; + if (DECIMAL_FLOAT_MODE_P (mode)) break; @@ -1297,6 +1327,10 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, break; case FLOAT_EXTEND: + /* Check for useless extension. */ + if (GET_MODE (op) == mode) + return op; + if (DECIMAL_FLOAT_MODE_P (mode)) break; @@ -1410,6 +1444,10 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, break; case SIGN_EXTEND: + /* Check for useless extension. */ + if (GET_MODE (op) == mode) + return op; + /* (sign_extend (truncate (minus (label_ref L1) (label_ref L2)))) becomes just the MINUS if its mode is MODE. This allows folding switch statements on machines using casesi (such as @@ -1580,6 +1618,10 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, break; case ZERO_EXTEND: + /* Check for useless extension. */ + if (GET_MODE (op) == mode) + return op; + /* Check for a zero extension of a subreg of a promoted variable, where the promotion is zero-extended, and the target mode is the same as the variable's promotion. */ @@ -7563,8 +7605,92 @@ test_scalar_int_ops (machine_mode mode) simplify_gen_binary (IOR, mode, and_op0_6, and_op1_6)); ASSERT_RTX_EQ (simplify_gen_binary (AND, mode, and_op0_op1, six), simplify_gen_binary (AND, mode, and_op0_6, and_op1_6)); + + /* Test useless extensions are eliminated. */ + ASSERT_RTX_EQ (op0, simplify_gen_unary (TRUNCATE, mode, op0, mode)); + ASSERT_RTX_EQ (op0, simplify_gen_unary (ZERO_EXTEND, mode, op0, mode)); + ASSERT_RTX_EQ (op0, simplify_gen_unary (SIGN_EXTEND, mode, op0, mode)); + ASSERT_RTX_EQ (op0, lowpart_subreg (mode, op0, mode)); +} + +/* Verify some simplifications of integer extension/truncation. + Machine mode BMODE is the guaranteed wider than SMODE. */ + +static void +test_scalar_int_ext_ops (machine_mode bmode, machine_mode smode) +{ + rtx sreg = make_test_reg (smode); + + /* Check truncation of extension. */ + ASSERT_RTX_EQ (simplify_gen_unary (TRUNCATE, smode, + simplify_gen_unary (ZERO_EXTEND, bmode, + sreg, smode), + bmode), + sreg); + ASSERT_RTX_EQ (simplify_gen_unary (TRUNCATE, smode, + simplify_gen_unary (SIGN_EXTEND, bmode, + sreg, smode), + bmode), + sreg); + ASSERT_RTX_EQ (simplify_gen_unary (TRUNCATE, smode, + lowpart_subreg (bmode, sreg, smode), + bmode), + sreg); } +/* Verify more simplifications of integer extension/truncation. + BMODE is wider than MMODE which is wider than SMODE. */ + +static void +test_scalar_int_ext_ops2 (machine_mode bmode, machine_mode mmode, + machine_mode smode) +{ + rtx breg = make_test_reg (bmode); + rtx mreg = make_test_reg (mmode); + rtx sreg = make_test_reg (smode); + + /* Check truncate of truncate. */ + ASSERT_RTX_EQ (simplify_gen_unary (TRUNCATE, smode, + simplify_gen_unary (TRUNCATE, mmode, + breg, bmode), + mmode), + simplify_gen_unary (TRUNCATE, smode, breg, bmode)); + + /* Check extension of extension. */ + ASSERT_RTX_EQ (simplify_gen_unary (ZERO_EXTEND, bmode, + simplify_gen_unary (ZERO_EXTEND, mmode, + sreg, smode), + mmode), + simplify_gen_unary (ZERO_EXTEND, bmode, sreg, smode)); + ASSERT_RTX_EQ (simplify_gen_unary (SIGN_EXTEND, bmode, + simplify_gen_unary (SIGN_EXTEND, mmode, + sreg, smode), + mmode), + simplify_gen_unary (SIGN_EXTEND, bmode, sreg, smode)); + ASSERT_RTX_EQ (simplify_gen_unary (SIGN_EXTEND, bmode, + simplify_gen_unary (ZERO_EXTEND, mmode, + sreg, smode), + mmode), + simplify_gen_unary (ZERO_EXTEND, bmode, sreg, smode)); + + /* Check truncation of extension. */ + ASSERT_RTX_EQ (simplify_gen_unary (TRUNCATE, smode, + simplify_gen_unary (ZERO_EXTEND, bmode, + mreg, mmode), + bmode), + simplify_gen_unary (TRUNCATE, smode, mreg, mmode)); + ASSERT_RTX_EQ (simplify_gen_unary (TRUNCATE, smode, + simplify_gen_unary (SIGN_EXTEND, bmode, + mreg, mmode), + bmode), + simplify_gen_unary (TRUNCATE, smode, mreg, mmode)); + ASSERT_RTX_EQ (simplify_gen_unary (TRUNCATE, smode, + lowpart_subreg (bmode, mreg, mmode), + bmode), + simplify_gen_unary (TRUNCATE, smode, mreg, mmode)); +} + + /* Verify some simplifications involving scalar expressions. */ static void @@ -7576,6 +7702,18 @@ test_scalar_ops () if (SCALAR_INT_MODE_P (mode) && mode != BImode) test_scalar_int_ops (mode); } + + test_scalar_int_ext_ops (HImode, QImode); + test_scalar_int_ext_ops (SImode, QImode); + test_scalar_int_ext_ops (SImode, HImode); + test_scalar_int_ext_ops (DImode, QImode); + test_scalar_int_ext_ops (DImode, HImode); + test_scalar_int_ext_ops (DImode, SImode); + + test_scalar_int_ext_ops2 (SImode, HImode, QImode); + test_scalar_int_ext_ops2 (DImode, HImode, QImode); + test_scalar_int_ext_ops2 (DImode, SImode, QImode); + test_scalar_int_ext_ops2 (DImode, SImode, HImode); } /* Test vector simplifications involving VEC_DUPLICATE in which the