PR89313 exposes a bug in the asmcons pass where it replaces input operands with matching constraints with their associated output operands, as well as all other uses of its pseudo registers. This is normally fine. However, if the matched output operand is marked as early clobber, then we cannot replace the uses of 'input' that do not have matching constraints, since they by definition conflict with the early clobber output operand and could be clobbered if assigned to the same register as the output operand.
The patch below fixes the bug by only doing the input pseudo replacement if the output operand is not early clobber or the input operand is known to be a matching constraint. This passed bootstrap and regression testing with no regressions on both x86_64-linux and powerpc64le-linux. Ok for mainline? Peter gcc/ PR rtl-optimization/89313 * function.c (matching_constraint_num): New static function. (match_asm_constraints_1): Use it. Fixup white space and comment. Don't replace inputs with non-matching constraints which conflict with early clobber outputs. gcc/testsuite/ PR rtl-optimization/89313 * gcc.dg/pr89313.c: New test. Index: gcc/function.c =================================================================== --- gcc/function.c (revision 268883) +++ gcc/function.c (working copy) @@ -6395,6 +6395,36 @@ make_pass_thread_prologue_and_epilogue ( } +/* If CONSTRAINT is a matching constraint, then return its number. + Otherwise, return -1. */ + +static int +matching_constraint_num (const char *constraint) +{ + int match; + + if (*constraint == '%') + constraint++; + + switch (*constraint) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + char *end; + match = strtoul (constraint, &end, 10); + if (end == constraint) + match = -1; + break; + } + + default: + match = -1; + break; + } + return match; +} + /* This mini-pass fixes fall-out from SSA in asm statements that have in-out constraints. Say you start with @@ -6453,14 +6483,10 @@ match_asm_constraints_1 (rtx_insn *insn, rtx input, output; rtx_insn *insns; const char *constraint = ASM_OPERANDS_INPUT_CONSTRAINT (op, i); - char *end; int match, j; - if (*constraint == '%') - constraint++; - - match = strtoul (constraint, &end, 10); - if (end == constraint) + match = matching_constraint_num (constraint); + if (match < 0) continue; gcc_assert (match < noutputs); @@ -6477,14 +6503,14 @@ match_asm_constraints_1 (rtx_insn *insn, /* We can't do anything if the output is also used as input, as we're going to overwrite it. */ for (j = 0; j < ninputs; j++) - if (reg_overlap_mentioned_p (output, RTVEC_ELT (inputs, j))) + if (reg_overlap_mentioned_p (output, RTVEC_ELT (inputs, j))) break; if (j != ninputs) continue; /* Avoid changing the same input several times. For asm ("" : "=mr" (out1), "=mr" (out2) : "0" (in), "1" (in)); - only change in once (to out1), rather than changing it + only change it once (to out1), rather than changing it first to out1 and afterwards to out2. */ if (i > 0) { @@ -6502,6 +6528,9 @@ match_asm_constraints_1 (rtx_insn *insn, end_sequence (); emit_insn_before (insns, insn); + constraint = ASM_OPERANDS_OUTPUT_CONSTRAINT(SET_SRC(p_sets[match])); + bool early_clobber_p = strchr (constraint, '&') != NULL; + /* Now replace all mentions of the input with output. We can't just replace the occurrence in inputs[i], as the register might also be used in some other input (or even in an address of an @@ -6523,7 +6552,14 @@ match_asm_constraints_1 (rtx_insn *insn, value, but different pseudos) where we formerly had only one. With more complicated asms this might lead to reload failures which wouldn't have happen without this pass. So, iterate over - all operands and replace all occurrences of the register used. */ + all operands and replace all occurrences of the register used. + + However, if one or more of the 'input' uses have a non-matching + constraint and the matched output operand is an early clobber + operand, then do not replace the input operand, since by definition + it conflicts with the output operand and cannot share the same + register. See PR89313 for details. */ + for (j = 0; j < noutputs; j++) if (!rtx_equal_p (SET_DEST (p_sets[j]), input) && reg_overlap_mentioned_p (input, SET_DEST (p_sets[j]))) @@ -6531,8 +6567,13 @@ match_asm_constraints_1 (rtx_insn *insn, input, output); for (j = 0; j < ninputs; j++) if (reg_overlap_mentioned_p (input, RTVEC_ELT (inputs, j))) - RTVEC_ELT (inputs, j) = replace_rtx (RTVEC_ELT (inputs, j), - input, output); + { + if (!early_clobber_p + || match == matching_constraint_num + (ASM_OPERANDS_INPUT_CONSTRAINT (op, j))) + RTVEC_ELT (inputs, j) = replace_rtx (RTVEC_ELT (inputs, j), + input, output); + } changed = true; } Index: gcc/testsuite/gcc.dg/pr89313.c =================================================================== --- gcc/testsuite/gcc.dg/pr89313.c (nonexistent) +++ gcc/testsuite/gcc.dg/pr89313.c (working copy) @@ -0,0 +1,26 @@ +/* PR rtl-optimization/89313 */ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2" } */ + +#if defined (__aarch64__) +# define REG "x0" +#elif defined (__arm__) +# define REG "r0" +#elif defined (__i386__) +# define REG "%eax" +#elif defined (__powerpc__) +# define REG "r3" +#elif defined (__s390__) +# define REG "0" +#elif defined (__x86_64__) +# define REG "rax" +#endif + +long +bug (long arg) +{ + register long output asm (REG); + long input = arg; + asm ("blah %0, %1, %2" : "=&r" (output) : "r" (input), "0" (input)); + return output; +}