Add call-site LPAD insertion for two cases:

1. setjmp / __attribute__((returns_twice)) calls, which may return a
   second time via longjmp.
2. A new "indirect_return" attribute for functions that may return to
   an unexpected address.

Detection uses riscv_call_needs_lpad_p() at expand time.  When needed,
call_internal_cfi / call_value_internal_cfi emit .p2align 2,
.option push/norelax/norvc, the call, .option pop, and lpad 0 as a
single insn.  This prevents the assembler (c.jal) or linker (jal
relaxation) from shifting the return address off the lpad.

Indirect calls to returns_twice or indirect_return functions are not
covered.

Changes in v2:
- Change indirect_return from a type attribute to a decl attribute,
  consistent with returns_twice.
- Add length attributes to call_internal_cfi and call_value_internal_cfi
  to reflect the multi-instruction sequence.
- Copy the explaining comment to call_value_internal_cfi.

gcc/ChangeLog:

        * config/riscv/riscv-protos.h (riscv_call_needs_lpad_p): Declare.
        * config/riscv/riscv.cc (riscv_gnu_attributes): Register new
        indirect_return attribute for function declarations.
        (riscv_call_needs_lpad_p): New function.
        * config/riscv/riscv.md (call_internal_cfi): New insn pattern with
        length attribute.
        (call_value_internal_cfi): Likewise for call-with-return-value,
        with comment and length attribute.
        (define_expand "call"): Emit call_internal_cfi when
        riscv_call_needs_lpad_p returns true.
        (define_expand "call_value"): Likewise.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/zicfilp-indirect-return.c: New test.
        * gcc.target/riscv/zicfilp-setjmp.c: New test.
---
 gcc/config/riscv/riscv-protos.h               |  1 +
 gcc/config/riscv/riscv.cc                     | 26 ++++++
 gcc/config/riscv/riscv.md                     | 80 ++++++++++++++++++-
 .../riscv/zicfilp-indirect-return.c           | 30 +++++++
 .../gcc.target/riscv/zicfilp-setjmp.c         | 26 ++++++
 5 files changed, 159 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/zicfilp-indirect-return.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c

diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index fa360157570..f429ca86a8f 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -156,6 +156,7 @@ extern rtx riscv_emit_binary (enum rtx_code code, rtx dest, 
rtx x, rtx y);
 #endif
 extern bool riscv_expand_conditional_move (rtx, rtx, rtx, rtx);
 extern rtx riscv_legitimize_call_address (rtx);
+extern bool riscv_call_needs_lpad_p (rtx);
 extern bool riscv_expand_zilsd_misaligned_move (rtx, rtx);
 extern bool riscv_zilsd_valid_mem_p (rtx, machine_mode);
 extern void riscv_set_return_address (rtx, rtx);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index ab50549d4e0..645796a681f 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -1030,6 +1030,10 @@ static const attribute_spec riscv_gnu_attributes[] =
     standard vector calling convention variant.  Syntax:
     __attribute__((norelax)). */
   {"norelax", 0, 0, true, false, false, false, NULL, NULL},
+  /* Marks functions that may return indirectly.  With Zicfilp, the compiler
+     inserts a landing-pad after calls to such functions.  Syntax:
+     __attribute__((indirect_return)).  */
+  {"indirect_return", 0, 0, true, false, false, false, NULL, NULL},
 };
 
 static const scoped_attribute_specs riscv_gnu_attribute_table  =
@@ -8076,6 +8080,28 @@ riscv_legitimize_call_address (rtx addr)
   return addr;
 }
 
+/* Return true if a Zicfilp landing-pad must follow a call to ADDR.
+   Indirect calls to returns_twice or indirect_return functions are not
+   covered.  */
+
+bool
+riscv_call_needs_lpad_p (rtx addr)
+{
+  if (!is_zicfilp_p () || GET_CODE (addr) != SYMBOL_REF)
+    return false;
+
+  tree decl = SYMBOL_REF_DECL (addr);
+  if (!decl || TREE_CODE (decl) != FUNCTION_DECL)
+    return false;
+
+  /* Use setjmp_call_p to cover both ECF_RETURNS_TWICE builtins (e.g. the
+     C-library setjmp) and explicit __attribute__((returns_twice)).  */
+  if (setjmp_call_p (decl))
+    return true;
+
+  return lookup_attribute ("indirect_return", DECL_ATTRIBUTES (decl)) != NULL;
+}
+
 /* Print symbolic operand OP, which is part of a HIGH or LO_SUM
    in context CONTEXT.  HI_RELOC indicates a high-part reloc.  */
 
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 2055e5c4a9d..5231610b054 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -4190,8 +4190,12 @@
              (use (match_operand 2 ""))])]
   ""
 {
-  rtx target = riscv_legitimize_call_address (XEXP (operands[0], 0));
-  emit_call_insn (gen_call_internal (target, operands[1]));
+  rtx addr = XEXP (operands[0], 0);
+  rtx target = riscv_legitimize_call_address (addr);
+  if (riscv_call_needs_lpad_p (addr))
+    emit_call_insn (gen_call_internal_cfi (target, operands[1]));
+  else
+    emit_call_insn (gen_call_internal (target, operands[1]));
   DONE;
 })
 
@@ -4206,6 +4210,37 @@
    call\t%0@plt"
   [(set_attr "type" "call")])
 
+;; Zicfilp-protected call: .option push/pop guards prevent c.jal compression
+;; and jal linker relaxation from moving the return address off the lpad.
+;; .p2align 2 ensures the lpad is 4-byte aligned.
+(define_insn "call_internal_cfi"
+  [(call (mem:SI (match_operand 0 "call_insn_operand" "l,S,U"))
+        (match_operand 1 "" ""))
+   (clobber (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZICFILP"
+  {
+    output_asm_insn (".p2align\t2", operands);
+    output_asm_insn (".option push", operands);
+    output_asm_insn (".option norelax", operands);
+    output_asm_insn (".option norvc", operands);
+    switch (which_alternative)
+      {
+      case 0:
+       output_asm_insn ("jalr\t%0", operands);
+       break;
+      case 1:
+       output_asm_insn ("call\t%0", operands);
+       break;
+      default:
+       output_asm_insn ("call\t%0@plt", operands);
+       break;
+      }
+    output_asm_insn (".option pop", operands);
+    return "lpad\t0";
+  }
+  [(set_attr "type" "call")
+   (set_attr "length" "8,12,12")])
+
 (define_expand "call_value"
   [(parallel [(set (match_operand 0 "")
                   (call (match_operand 1 "")
@@ -4213,8 +4248,13 @@
              (use (match_operand 3 ""))])]
   ""
 {
-  rtx target = riscv_legitimize_call_address (XEXP (operands[1], 0));
-  emit_call_insn (gen_call_value_internal (operands[0], target, operands[2]));
+  rtx addr = XEXP (operands[1], 0);
+  rtx target = riscv_legitimize_call_address (addr);
+  if (riscv_call_needs_lpad_p (addr))
+    emit_call_insn (gen_call_value_internal_cfi (operands[0], target,
+                                                operands[2]));
+  else
+    emit_call_insn (gen_call_value_internal (operands[0], target, 
operands[2]));
   DONE;
 })
 
@@ -4230,6 +4270,38 @@
    call\t%1@plt"
   [(set_attr "type" "call")])
 
+;; Zicfilp-protected call: .option push/pop guards prevent c.jal compression
+;; and jal linker relaxation from moving the return address off the lpad.
+;; .p2align 2 ensures the lpad is 4-byte aligned.
+(define_insn "call_value_internal_cfi"
+  [(set (match_operand 0 "" "")
+       (call (mem:SI (match_operand 1 "call_insn_operand" "l,S,U"))
+             (match_operand 2 "" "")))
+   (clobber (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZICFILP"
+  {
+    output_asm_insn (".p2align\t2", operands);
+    output_asm_insn (".option push", operands);
+    output_asm_insn (".option norelax", operands);
+    output_asm_insn (".option norvc", operands);
+    switch (which_alternative)
+      {
+      case 0:
+       output_asm_insn ("jalr\t%1", operands);
+       break;
+      case 1:
+       output_asm_insn ("call\t%1", operands);
+       break;
+      default:
+       output_asm_insn ("call\t%1@plt", operands);
+       break;
+      }
+    output_asm_insn (".option pop", operands);
+    return "lpad\t0";
+  }
+  [(set_attr "type" "call")
+   (set_attr "length" "8,12,12")])
+
 ;; Call subroutine returning any type.
 
 (define_expand "untyped_call"
diff --git a/gcc/testsuite/gcc.target/riscv/zicfilp-indirect-return.c 
b/gcc/testsuite/gcc.target/riscv/zicfilp-indirect-return.c
new file mode 100644
index 00000000000..230d865d126
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zicfilp-indirect-return.c
@@ -0,0 +1,30 @@
+/* Test RISC-V Zicfilp indirect_return attribute functionality.  */
+/* { dg-do compile { target { riscv64*-*-* } } } */
+/* { dg-options "-O2 -march=rv64gc_zicfilp -mabi=lp64d -fcf-protection=none 
-fcf-protection=branch -fno-inline" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-g" "-flto" } } */
+/* { dg-final { check-function-bodies "**" "" {\.p2align|\.option|call|lpad} } 
} */
+
+/* Function marked with indirect_return attribute.  */
+int __attribute__((indirect_return, noinline)) indirect_func (int x)
+{
+  return x * 2 + 1;
+}
+
+/*
+** test_indirect_call:
+**     ...
+**     .p2align        2
+**     .option push
+**     .option norelax
+**     .option norvc
+**     call    indirect_func.*
+**     .option pop
+**     lpad    \d+
+**     ...
+*/
+int test_indirect_call (int x)
+{
+  /* Use the result to prevent tail call optimization.  */
+  int result = indirect_func (x);
+  return result + 1;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c 
b/gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c
new file mode 100644
index 00000000000..9d90c14db63
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c
@@ -0,0 +1,26 @@
+/* Test RISC-V Zicfilp setjmp call protection functionality.  */
+/* { dg-do compile { target { riscv64*-*-* } } } */
+/* { dg-options "-O2 -march=rv64gc_zicfilp -mabi=lp64d -fcf-protection=none 
-fcf-protection=branch" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-g" "-flto" } } */
+/* { dg-final { check-function-bodies "**" "" {\.p2align|\.option|call|lpad} } 
} */
+
+#include <setjmp.h>
+
+jmp_buf test_env;
+
+/*
+** test_setjmp_call:
+**     ...
+**     .p2align        2
+**     .option push
+**     .option norelax
+**     .option norvc
+**     call    _?setjmp.*
+**     .option pop
+**     lpad    \d+
+**     ...
+*/
+int test_setjmp_call (void)
+{
+  return setjmp (test_env);
+}
-- 
2.54.0

Reply via email to