diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a7a6ddbb0cc..85788749b46 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2018-11-08 Peter Bergner + + PR rtl-optimization/87600 + * cfgexpand.c (expand_asm_stmt): Catch illegal asm constraint usage. + * lra-constraints.c (process_alt_operands): Skip illegal hard + register usage. Prefer reloading non hard register operands. + +gcc/testsuite/ + PR rtl-optimization/87600 + * gcc.dg/pr87600.h: New file. + * gcc.dg/pr87600-1.c: New test. + * gcc.dg/pr87600-2.c: Likewise. + 2018-11-08 Sandra Loosemore PR other/36572 @@ -12,7 +25,7 @@ * common.opt (-fuse-ld=lld): New option. * doc/invoke.texi (-fuse-ld=lld): Document. * opts.c (common_handle_option): Handle OPT_fuse_ld_lld. - + 2018-11-08 Paul Koning * config/pdp11/constraints.md: Add "Z" series constraints for use diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 672fc57687c..8fa392fcd8a 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -3010,6 +3010,55 @@ expand_asm_stmt (gasm *stmt) &allows_mem, &allows_reg, &is_inout)) return; + /* If the output is a hard register, verify it doesn't conflict with + any other operand's possible hard register use. */ + if (DECL_P (val) + && REG_P (DECL_RTL (val)) + && HARD_REGISTER_P (DECL_RTL (val))) + { + unsigned j, output_hregno = REGNO (DECL_RTL (val)); + bool early_clobber_p = strchr (constraints[i], '&') != NULL; + unsigned long match; + + /* Verify the other outputs do not use the same hard register. */ + for (j = i + 1; j < noutputs; ++j) + if (DECL_P (output_tvec[j]) + && REG_P (DECL_RTL (output_tvec[j])) + && HARD_REGISTER_P (DECL_RTL (output_tvec[j])) + && output_hregno == REGNO (DECL_RTL (output_tvec[j]))) + error ("invalid hard register usage between output operands"); + + /* Verify matching constraint operands use the same hard register + and that the non-matching constraint operands do not use the same + hard register if the output is an early clobber operand. */ + for (j = 0; j < ninputs; ++j) + if (DECL_P (input_tvec[j]) + && REG_P (DECL_RTL (input_tvec[j])) + && HARD_REGISTER_P (DECL_RTL (input_tvec[j]))) + { + unsigned input_hregno = REGNO (DECL_RTL (input_tvec[j])); + switch (*constraints[j + noutputs]) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + match = strtoul (constraints[j + noutputs], NULL, 10); + break; + default: + match = ULONG_MAX; + break; + } + if (i == match + && output_hregno != input_hregno) + error ("invalid hard register usage between output operand " + "and matching constraint operand"); + else if (early_clobber_p + && i != match + && output_hregno == input_hregno) + error ("invalid hard register usage between earlyclobber " + "operand and input operand"); + } + } + if (! allows_reg && (allows_mem || is_inout diff --git a/gcc/lra-constraints.c b/gcc/lra-constraints.c index ab61989734d..88546d2138a 100644 --- a/gcc/lra-constraints.c +++ b/gcc/lra-constraints.c @@ -2146,9 +2146,32 @@ process_alt_operands (int only_alternative) } else { - /* Operands don't match. Both operands must - allow a reload register, otherwise we - cannot make them match. */ + /* Operands don't match. If the operands are + different user defined explicit hard registers, + then we cannot make them match. */ + if ((REG_P (*curr_id->operand_loc[nop]) + || SUBREG_P (*curr_id->operand_loc[nop])) + && (REG_P (*curr_id->operand_loc[m]) + || SUBREG_P (*curr_id->operand_loc[m]))) + { + rtx nop_reg = *curr_id->operand_loc[nop]; + if (SUBREG_P (nop_reg)) + nop_reg = SUBREG_REG (nop_reg); + rtx m_reg = *curr_id->operand_loc[m]; + if (SUBREG_P (m_reg)) + m_reg = SUBREG_REG (m_reg); + + if (REG_P (nop_reg) + && HARD_REGISTER_P (nop_reg) + && REG_USERVAR_P (nop_reg) + && REG_P (m_reg) + && HARD_REGISTER_P (m_reg) + && REG_USERVAR_P (m_reg)) + break; + } + + /* Both operands must allow a reload register, + otherwise we cannot make them match. */ if (curr_alt[m] == NO_REGS) break; /* Retroactively mark the operand we had to @@ -2910,18 +2933,31 @@ process_alt_operands (int only_alternative) if (first_conflict_j < 0) first_conflict_j = j; last_conflict_j = j; + /* Both the earlyclobber operand and conflicting operand + cannot both be user defined hard registers. */ + if (HARD_REGISTER_P (operand_reg[i]) + && REG_USERVAR_P (operand_reg[i]) + && operand_reg[j] != NULL_RTX + && HARD_REGISTER_P (operand_reg[j]) + && REG_USERVAR_P (operand_reg[j])) + fatal_insn ("unable to generate reloads for " + "impossible constraints:", curr_insn); } if (last_conflict_j < 0) continue; - /* If earlyclobber operand conflicts with another - non-matching operand which is actually the same register - as the earlyclobber operand, it is better to reload the - another operand as an operand matching the earlyclobber - operand can be also the same. */ - if (first_conflict_j == last_conflict_j - && operand_reg[last_conflict_j] != NULL_RTX - && ! curr_alt_match_win[last_conflict_j] - && REGNO (operand_reg[i]) == REGNO (operand_reg[last_conflict_j])) + + /* If an earlyclobber operand conflicts with another non-matching + operand (ie, they have been assigned the same hard register), + then it is better to reload the other operand, as there may + exist yet another operand with a matching constraint associated + with the earlyclobber operand. However, if one of the operands + is an explicit use of a hard register, then we must reload the + other non-hard register operand. */ + if (HARD_REGISTER_P (operand_reg[i]) + || (first_conflict_j == last_conflict_j + && operand_reg[last_conflict_j] != NULL_RTX + && !curr_alt_match_win[last_conflict_j] + && !HARD_REGISTER_P (operand_reg[last_conflict_j]))) { curr_alt_win[last_conflict_j] = false; curr_alt_dont_inherit_ops[curr_alt_dont_inherit_ops_num++] diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c227dc95ca1..4769e451d4c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2018-11-08 Peter Bergner + + PR rtl-optimization/87600 + * gcc.dg/pr87600.h: New file. + * gcc.dg/pr87600-1.c: New test. + * gcc.dg/pr87600-2.c: Likewise. + 2018-11-08 Jakub Jelinek * c-c++-common/gomp/atomic-17.c: New test. diff --git a/gcc/testsuite/gcc.dg/pr87600-1.c b/gcc/testsuite/gcc.dg/pr87600-1.c new file mode 100644 index 00000000000..351795756a3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr87600-1.c @@ -0,0 +1,52 @@ +/* PR rtl-optimization/87600 */ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2" } */ + +#include "pr87600.h" + +/* The following are all valid uses of local register variables. */ + +long +test0 (long arg) +{ + register long var asm (REG1); + asm ("blah %0 %1" : "+&r" (var) : "r" (arg)); + return var; +} + +long +test1 (long arg0, long arg1) +{ + register long var asm (REG1); + asm ("blah %0, %1, %2" : "=&r" (var) : "r" (arg0), "0" (arg1)); + return var + arg1; +} + +long +test2 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG1); + asm ("blah %0 %1" : "=&r" (var1) : "0" (var2)); + return var1; +} + +long +test3 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG2); + long var3; + asm ("blah %0 %1" : "=&r" (var1), "=r" (var3) : "1" (var2)); + return var1 + var3; +} + +long +test4 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG2); + register long var3 asm (REG2); + asm ("blah %0 %1" : "=&r" (var1), "=r" (var2) : "1" (var3)); + return var1; +} diff --git a/gcc/testsuite/gcc.dg/pr87600-2.c b/gcc/testsuite/gcc.dg/pr87600-2.c new file mode 100644 index 00000000000..e8a9f194b73 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr87600-2.c @@ -0,0 +1,44 @@ +/* PR rtl-optimization/87600 */ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2" } */ + +#include "pr87600.h" + +/* The following are all invalid uses of local register variables. */ + +long +test0 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG1); + asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "invalid hard register usage between output operands" } */ + return var1; +} + +long +test1 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG2); + asm ("blah %0 %1" : "=r" (var1) : "0" (var2)); /* { dg-error "invalid hard register usage between output operand and matching constraint operand" } */ + return var1; +} + +long +test2 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG1); + asm ("blah %0 %1" : "=&r" (var1) : "r" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ + return var1; +} + +long +test3 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG1); + long var3; + asm ("blah %0 %1" : "=&r" (var1), "=r" (var3) : "1" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ + return var1 + var3; +} diff --git a/gcc/testsuite/gcc.dg/pr87600.h b/gcc/testsuite/gcc.dg/pr87600.h new file mode 100644 index 00000000000..5ebb9288e6f --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr87600.h @@ -0,0 +1,19 @@ +#if defined (__aarch64__) +# define REG1 "x0" +# define REG2 "x1" +#elif defined (__arm__) +# define REG1 "r0" +# define REG2 "r1" +#elif defined (__i386__) +# define REG1 "%eax" +# define REG2 "%edx" +#elif defined (__powerpc__) +# define REG1 "r3" +# define REG2 "r4" +#elif defined (__s390__) +# define REG1 "0" +# define REG2 "1" +#elif defined (__x86_64__) +# define REG1 "rax" +# define REG2 "rdx" +#endif