Hi! As mentioned in the PR, we generate a useless movzbl insn before lock cmpxchg. The problem is that the builtin for the char/short cases has the arguments promoted to int and combine gives up, because the instructions have MEM_VOLATILE_P arguments and recog in that case doesn't recognize anything when volatile_ok is false, and nothing afterwards optimizes the (reg:SI a) = (zero_extend:SI (reg:QI a)) ... (subreg:QI (reg:SI a) 0) ...
The following patch fixes it at expansion time, we already have a function that is meant to undo the promotion, so this just adds the very common case to that. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2020-07-14 Jakub Jelinek <ja...@redhat.com> PR target/96176 * builtins.c: Include gimple-ssa.h, tree-ssa-live.h and tree-outof-ssa.h. (expand_expr_force_mode): If exp is a SSA_NAME with different mode from MODE and get_gimple_for_ssa_name is a cast from MODE, use the cast's rhs. * gcc.target/i386/pr96176.c: New test. --- gcc/builtins.c.jj 2020-06-22 10:59:15.000000000 +0200 +++ gcc/builtins.c 2020-07-13 12:37:56.519653940 +0200 @@ -73,6 +73,9 @@ along with GCC; see the file COPYING3. #include "gomp-constants.h" #include "omp-general.h" #include "tree-dfa.h" +#include "gimple-ssa.h" +#include "tree-ssa-live.h" +#include "tree-outof-ssa.h" struct target_builtins default_target_builtins; #if SWITCHABLE_TARGET @@ -6671,6 +6674,21 @@ expand_expr_force_mode (tree exp, machin rtx val; machine_mode old_mode; + if (TREE_CODE (exp) == SSA_NAME + && TYPE_MODE (TREE_TYPE (exp)) != mode) + { + /* Undo argument promotion if possible, as combine might not + be able to do it later due to MEM_VOLATILE_P uses in the + patterns. */ + gimple *g = get_gimple_for_ssa_name (exp); + if (g && gimple_assign_cast_p (g)) + { + tree rhs = gimple_assign_rhs1 (g); + if (TYPE_MODE (TREE_TYPE (rhs)) == mode) + exp = rhs; + } + } + val = expand_expr (exp, NULL_RTX, mode, EXPAND_NORMAL); /* If VAL is promoted to a wider mode, convert it back to MODE. Take care of CONST_INTs, where we know the old_mode only from the call argument. */ --- gcc/testsuite/gcc.target/i386/pr96176.c.jj 2020-07-13 12:44:15.940149701 +0200 +++ gcc/testsuite/gcc.target/i386/pr96176.c 2020-07-13 12:45:07.822396980 +0200 @@ -0,0 +1,13 @@ +/* PR target/96176 */ +/* { dg-do compile { target lp64 } } */ +/* { dg-options "-O2" } */ +/* { dg-final { scan-assembler-not "\tmovzbl\t" } } */ + +unsigned char v; + +void +foo (unsigned char *x, unsigned char y, unsigned char z) +{ + __atomic_compare_exchange_n (x, &y, z, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); + v = y; +} Jakub