gcc/ChangeLog:

        * config/loongarch/loongarch.cc
        (loongarch_compute_frame_info): Do not compute the saved
        registers masks for naked functions.
        (loongarch_naked_function_p): New function.
        (loongarch_expand_prologue): Do not emit a prologue for
        naked functions and report a zero static stack size.
        (loongarch_expand_epilogue): Do not emit an epilogue for
        naked functions.
        (loongarch_function_ok_for_sibcall): Return false for naked
        functions.
        (loongarch_allocate_stack_slots_for_args): New function.
        (loongarch_warn_func_return): New function.
        (loongarch_set_current_function): Record whether the current
        function is naked in machine_function.
        (loongarch_handle_fndecl_attribute): New function.
        (TARGET_GNU_ATTRIBUTES): Add naked attribute.
        (TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS): Define.
        (TARGET_WARN_FUNC_RETURN): Define.
        * config/loongarch/loongarch.h (machine_function):
        Add naked_p field.
        * config/loongarch/loongarch.md (*<optab>): Emit no assembly
        for the return instruction of a naked function.
        * doc/extend.texi (Common Attributes): Document LoongArch
        support for the naked attribute.

gcc/testsuite/ChangeLog:

        * gcc.target/loongarch/naked-1.c: New test.
        * gcc.target/loongarch/naked-2.c: New test.

---
 gcc/config/loongarch/loongarch.cc            | 123 ++++++++++++++++---
 gcc/config/loongarch/loongarch.h             |   3 +
 gcc/config/loongarch/loongarch.md            |   5 +
 gcc/doc/extend.texi                          |   3 +-
 gcc/testsuite/gcc.target/loongarch/naked-1.c |  14 +++
 gcc/testsuite/gcc.target/loongarch/naked-2.c |  17 +++
 6 files changed, 147 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/loongarch/naked-1.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/naked-2.c

diff --git a/gcc/config/loongarch/loongarch.cc 
b/gcc/config/loongarch/loongarch.cc
index b0f7abc0483..bfb9022057a 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -902,23 +902,28 @@ loongarch_compute_frame_info (void)
   frame = &cfun->machine->frame;
   memset (frame, 0, sizeof (*frame));
 
-  /* Find out which GPRs we need to save.  */
-  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
-    if (loongarch_save_reg_p (regno))
-      frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++;
-
-  /* If this function calls eh_return, we must also save and restore the
-     EH data registers.  */
-  if (crtl->calls_eh_return)
-    for (i = 0; (regno = EH_RETURN_DATA_REGNO (i)) != INVALID_REGNUM; i++)
-      frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++;
-
-  /* Find out which FPRs we need to save.  This loop must iterate over
-     the same space as its companion in loongarch_for_each_saved_reg.  */
-  if (TARGET_HARD_FLOAT)
-    for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
-      if (loongarch_save_reg_p (regno))
-       frame->fmask |= 1 << (regno - FP_REG_FIRST), num_f_saved++;
+  /* Naked functions do not save any registers: the asm body manages the
+     register and stack state itself.  */
+  if (!cfun->machine->naked_p)
+    {
+      /* Find out which GPRs we need to save.  */
+      for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+       if (loongarch_save_reg_p (regno))
+         frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++;
+
+      /* If this function calls eh_return, we must also save and restore the
+        EH data registers.  */
+      if (crtl->calls_eh_return)
+       for (i = 0; (regno = EH_RETURN_DATA_REGNO (i)) != INVALID_REGNUM; i++)
+         frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++;
+
+      /* Find out which FPRs we need to save.  This loop must iterate over
+        the same space as its companion in loongarch_for_each_saved_reg.  */
+      if (TARGET_HARD_FLOAT)
+       for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
+         if (loongarch_save_reg_p (regno))
+           frame->fmask |= 1 << (regno - FP_REG_FIRST), num_f_saved++;
+    }
 
   /* At the bottom of the frame are any outgoing stack arguments.  */
   offset = LARCH_STACK_ALIGN (crtl->outgoing_args_size);
@@ -1288,6 +1293,16 @@ loongarch_output_probe_stack_range (rtx reg1, rtx reg2, 
rtx reg3)
   return "";
 }
 
+/* Return true if FUNC is a naked function.  */
+
+static bool
+loongarch_naked_function_p (tree func)
+{
+  if (func == NULL_TREE)
+    func = current_function_decl;
+  return lookup_attribute ("naked", DECL_ATTRIBUTES (func)) != NULL;
+}
+
 /* Expand the "prologue" pattern.  */
 
 void
@@ -1300,6 +1315,15 @@ loongarch_expand_prologue (void)
   if (flag_stack_usage_info)
     current_function_static_stack_size = size;
 
+  /* Naked functions have no prologue: the asm body takes care of the
+     stack itself.  */
+  if (loongarch_naked_function_p (current_function_decl))
+    {
+      if (flag_stack_usage_info)
+       current_function_static_stack_size = 0;
+      return;
+    }
+
   /* Save the registers.  */
   if ((frame->mask | frame->fmask) != 0)
     {
@@ -1453,6 +1477,16 @@ loongarch_expand_epilogue (int style)
   bool need_barrier_p
     = (get_frame_size () + cfun->machine->frame.arg_pointer_offset) != 0;
 
+  /* Naked functions have no epilogue either.  The compiler still needs
+     a return insn to terminate the RTL, but its assembly output is
+     suppressed below so the asm body fully controls the return.  */
+  if (loongarch_naked_function_p (current_function_decl))
+    {
+      gcc_assert (style == NORMAL_RETURN);
+      emit_jump_insn (gen_return ());
+      return;
+    }
+
   /* Handle simple returns.  */
   if (style == NORMAL_RETURN && loongarch_can_use_return_insn ())
     {
@@ -6090,10 +6124,33 @@ static bool
 loongarch_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
                                   tree exp ATTRIBUTE_UNUSED)
 {
+  /* Don't use sibcall for naked functions.  */
+  if (loongarch_naked_function_p (current_function_decl))
+    return false;
+
   /* Always OK.  */
   return true;
 }
 
+/* Implement TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS.  */
+
+static bool
+loongarch_allocate_stack_slots_for_args (void)
+{
+  /* Naked functions should not allocate stack slots for arguments.  */
+  return !loongarch_naked_function_p (current_function_decl);
+}
+
+/* Implement TARGET_WARN_FUNC_RETURN.  */
+
+static bool
+loongarch_warn_func_return (tree decl)
+{
+  /* Naked functions are implemented entirely in assembly, including the
+     return sequence, so suppress warnings about this.  */
+  return !loongarch_naked_function_p (decl);
+}
+
 static machine_mode
 loongarch_mode_for_move_size (HOST_WIDE_INT size)
 {
@@ -8369,6 +8426,11 @@ loongarch_set_current_function (tree fndecl)
 
   loongarch_previous_fndecl = fndecl;
 
+  /* Only query decl attributes for real functions; during error
+     recovery the hook can be invoked with an error_mark_node.  */
+  if (fndecl != error_mark_node && cfun && cfun->machine)
+    cfun->machine->naked_p = loongarch_naked_function_p (fndecl);
+
   if (new_tree != old_tree)
     /* According to the settings of the functions attribute and pragma,
        the options is corrected.  */
@@ -8643,6 +8705,25 @@ loongarch_expand_vselect_vconcat (rtx target, rtx op0, 
rtx op1,
   return loongarch_expand_vselect (target, x, perm, nelt, testing_p);
 }
 
+/* Handle an attribute requiring a FUNCTION_DECL;
+   arguments as in struct attribute_spec.handler.  */
+
+static tree
+loongarch_handle_fndecl_attribute (tree *node, tree name,
+                                  tree args ATTRIBUTE_UNUSED,
+                                  int flags ATTRIBUTE_UNUSED,
+                                  bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+              name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 static tree
 loongarch_handle_model_attribute (tree *node, tree name, tree arg, int,
                                  bool *no_add_attrs)
@@ -8716,6 +8797,9 @@ TARGET_GNU_ATTRIBUTES (loongarch_attribute_table,
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
+  /* The attribute telling no prologue/epilogue.  */
+  { "naked", 0, 0, true, false, false, false,
+    loongarch_handle_fndecl_attribute, NULL },
   { "model", 1, 1, true, false, false, false,
     loongarch_handle_model_attribute, NULL }
 });
@@ -12241,6 +12325,11 @@ loongarch_output_asm_load_canary (rtx reg, rtx canary, 
rtx tmp)
 
 #undef TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL loongarch_function_ok_for_sibcall
+#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
+#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS \
+  loongarch_allocate_stack_slots_for_args
+#undef TARGET_WARN_FUNC_RETURN
+#define TARGET_WARN_FUNC_RETURN loongarch_warn_func_return
 
 #undef TARGET_VALID_POINTER_MODE
 #define TARGET_VALID_POINTER_MODE loongarch_valid_pointer_mode
diff --git a/gcc/config/loongarch/loongarch.h b/gcc/config/loongarch/loongarch.h
index 3339040a433..f705c2937dd 100644
--- a/gcc/config/loongarch/loongarch.h
+++ b/gcc/config/loongarch/loongarch.h
@@ -1254,6 +1254,9 @@ struct GTY (()) machine_function
      This area is allocated by the callee at the very top of the frame.  */
   int varargs_size;
 
+  /* True if current function is a naked function.  */
+  bool naked_p;
+
   /* The current frame information, calculated by loongarch_compute_frame_info.
    */
   struct loongarch_frame_info frame;
diff --git a/gcc/config/loongarch/loongarch.md 
b/gcc/config/loongarch/loongarch.md
index ecf1563270f..a0ae750cf11 100644
--- a/gcc/config/loongarch/loongarch.md
+++ b/gcc/config/loongarch/loongarch.md
@@ -4044,6 +4044,11 @@ (define_insn "*<optab>"
   [(any_return)]
   ""
 {
+  /* Naked functions must provide their own return sequence, so make
+     the compiler-generated return emit no assembly.  */
+  if (cfun->machine->naked_p)
+    return "";
+
   operands[0] = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
   return "jr\t%0";
 }
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 88213d25a89..d5980487398 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3685,7 +3685,8 @@ may appear to work, they cannot be depended upon to work 
reliably and
 are not supported.
 
 Not all targets support this attribute.  Those that do include ARC, ARM,
-AVR, BPF, C-SKY, MCORE, MSP430, NDS32, RISC-V, RL78, RX, and x86.
+AVR, BPF, C-SKY, LoongArch, MCORE, MSP430, NDS32, RISC-V, RL78,
+RX, and x86.
 
 @atindex @code{no_icf}
 @item no_icf
diff --git a/gcc/testsuite/gcc.target/loongarch/naked-1.c 
b/gcc/testsuite/gcc.target/loongarch/naked-1.c
new file mode 100644
index 00000000000..801ed4f7c1b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/naked-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+/* Verify that __attribute__((naked)) produces a function with neither
+   a prologue nor an epilogue.  */
+void
+__attribute__((naked))
+foo (void)
+{
+  __asm__ ("# naked");
+}
+
+/* { dg-final { scan-assembler "# naked" } } */
+/* { dg-final { scan-assembler-not "(?n)^\\s*jr\\t\\\$r1" } } */
diff --git a/gcc/testsuite/gcc.target/loongarch/naked-2.c 
b/gcc/testsuite/gcc.target/loongarch/naked-2.c
new file mode 100644
index 00000000000..c640aff9cd5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/naked-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+/* Verify that a naked function does not spill incoming arguments onto the
+   stack and does not build a frame or emit a return sequence, even when
+   there are more arguments than argument registers.  */
+void
+__attribute__((naked))
+foo (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j)
+{
+  __asm__ volatile ("# naked-many-args");
+}
+
+/* { dg-final { scan-assembler "# naked-many-args" } } */
+/* { dg-final { scan-assembler-not "(?n)^\\s*addi\\." } } */
+/* { dg-final { scan-assembler-not "(?n)^\\s*st\\." } } */
+/* { dg-final { scan-assembler-not "(?n)^\\s*jr\\t\\\$r1" } } */
-- 
2.20.1

Reply via email to