While it always seemed wrong to me that there's no way to avoid the default "flags" and "fpsr" clobbers, the regression the fix for PR/60663 introduced (see PR/63637) makes it even more desirable to have such a mechanism: This way, at least asm()s with a single output and no explicit clobbers could again have been made subject to CSE even with that bug unfixed. --- There wasn't much feedback on v1 (https://gcc.gnu.org/ml/gcc-patches/2014-10/msg03251.html) and the feedback I did get from Jeff I didn't really mean to address in this version:
> I really don't like having an option that's globally applied for this > feature. THough I am OK with having a mechanism to avoid > implicit clobbers on specific ASMs. I don't really understand what's wrong with a command line option allowing to state this globally for a source file or even entire project. > Why use negative numbers for the hard register numbers? I > wouldn't be at all surprised if lots of random code assumes > register numbers are always positive. I'm lacking an idea (or suggestion) of a better alternative. Using positive numbers resulted in far more problems, as such registers then got accepted elsewhere as valid too. > I don't like adding new registers with special names like !foo. > Instead I think that listing "!cc" or something similar in the asm > itself if it doesn't clobber the cc register would be better. I didn't really understand what was meant here, i.e. how the proposed alternative was supposed to look like in an actual asm(). gcc/ 2016-07-06 Jan Beulich <jbeul...@suse.com> * cfgexpand.c (expand_asm_stmt): Cope with negative register numbers. * config/i386/i386.c (ix86_target_string): Add -mno-default-asm-clobbers. (ix86_valid_target_attribute_inner_p): Handle -m{,no-}default-asm-clobbers. (ix86_md_asm_adjust): Handle "inverse" clobbers. * config/i386/i386.h (NOFLAGS_REGNUM, NOFPSR_REGNUM): Define. (ADDITIONAL_REGISTER_NAMES): Add "!flags" and "!fpsr". (OVERLAPPING_REGISTER_NAMES): Define. * config/i386/i386.opt: Add mdefault-asm-clobbers and mno-default-asm-clobbers. * varasm.c (decode_reg_name_and_count): Permit negative register numbers from ADDITIONAL_REGISTER_NAMES. gcc/testsuite/ 2016-07-06 Jan Beulich <jbeul...@suse.com> * gcc.target/i386/20060218-1.c: Adjust expected error. * gcc.target/i386/invclbr[123].c: New. --- 2016-06-30/gcc/cfgexpand.c +++ 2016-06-30/gcc/cfgexpand.c @@ -2884,26 +2884,18 @@ expand_asm_stmt (gasm *stmt) int nregs, j; j = decode_reg_name_and_count (regname, &nregs); - if (j < 0) + if (j == -2) { - if (j == -2) - { - /* ??? Diagnose during gimplification? */ - error ("unknown register name %qs in %<asm%>", regname); - } - else if (j == -4) - { - rtx x = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)); - clobber_rvec.safe_push (x); - } - else - { - /* Otherwise we should have -1 == empty string - or -3 == cc, which is not a register. */ - gcc_assert (j == -1 || j == -3); - } + /* ??? Diagnose during gimplification? */ + error ("unknown register name %qs in %<asm%>", regname); } - else + else if (j == -4) + { + rtx x = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)); + clobber_rvec.safe_push (x); + } + else if (j != -1 /* empty string */ + && j != -3 /* cc, which is not a register */) for (int reg = j; reg < j + nregs; reg++) { /* Clobbering the PIC register is an error. */ @@ -2915,7 +2907,8 @@ expand_asm_stmt (gasm *stmt) return; } - SET_HARD_REG_BIT (clobbered_regs, reg); + if (reg >= 0) + SET_HARD_REG_BIT (clobbered_regs, reg); rtx x = gen_rtx_REG (reg_raw_mode[reg], reg); clobber_rvec.safe_push (x); } --- 2016-06-30/gcc/config/i386/i386.c +++ 2016-06-30/gcc/config/i386/i386.c @@ -4207,6 +4207,7 @@ ix86_target_string (HOST_WIDE_INT isa, i { "-minline-stringops-dynamically", MASK_INLINE_STRINGOPS_DYNAMICALLY }, { "-mms-bitfields", MASK_MS_BITFIELD_LAYOUT }, { "-mno-align-stringops", MASK_NO_ALIGN_STRINGOPS }, + { "-mno-default-asm-clobbers", MASK_NO_DEFAULT_ASM_CLOBBERS }, { "-mno-fancy-math-387", MASK_NO_FANCY_MATH_387 }, { "-mno-push-args", MASK_NO_PUSH_ARGS }, { "-mno-red-zone", MASK_NO_RED_ZONE }, @@ -6488,6 +6489,10 @@ ix86_valid_target_attribute_inner_p (tre OPT_mno_align_stringops, MASK_NO_ALIGN_STRINGOPS), + IX86_ATTR_NO ("default-asm-clobbers", + OPT_mno_default_asm_clobbers, + MASK_NO_DEFAULT_ASM_CLOBBERS), + IX86_ATTR_YES ("recip", OPT_mrecip, MASK_RECIP), @@ -48384,8 +48389,38 @@ ix86_md_asm_adjust (vec<rtx> &outputs, v vec<const char *> &constraints, vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs) { - clobbers.safe_push (gen_rtx_REG (CCFPmode, FPSR_REG)); - SET_HARD_REG_BIT (clobbered_regs, FPSR_REG); + bool noflags_used = false, nofpsr_used = false; + + for (unsigned i = 0; i < clobbers.length (); ) + { + const_rtx clobbered_reg = clobbers[i]; + + if (! REG_P (clobbered_reg)) + { + ++i; + continue; + } + + switch (REGNO (clobbered_reg)) + { + default: ++i; continue; + case NOFLAGS_REGNUM: noflags_used = true; break; + case NOFPSR_REGNUM: nofpsr_used = true; break; + } + + clobbers.unordered_remove (i); + } + + if ((noflags_used && TEST_HARD_REG_BIT (clobbered_regs, FLAGS_REG)) + || (nofpsr_used && TEST_HARD_REG_BIT (clobbered_regs, FPSR_REG))) + error ("conflicting clobbers in %<asm%>"); + + if (TARGET_DEFAULT_ASM_CLOBBERS && !nofpsr_used + && !TEST_HARD_REG_BIT (clobbered_regs, FPSR_REG)) + { + clobbers.safe_push (gen_rtx_REG (CCFPmode, FPSR_REG)); + SET_HARD_REG_BIT (clobbered_regs, FPSR_REG); + } bool saw_asm_flag = false; @@ -48395,6 +48430,11 @@ ix86_md_asm_adjust (vec<rtx> &outputs, v const char *con = constraints[i]; if (strncmp (con, "=@cc", 4) != 0) continue; + if (noflags_used) + { + error ("%<asm%> flag output conflicts with clobbers"); + continue; + } con += 4; if (strchr (con, ',') != NULL) { @@ -48530,13 +48570,16 @@ ix86_md_asm_adjust (vec<rtx> &outputs, v if (saw_asm_flag) return seq; - else + + if (TARGET_DEFAULT_ASM_CLOBBERS && !noflags_used + && !TEST_HARD_REG_BIT (clobbered_regs, FLAGS_REG)) { /* If we had no asm flag outputs, clobber the flags. */ clobbers.safe_push (gen_rtx_REG (CCmode, FLAGS_REG)); SET_HARD_REG_BIT (clobbered_regs, FLAGS_REG); - return NULL; } + + return NULL; } /* Implements target vector targetm.asm.encode_section_info. */ --- 2016-06-30/gcc/config/i386/i386.h +++ 2016-06-30/gcc/config/i386/i386.h @@ -1304,6 +1304,11 @@ extern const char *host_detect_local_cpu : REAL_PIC_OFFSET_TABLE_REGNUM) \ : INVALID_REGNUM) +/* Fake register numbers to be used as "inverse" asm() clobber specifiers. + Any negative numbers below the range used by decode_reg_name () will do. */ +#define NOFLAGS_REGNUM (-127) +#define NOFPSR_REGNUM (-126) + #define GOT_SYMBOL_NAME "_GLOBAL_OFFSET_TABLE_" /* This is overridden by <cygwin.h>. */ @@ -2141,7 +2146,11 @@ do { \ { "zmm16", 53}, { "zmm17", 54}, { "zmm18", 55}, { "zmm19", 56}, \ { "zmm20", 57}, { "zmm21", 58}, { "zmm22", 59}, { "zmm23", 60}, \ { "zmm24", 61}, { "zmm25", 62}, { "zmm26", 63}, { "zmm27", 64}, \ - { "zmm28", 65}, { "zmm29", 66}, { "zmm30", 67}, { "zmm31", 68} } + { "zmm28", 65}, { "zmm29", 66}, { "zmm30", 67}, { "zmm31", 68}, \ + { "!flags", NOFLAGS_REGNUM }, { "!fpsr", NOFPSR_REGNUM } } + +#define OVERLAPPING_REGISTER_NAMES \ +{ { "cc", FLAGS_REG, 2 }, { "!cc", NOFLAGS_REGNUM, 2 } } /* Note we are omitting these since currently I don't know how to get gcc to use these, since they want the same but different --- 2016-06-30/gcc/config/i386/i386.opt +++ 2016-06-30/gcc/config/i386/i386.opt @@ -304,6 +304,10 @@ Enum(pmode) String(long) Value(PMODE_DI) mcpu= Target RejectNegative Joined Undocumented Alias(mtune=) Warn(%<-mcpu=%> is deprecated; use %<-mtune=%> or %<-march=%> instead) +mdefault-asm-clobbers +Target RejectNegative Report InverseMask(NO_DEFAULT_ASM_CLOBBERS, DEFAULT_ASM_CLOBBERS) Undocumented Save +Attach compatibility clobbers (flags and fpsr) to every asm(). + mfancy-math-387 Target RejectNegative Report InverseMask(NO_FANCY_MATH_387, USE_FANCY_MATH_387) Save Generate sin, cos, sqrt for FPU. @@ -372,6 +376,10 @@ Use native (MS) bitfield layout. mno-align-stringops Target RejectNegative Report Mask(NO_ALIGN_STRINGOPS) Undocumented Save +mno-default-asm-clobbers +Target RejectNegative Report Mask(NO_DEFAULT_ASM_CLOBBERS) Save +Don't attach compatibility clobbers (flags and fpsr) to every asm(). + mno-fancy-math-387 Target RejectNegative Report Mask(NO_FANCY_MATH_387) Undocumented Save --- 2016-06-30/gcc/testsuite/gcc.target/i386/20060218-1.c +++ 2016-06-30/gcc/testsuite/gcc.target/i386/20060218-1.c @@ -3,6 +3,6 @@ void foo (void) { - register int cc __asm ("cc"); /* { dg-error "invalid register name" } */ + register int cc __asm ("cc"); /* { dg-error "register specified .* not general enough" } */ __asm ("" : : "r" (cc) : "cc"); } --- 2016-06-30/gcc/testsuite/gcc.target/i386/invclbr1.c +++ 2016-06-30/gcc/testsuite/gcc.target/i386/invclbr1.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "" } */ + +void test(void) +{ + int v; + + asm ("" ::: "cc"); + asm ("" ::: "!cc"); + asm ("" ::: "flags"); + asm ("" ::: "!flags"); + asm ("" ::: "fpsr"); + asm ("" ::: "!fpsr"); + + asm ("" ::: "cc", "!cc"); /* { dg-error "conflicting clobbers" } */ + asm ("" ::: "flags", "!flags"); /* { dg-error "conflicting clobbers" } */ + asm ("" ::: "fpsr", "!fpsr"); /* { dg-error "conflicting clobbers" } */ + asm ("" ::: "flags", "!cc"); /* { dg-error "conflicting clobbers" } */ + asm ("" ::: "cc", "!flags"); /* { dg-error "conflicting clobbers" } */ + asm ("" ::: "fpsr", "!cc"); /* { dg-error "conflicting clobbers" } */ + asm ("" ::: "cc", "!fpsr"); /* { dg-error "conflicting clobbers" } */ + +#if 0 /* These right now are internal compiler errors. */ + asm ("" : "=@ccz" (v) :: "cc"); /* { dg-bogus "clobber conflict with output" } */ + asm ("" : "=@ccz" (v) :: "flags"); /* { dg-bogus "clobber conflict with output" } */ +#endif + asm ("" : "=@ccz" (v) :: "!cc"); /* { dg-error "output conflicts with clobbers" } */ + asm ("" : "=@ccz" (v) :: "!flags"); /* { dg-error "output conflicts with clobbers" } */ + + asm ("" ::: "flags", "!fpsr"); + asm ("" ::: "fpsr", "!flags"); +} --- 2016-06-30/gcc/testsuite/gcc.target/i386/invclbr2.c +++ 2016-06-30/gcc/testsuite/gcc.target/i386/invclbr2.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-rtl-expand" } */ + +void test(void) +{ + asm ("" ::: "!cc"); + asm ("" ::: "!fpsr", "!flags"); +} + +/* { dg-final { scan-rtl-dump-not "clobber" "expand" } } */ --- 2016-06-30/gcc/testsuite/gcc.target/i386/invclbr3.c +++ 2016-06-30/gcc/testsuite/gcc.target/i386/invclbr3.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-rtl-expand" } */ + +void test(void) +{ + asm ("" ::: "!cc"); + asm ("" ::: "!flags"); + asm ("" ::: "!fpsr"); +} + +/* { dg-final { scan-rtl-dump-times "\\(clobber \\(reg\[^()\]* fpsr\\)\\)" 1 "expand" } } */ +/* { dg-final { scan-rtl-dump-times "\\(clobber \\(reg\[^()\]* flags\\)\\)" 1 "expand" } } */ --- 2016-06-30/gcc/varasm.c +++ 2016-06-30/gcc/varasm.c @@ -943,7 +943,7 @@ decode_reg_name_and_count (const char *a for (i = 0; i < (int) ARRAY_SIZE (table); i++) if (table[i].name[0] && ! strcmp (asmspec, table[i].name) - && reg_names[table[i].number][0]) + && (table[i].number < 0 || reg_names[table[i].number][0])) return table[i].number; } #endif /* ADDITIONAL_REGISTER_NAMES */