(const_int 12)))
;; Jumps further than +/- 1 MiB require two instructions.
+ ;; Also, jumps that cross section boundaries (e.g., from hot to cold
+ ;; section when -freorder-blocks-and-partition is used) require two
+ ;; instructions because the linker may place the sections far apart.
(eq_attr "type" "jump")
- (if_then_else (and (le (minus (match_dup 0) (pc))
- (const_int 1048568))
- (le (minus (pc) (match_dup 0))
- (const_int 1048572)))
- (const_int 4)
- (const_int 8))
+ (if_then_else (match_test "CROSSING_JUMP_P (insn)")
+ (const_int 8)
+ (if_then_else (and (le (minus (match_dup 0) (pc))
+ (const_int 1048568))
+ (le (minus (pc) (match_dup 0))
+ (const_int 1048572)))
+ (const_int 4)
+ (const_int 8)))
;; Conservatively assume calls take two instructions (AUIPC + JALR).
;; The linker will opportunistically relax the sequence to JAL.
[(set (pc) (label_ref (match_operand 0 "" "")))]
""
{
- /* Hopefully this does not happen often as this is going
- to clobber $ra and muck up the return stack predictors. */
+ /* Use the long form (AUIPC+JALR) if the jump distance exceeds 1 MiB,
+ or if the jump crosses section boundaries (e.g., from hot to cold
+ section when -freorder-blocks-and-partition is used).
+ Note: This clobbers $ra and mucks up the return stack predictors. */
if (get_attr_length (insn) == 8)
return "jump\t%l0,ra";
--- /dev/null
+/* Test that jumps crossing section boundaries use the long form (AUIPC+JALR).
+ When -freorder-blocks-and-partition is used, cold code is placed in
+ .text.unlikely section. Jumps from hot code (.text) to cold code
+ (.text.unlikely) must use the long form because the linker may place
+ these sections more than 1MB apart, exceeding the JAL instruction's range.
+
+ This test verifies that CROSSING_JUMP_P is handled correctly in the
+ RISC-V backend. */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target freorder } */
+/* { dg-options "-O2 -freorder-blocks-and-partition" } */
+
+extern void abort (void);
+
+/* Force the error path to be cold. */
+static void __attribute__((cold, noinline))
+handle_error (void)
+{
+ abort ();
+}
+
+/* Function with hot and cold paths. */
+int
+check_positive (int x)
+{
+ if (__builtin_expect (x > 0, 1))
+ {
+ /* Hot path - stays in .text */
+ return x;
+ }
+ /* Cold path - moved to .text.unlikely */
+ handle_error ();
+ return -1;
+}
+
+/* The jump to the cold section should use "jump" (AUIPC+JALR) not "j" (JAL).
+ We check for "jump" instruction which is the long form. */
+
+/* { dg-final { scan-assembler "jump\\t\\.L\[0-9\]+,ra" } } */
+
--- /dev/null
+/* Test that multiple crossing jumps all use the long form.
+ This is a more comprehensive test for CROSSING_JUMP_P handling. */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target freorder } */
+/* { dg-options "-O2 -freorder-blocks-and-partition" } */
+
+extern void abort (void);
+
+static void __attribute__((cold, noinline))
+cold_path_1 (void)
+{
+ abort ();
+}
+
+static void __attribute__((cold, noinline))
+cold_path_2 (void)
+{
+ abort ();
+}
+
+/* Function with multiple cold paths. */
+int
+validate (int a, int b)
+{
+ if (__builtin_expect (a > 0, 1))
+ {
+ if (__builtin_expect (b > 0, 1))
+ {
+ /* Hot path */
+ return a + b;
+ }
+ /* Cold path 1 */
+ cold_path_1 ();
+ }
+ /* Cold path 2 */
+ cold_path_2 ();
+ return -1;
+}
+
+/* All jumps to cold sections should use "jump" (AUIPC+JALR).
+ We expect at least 2 crossing jumps. */
+
+/* { dg-final { scan-assembler-times "jump\\t\\.L\[0-9\]+,ra" 2 } } */
+
--- /dev/null
+/* Test that jumps within the same section still use the short form (JAL).
+ This ensures that the CROSSING_JUMP_P fix doesn't pessimize normal jumps. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+int global;
+
+/* Simple function with a loop - jumps stay within .text section. */
+int
+sum_to_n (int n)
+{
+ int sum = 0;
+ for (int i = 1; i <= n; i++)
+ sum += i;
+ return sum;
+}
+
+/* Function with conditional - jumps stay within .text section. */
+int
+abs_value (int x)
+{
+ if (x < 0)
+ return -x;
+ return x;
+}
+
+/* The backward jump in the loop should use "j" (JAL) not "jump" (AUIPC+JALR)
+ since it doesn't cross section boundaries. We verify that "jump" is NOT
+ used for intra-section jumps. */
+
+/* { dg-final { scan-assembler-not "jump\\t\\.L\[0-9\]+,ra" } } */
+