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