PR #104914 When work with int val; ((unsigned char*)&val)[3] = *buf; if (val > 0) ... The RTX mode is obtained from REG instead of SUBREG, which make D<INS> is used instead of <INS>. Thus something wrong happens on sign-extend default architectures, like MIPS64.
Let's use str_rtx and mode of str_rtx as the parameters for store_integral_bit_field if: modes of op0 and str_rtx are INT; length of op0 is greater than str_rtx. This patch has been tested on aarch64-linux-gnu, x86_64-linux-gnu, mips64el-linux-gnuabi64 without regression. gcc/ChangeLog: PR: 104914. * expmed.cc(store_bit_field_1): Pass str_rtx and its mode to store_integral_bit_field if the length of op0 is greater than str_rtx. gcc/testsuite/ChangeLog: PR: 104914. * gcc.target/mips/pr104914.c: New testcase. --- gcc/expmed.cc | 20 +++++++++++++++++--- gcc/testsuite/gcc.target/mips/pr104914.c | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.target/mips/pr104914.c diff --git a/gcc/expmed.cc b/gcc/expmed.cc index fbd4ce2d42f..5531c19e891 100644 --- a/gcc/expmed.cc +++ b/gcc/expmed.cc @@ -850,6 +850,7 @@ store_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum, since that case is valid for any mode. The following cases are only valid for integral modes. */ opt_scalar_int_mode op0_mode = int_mode_for_mode (GET_MODE (op0)); + opt_scalar_int_mode str_mode = int_mode_for_mode (GET_MODE (str_rtx)); scalar_int_mode imode; if (!op0_mode.exists (&imode) || imode != GET_MODE (op0)) { @@ -881,9 +882,22 @@ store_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum, op0 = gen_lowpart (op0_mode.require (), op0); } - return store_integral_bit_field (op0, op0_mode, ibitsize, ibitnum, - bitregion_start, bitregion_end, - fieldmode, value, reverse, fallback_p); + /* If MODEs of str_rtx and op0 are INT, and the length of op0 is greater than + str_rtx, it means that str_rtx has a shorter SUBREG: int32 on 64 mach/ABI + is an example. For this case, we should use the mode of SUBREG, otherwise + bad code will generate for sign-extension ports, like MIPS. */ + bool use_str_mode = false; + if (GET_MODE_CLASS (GET_MODE (str_rtx)) == MODE_INT + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT + && known_gt (GET_MODE_SIZE (GET_MODE (op0)), + GET_MODE_SIZE (GET_MODE (str_rtx)))) + use_str_mode = true; + + return store_integral_bit_field (use_str_mode ? str_rtx : op0, + use_str_mode ? str_mode : op0_mode, + ibitsize, ibitnum, bitregion_start, + bitregion_end, fieldmode, value, + reverse, fallback_p); } /* Subroutine of store_bit_field_1, with the same arguments, except diff --git a/gcc/testsuite/gcc.target/mips/pr104914.c b/gcc/testsuite/gcc.target/mips/pr104914.c new file mode 100644 index 00000000000..fd6ef6af446 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/pr104914.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-march=mips64r2 -mabi=64" } */ + +/* { dg-final { scan-assembler-not "\tdins\t" } } */ + +NOMIPS16 int test (const unsigned char *buf) +{ + int val; + ((unsigned char*)&val)[0] = *buf++; + ((unsigned char*)&val)[1] = *buf++; + ((unsigned char*)&val)[2] = *buf++; + ((unsigned char*)&val)[3] = *buf++; + if(val > 0) + return 1; + else + return 0; +} -- 2.30.2