BSR leaves its destination unmodified when the source is zero, so the
hardware treats the destination as an input. This creates a false
dependency on the previous value of the destination register, which
can delay BSR until that value is available. Clear the destination
before BSR via post-epilogue splitters when the destination is not
used by the source, controlled by the new
X86_TUNE_AVOID_FALSE_DEP_FOR_BSR tuning.
The tuning is enabled for processors where BSR is a single uop and the
dependency is longer than its throughput cost, and for generic.
Disable the new tuning in pr78103-*.c, which check the exact BSR
code sequence generated for generic tuning.
Assisted-by: Claude Sonnet 5.5
PR target/128662
gcc/ChangeLog:
* config/i386/i386.h (TARGET_AVOID_FALSE_DEP_FOR_BSR): New.
* config/i386/x86-tune.def (X86_TUNE_AVOID_FALSE_DEP_FOR_BSR):
New tuning.
* config/i386/i386.md (bsr_rex64): Convert to
define_insn_and_split. Split to *bsr_rex64_falsedep when
TARGET_AVOID_FALSE_DEP_FOR_BSR is set and the destination is not
mentioned in the source.
(bsr_rex64_1): Ditto. Split to *bsr_rex64_1_falsedep.
(bsr_rex64_1_zext): Ditto. Split to *bsr_rex64_1_zext_falsedep.
(bsr): Ditto. Split to *bsr_falsedep.
(bsr_1): Ditto. Split to *bsr_1_falsedep.
(bsr_zext_1): Ditto. Split to *bsr_zext_1_falsedep.
(*bsr_rex64_falsedep, *bsr_rex64_1_falsedep)
(*bsr_rex64_1_zext_falsedep, *bsr_falsedep, *bsr_1_falsedep)
(*bsr_zext_1_falsedep): New insns.
gcc/testsuite/ChangeLog:
* gcc.target/i386/pr128662.c: New test.
* gcc.target/i386/pr78103-1.c (dg-options): Add
-mtune-ctrl=^avoid_false_dep_for_bsr.
* gcc.target/i386/pr78103-2.c: Ditto.
* gcc.target/i386/pr78103-3.c: Ditto.
Bootstrapped and regression tested on x86_64-pc-linux-gnu {,-m32}.
Uros.
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index b28a3fc3b0e..31a36d2e274 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -489,7 +489,9 @@ extern unsigned char ix86_tune_features[X86_TUNE_LAST];
#define TARGET_SPLIT_MEM_OPND_FOR_FP_CONVERTS \
ix86_tune_features[X86_TUNE_SPLIT_MEM_OPND_FOR_FP_CONVERTS]
#define TARGET_ADJUST_UNROLL \
- ix86_tune_features[X86_TUNE_ADJUST_UNROLL]
+ ix86_tune_features[X86_TUNE_ADJUST_UNROLL]
+#define TARGET_AVOID_FALSE_DEP_FOR_BSR \
+ ix86_tune_features[X86_TUNE_AVOID_FALSE_DEP_FOR_BSR]
#define TARGET_AVOID_FALSE_DEP_FOR_BMI \
ix86_tune_features[X86_TUNE_AVOID_FALSE_DEP_FOR_BMI]
#define TARGET_AVOID_FALSE_DEP_FOR_TZCNT \
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 2d2e620713f..23a9bc7fa59 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -22472,7 +22472,7 @@ (define_insn "*ctzsidi2_<s>ext_falsedep"
(set_attr "prefix_rep" "1")
(set_attr "mode" "SI")])
-(define_insn "bsr_rex64"
+(define_insn_and_split "bsr_rex64"
[(set (reg:CCZ FLAGS_REG)
(compare:CCZ (match_operand:DI 1 "nonimmediate_operand" "rm")
(const_int 0)))
@@ -22481,32 +22481,116 @@ (define_insn "bsr_rex64"
(clz:DI (match_dup 1))))]
"TARGET_64BIT"
"bsr{q}\t{%1, %0|%0, %1}"
+ "&& TARGET_AVOID_FALSE_DEP_FOR_BSR && epilogue_completed
+ && optimize_function_for_speed_p (cfun)
+ && !reg_mentioned_p (operands[0], operands[1])"
+ [(parallel
+ [(set (reg:CCZ FLAGS_REG)
+ (compare:CCZ (match_dup 1) (const_int 0)))
+ (set (match_dup 0)
+ (minus:DI (const_int 63)
+ (clz:DI (match_dup 1))))
+ (unspec [(match_dup 0)] UNSPEC_INSN_FALSE_DEP)])]
+ "ix86_expand_clear (operands[0]);"
+ [(set_attr "type" "alu1")
+ (set_attr "prefix_0f" "1")
+ (set_attr "znver1_decode" "vector")
+ (set_attr "c86_decode" "vector")
+ (set_attr "mode" "DI")])
+
+(define_insn "*bsr_rex64_falsedep"
+ [(set (reg:CCZ FLAGS_REG)
+ (compare:CCZ (match_operand:DI 1 "nonimmediate_operand" "rm")
+ (const_int 0)))
+ (set (match_operand:DI 0 "register_operand" "=r")
+ (minus:DI (const_int 63)
+ (clz:DI (match_dup 1))))
+ (unspec [(match_operand:DI 2 "register_operand" "0")]
+ UNSPEC_INSN_FALSE_DEP)]
+ "TARGET_64BIT"
+ "bsr{q}\t{%1, %0|%0, %1}"
+ [(set_attr "type" "alu1")
+ (set_attr "prefix_0f" "1")
+ (set_attr "znver1_decode" "vector")
+ (set_attr "c86_decode" "vector")
+ (set_attr "mode" "DI")])
+
+(define_insn_and_split "bsr_rex64_1"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (minus:DI (const_int 63)
+ (clz:DI (match_operand:DI 1 "nonimmediate_operand" "rm"))))
+ (clobber (reg:CC FLAGS_REG))]
+ "!TARGET_LZCNT && TARGET_64BIT"
+ "bsr{q}\t{%1, %0|%0, %1}"
+ "&& TARGET_AVOID_FALSE_DEP_FOR_BSR && epilogue_completed
+ && optimize_function_for_speed_p (cfun)
+ && !reg_mentioned_p (operands[0], operands[1])"
+ [(parallel
+ [(set (match_dup 0)
+ (minus:DI (const_int 63)
+ (clz:DI (match_dup 1))))
+ (unspec [(match_dup 0)] UNSPEC_INSN_FALSE_DEP)
+ (clobber (reg:CC FLAGS_REG))])]
+ "ix86_expand_clear (operands[0]);"
[(set_attr "type" "alu1")
(set_attr "prefix_0f" "1")
(set_attr "znver1_decode" "vector")
(set_attr "c86_decode" "vector")
(set_attr "mode" "DI")])
-(define_insn "bsr_rex64_1"
+(define_insn "*bsr_rex64_1_falsedep"
[(set (match_operand:DI 0 "register_operand" "=r")
(minus:DI (const_int 63)
(clz:DI (match_operand:DI 1 "nonimmediate_operand" "rm"))))
+ (unspec [(match_operand:DI 2 "register_operand" "0")]
+ UNSPEC_INSN_FALSE_DEP)
+ (clobber (reg:CC FLAGS_REG))]
+ "!TARGET_LZCNT && TARGET_64BIT"
+ "bsr{q}\t{%1, %0|%0, %1}"
+ [(set_attr "type" "alu1")
+ (set_attr "prefix_0f" "1")
+ (set_attr "znver1_decode" "vector")
+ (set_attr "c86_decode" "vector")
+ (set_attr "mode" "DI")])
+
+(define_insn_and_split "bsr_rex64_1_zext"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (zero_extend:DI
+ (minus:SI
+ (const_int 63)
+ (subreg:SI
+ (clz:DI (match_operand:DI 1 "nonimmediate_operand" "rm")) 0))))
(clobber (reg:CC FLAGS_REG))]
"!TARGET_LZCNT && TARGET_64BIT"
"bsr{q}\t{%1, %0|%0, %1}"
+ "&& TARGET_AVOID_FALSE_DEP_FOR_BSR && epilogue_completed
+ && optimize_function_for_speed_p (cfun)
+ && !reg_mentioned_p (operands[0], operands[1])"
+ [(parallel
+ [(set (match_dup 0)
+ (zero_extend:DI
+ (minus:SI
+ (const_int 63)
+ (subreg:SI
+ (clz:DI (match_dup 1)) 0))))
+ (unspec [(match_dup 0)] UNSPEC_INSN_FALSE_DEP)
+ (clobber (reg:CC FLAGS_REG))])]
+ "ix86_expand_clear (operands[0]);"
[(set_attr "type" "alu1")
(set_attr "prefix_0f" "1")
(set_attr "znver1_decode" "vector")
(set_attr "c86_decode" "vector")
(set_attr "mode" "DI")])
-(define_insn "bsr_rex64_1_zext"
+(define_insn "*bsr_rex64_1_zext_falsedep"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI
- (minus:SI (const_int 63)
- (subreg:SI
- (clz:DI (match_operand:DI 1 "nonimmediate_operand" "rm"))
- 0))))
+ (minus:SI
+ (const_int 63)
+ (subreg:SI
+ (clz:DI (match_operand:DI 1 "nonimmediate_operand" "rm")) 0))))
+ (unspec [(match_operand:DI 2 "register_operand" "0")]
+ UNSPEC_INSN_FALSE_DEP)
(clobber (reg:CC FLAGS_REG))]
"!TARGET_LZCNT && TARGET_64BIT"
"bsr{q}\t{%1, %0|%0, %1}"
@@ -22516,7 +22600,7 @@ (define_insn "bsr_rex64_1_zext"
(set_attr "c86_decode" "vector")
(set_attr "mode" "DI")])
-(define_insn "bsr"
+(define_insn_and_split "bsr"
[(set (reg:CCZ FLAGS_REG)
(compare:CCZ (match_operand:SI 1 "nonimmediate_operand" "rm")
(const_int 0)))
@@ -22525,26 +22609,79 @@ (define_insn "bsr"
(clz:SI (match_dup 1))))]
""
"bsr{l}\t{%1, %0|%0, %1}"
+ "TARGET_AVOID_FALSE_DEP_FOR_BSR && epilogue_completed
+ && optimize_function_for_speed_p (cfun)
+ && !reg_mentioned_p (operands[0], operands[1])"
+ [(parallel
+ [(set (reg:CCZ FLAGS_REG)
+ (compare:CCZ (match_dup 1) (const_int 0)))
+ (set (match_dup 0)
+ (minus:SI (const_int 31)
+ (clz:SI (match_dup 1))))
+ (unspec [(match_dup 0)] UNSPEC_INSN_FALSE_DEP)])]
+ "ix86_expand_clear (operands[0]);"
+ [(set_attr "type" "alu1")
+ (set_attr "prefix_0f" "1")
+ (set_attr "znver1_decode" "vector")
+ (set_attr "c86_decode" "vector")
+ (set_attr "mode" "SI")])
+
+(define_insn "*bsr_falsedep"
+ [(set (reg:CCZ FLAGS_REG)
+ (compare:CCZ (match_operand:SI 1 "nonimmediate_operand" "rm")
+ (const_int 0)))
+ (set (match_operand:SI 0 "register_operand" "=r")
+ (minus:SI (const_int 31)
+ (clz:SI (match_dup 1))))
+ (unspec [(match_operand:SI 2 "register_operand" "0")]
+ UNSPEC_INSN_FALSE_DEP)]
+ ""
+ "bsr{l}\t{%1, %0|%0, %1}"
[(set_attr "type" "alu1")
(set_attr "prefix_0f" "1")
(set_attr "znver1_decode" "vector")
(set_attr "c86_decode" "vector")
(set_attr "mode" "SI")])
-(define_insn "bsr_1"
+(define_insn_and_split "bsr_1"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (const_int 31)
(clz:SI (match_operand:SI 1 "nonimmediate_operand" "rm"))))
(clobber (reg:CC FLAGS_REG))]
"!TARGET_LZCNT"
"bsr{l}\t{%1, %0|%0, %1}"
+ "&& TARGET_AVOID_FALSE_DEP_FOR_BSR && epilogue_completed
+ && optimize_function_for_speed_p (cfun)
+ && !reg_mentioned_p (operands[0], operands[1])"
+ [(parallel
+ [(set (match_dup 0)
+ (minus:SI (const_int 31)
+ (clz:SI (match_dup 1))))
+ (unspec [(match_dup 0)] UNSPEC_INSN_FALSE_DEP)
+ (clobber (reg:CC FLAGS_REG))])]
+ "ix86_expand_clear (operands[0]);"
[(set_attr "type" "alu1")
(set_attr "prefix_0f" "1")
(set_attr "znver1_decode" "vector")
(set_attr "c86_decode" "vector")
(set_attr "mode" "SI")])
-(define_insn "bsr_zext_1"
+(define_insn "*bsr_1_falsedep"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (minus:SI (const_int 31)
+ (clz:SI (match_operand:SI 1 "nonimmediate_operand" "rm"))))
+ (unspec [(match_operand:SI 2 "register_operand" "0")]
+ UNSPEC_INSN_FALSE_DEP)
+ (clobber (reg:CC FLAGS_REG))]
+ "!TARGET_LZCNT"
+ "bsr{l}\t{%1, %0|%0, %1}"
+ [(set_attr "type" "alu1")
+ (set_attr "prefix_0f" "1")
+ (set_attr "znver1_decode" "vector")
+ (set_attr "c86_decode" "vector")
+ (set_attr "mode" "SI")])
+
+(define_insn_and_split "bsr_zext_1"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI
(minus:SI
@@ -22553,6 +22690,35 @@ (define_insn "bsr_zext_1"
(clobber (reg:CC FLAGS_REG))]
"!TARGET_LZCNT && TARGET_64BIT"
"bsr{l}\t{%1, %k0|%k0, %1}"
+ "&& TARGET_AVOID_FALSE_DEP_FOR_BSR && epilogue_completed
+ && optimize_function_for_speed_p (cfun)
+ && !reg_mentioned_p (operands[0], operands[1])"
+ [(parallel
+ [(set (match_dup 0)
+ (zero_extend:DI
+ (minus:SI
+ (const_int 31)
+ (clz:SI (match_dup 1)))))
+ (unspec [(match_dup 0)] UNSPEC_INSN_FALSE_DEP)
+ (clobber (reg:CC FLAGS_REG))])]
+ "ix86_expand_clear (operands[0]);"
+ [(set_attr "type" "alu1")
+ (set_attr "prefix_0f" "1")
+ (set_attr "znver1_decode" "vector")
+ (set_attr "c86_decode" "vector")
+ (set_attr "mode" "SI")])
+
+(define_insn "*bsr_zext_1_falsedep"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (zero_extend:DI
+ (minus:SI
+ (const_int 31)
+ (clz:SI (match_operand:SI 1 "nonimmediate_operand" "rm")))))
+ (unspec [(match_operand:DI 2 "register_operand" "0")]
+ UNSPEC_INSN_FALSE_DEP)
+ (clobber (reg:CC FLAGS_REG))]
+ "!TARGET_LZCNT && TARGET_64BIT"
+ "bsr{l}\t{%1, %k0|%k0, %1}"
[(set_attr "type" "alu1")
(set_attr "prefix_0f" "1")
(set_attr "znver1_decode" "vector")
diff --git a/gcc/config/i386/x86-tune.def b/gcc/config/i386/x86-tune.def
index 11617220282..a219a688e7c 100644
--- a/gcc/config/i386/x86-tune.def
+++ b/gcc/config/i386/x86-tune.def
@@ -362,6 +362,12 @@ DEF_TUNE (X86_TUNE_USE_BT, "use_bt",
| m_AMD_MULTIPLE | m_ZHAOXIN | m_GOLDMONT | m_GOLDMONT_PLUS
| m_TREMONT | m_CORE_HYBRID | m_CORE_ATOM | m_C86_4G | m_GENERIC)
+/* X86_TUNE_AVOID_FALSE_DEP_FOR_BSR: BSR leaves its destination unmodified
+ for a zero source, so the hardware treats it as an input. */
+DEF_TUNE (X86_TUNE_AVOID_FALSE_DEP_FOR_BSR, "avoid_false_dep_for_bsr",
+ m_NEHALEM | m_SANDYBRIDGE | m_CORE_AVX2 | m_TREMONT | m_CORE_HYBRID
+ | m_CORE_ATOM | m_ZNVER4 | m_ZNVER5 | m_ZNVER6 | m_GENERIC)
+
/* X86_TUNE_AVOID_FALSE_DEP_FOR_BMI: Avoid false dependency
for bit-manipulation instructions. */
DEF_TUNE (X86_TUNE_AVOID_FALSE_DEP_FOR_BMI, "avoid_false_dep_for_bmi",
diff --git a/gcc/testsuite/gcc.target/i386/pr128662.c
b/gcc/testsuite/gcc.target/i386/pr128662.c
new file mode 100644
index 00000000000..fae9c0ae03a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr128662.c
@@ -0,0 +1,26 @@
+/* PR target/128662 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-lzcnt -masm=att -mtune-ctrl=avoid_false_dep_for_bsr"
} */
+
+#define BITS (__SIZEOF_LONG__ * __CHAR_BIT__)
+
+unsigned long
+f1 (unsigned long x)
+{
+ return BITS - 1 - __builtin_clzl (x);
+}
+
+unsigned long
+f2 (unsigned long x)
+{
+ return __builtin_clzl (x) ^ (BITS - 1);
+}
+
+/* Zero-extending 32-bit variant (bsr_zext_1 on 64-bit targets). */
+unsigned long
+f3 (unsigned int x)
+{
+ return 31 - __builtin_clz (x);
+}
+
+/* { dg-final { scan-assembler-times {xorl\t(%\w+), \1\n\tbsr[lq]\t} 3 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr78103-1.c
b/gcc/testsuite/gcc.target/i386/pr78103-1.c
index 95aea694c45..89f0282e971 100644
--- a/gcc/testsuite/gcc.target/i386/pr78103-1.c
+++ b/gcc/testsuite/gcc.target/i386/pr78103-1.c
@@ -1,6 +1,6 @@
/* PR target/78103 */
/* { dg-do compile { target { ! ia32 } } } */
-/* { dg-options "-O2 -mno-lzcnt" } */
+/* { dg-options "-O2 -mno-lzcnt -mtune-ctrl=^avoid_false_dep_for_bsr" } */
/* { dg-final { scan-assembler-not {\mcltq\M} } } */
long long
diff --git a/gcc/testsuite/gcc.target/i386/pr78103-2.c
b/gcc/testsuite/gcc.target/i386/pr78103-2.c
index 0183554389f..d5fb9303368 100644
--- a/gcc/testsuite/gcc.target/i386/pr78103-2.c
+++ b/gcc/testsuite/gcc.target/i386/pr78103-2.c
@@ -1,6 +1,6 @@
/* PR target/78103 */
/* { dg-do compile } */
-/* { dg-options "-O2 -mno-lzcnt" } */
+/* { dg-options "-O2 -mno-lzcnt -mtune-ctrl=^avoid_false_dep_for_bsr" } */
/* Cope with --enable-frame-pointer. */
/* { dg-additional-options "-fomit-frame-pointer" } */
/* { dg-final { scan-assembler-not {\mmovl\M} } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr78103-3.c
b/gcc/testsuite/gcc.target/i386/pr78103-3.c
index b8d82312a0e..69eb836d508 100644
--- a/gcc/testsuite/gcc.target/i386/pr78103-3.c
+++ b/gcc/testsuite/gcc.target/i386/pr78103-3.c
@@ -1,6 +1,6 @@
/* PR target/78103 */
/* { dg-do compile { target { ! ia32 } } } */
-/* { dg-options "-O2 -mno-lzcnt" } */
+/* { dg-options "-O2 -mno-lzcnt -mtune-ctrl=^avoid_false_dep_for_bsr" } */
/* { dg-final { scan-assembler-not {\mmovl\M} } } */
/* { dg-final { scan-assembler-not {\mmovslq\M} } } */
/* { dg-final { scan-assembler-not {\mxor[lq]\M} } } */