diff mbox series

RISC-V: Add naked function support.

Message ID 20180110230629.22201-1-jimw@sifive.com
State New
Headers show
Series RISC-V: Add naked function support. | expand

Commit Message

Jim Wilson Jan. 10, 2018, 11:06 p.m. UTC
This adds naked function support to the RISC-V port.  The basic structure was
copied from other ports, so there should be nothing unexpected here.

This was tested with a riscv64-linux build and make check.  There were no
regressions.  I also hand checked the info docs to make sure the extend.texi
change looked OK.

	2018-01-10  Kito Cheng  <kito.cheng@gmail.com>

	gcc/
	* config/riscv/riscv-protos.h (riscv_output_return): New.
	* config/riscv/riscv.c (struct machine_function): New naked_p field.
	(riscv_attribute_table, riscv_output_return),
	(riscv_handle_fndecl_attribute, riscv_naked_function_p),
	(riscv_allocate_stack_slots_for_args, riscv_warn_func_return): New.
	(riscv_compute_frame_info): Only compute frame->mask if not a naked
	function.
	(riscv_expand_prologue): Add early return for naked function.
	(riscv_expand_epilogue): Likewise.
	(riscv_function_ok_for_sibcall): Return false for naked function.
	(riscv_set_current_function): New.
	(TARGET_SET_CURRENT_FUNCTION, TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS),
	(TARGET_ATTRIBUTE_TABLE, TARGET_WARN_FUNC_RETURN): New.
	* config/riscv/riscv.md (simple_return): Call riscv_output_return.
	* doc/extend.texi (RISC-V Function Attributes): New.
---
 gcc/config/riscv/riscv-protos.h |   1 +
 gcc/config/riscv/riscv.c        | 157 +++++++++++++++++++++++++++++++++++-----
 gcc/config/riscv/riscv.md       |   4 +-
 gcc/doc/extend.texi             |  19 +++++
 4 files changed, 163 insertions(+), 18 deletions(-)
diff mbox series

Patch

diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 1cf016d850b..0538ede77e4 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -54,6 +54,7 @@  extern bool riscv_split_64bit_move_p (rtx, rtx);
 extern void riscv_split_doubleword_move (rtx, rtx);
 extern const char *riscv_output_move (rtx, rtx);
 extern const char *riscv_output_gpr_save (unsigned);
+extern const char *riscv_output_return ();
 #ifdef RTX_CODE
 extern void riscv_expand_int_scc (rtx, enum rtx_code, rtx, rtx);
 extern void riscv_expand_float_scc (rtx, enum rtx_code, rtx, rtx);
diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c
index b6270f7bfd7..d260c0ebae1 100644
--- a/gcc/config/riscv/riscv.c
+++ b/gcc/config/riscv/riscv.c
@@ -127,6 +127,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 riscv_compute_frame_info.  */
   struct riscv_frame_info frame;
 };
@@ -269,6 +272,23 @@  static const struct riscv_tune_info optimize_size_tune_info = {
   false,					/* slow_unaligned_access */
 };
 
+static tree riscv_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
+
+/* Defining target-specific uses of __attribute__.  */
+static const struct attribute_spec riscv_attribute_table[] =
+{
+  /* Syntax: { name, min_len, max_len, decl_required, type_required,
+	       function_type_required, affects_type_identity, handler,
+	       exclude } */
+
+  /* The attribute telling no prologue/epilogue.  */
+  { "naked",	0,  0, true, false, false, false,
+    riscv_handle_fndecl_attribute, NULL },
+
+  /* The last attribute spec is set to be NULL.  */
+  { NULL,	0,  0, false, false, false, false, NULL, NULL }
+};
+
 /* A table describing all the processors GCC knows about.  */
 static const struct riscv_cpu_info riscv_cpu_info_table[] = {
   { "rocket", &rocket_tune_info },
@@ -1827,6 +1847,16 @@  riscv_output_move (rtx dest, rtx src)
     }
   gcc_unreachable ();
 }
+
+const char *
+riscv_output_return ()
+{
+  if (cfun->machine->naked_p)
+    return "";
+
+  return "ret";
+}
+
 
 /* Return true if CMP1 is a suitable second operand for integer ordering
    test CODE.  See also the *sCC patterns in riscv.md.  */
@@ -2647,6 +2677,50 @@  riscv_setup_incoming_varargs (cumulative_args_t cum, machine_mode mode,
     cfun->machine->varargs_size = gp_saved * UNITS_PER_WORD;
 }
 
+/* Handle an attribute requiring a FUNCTION_DECL;
+   arguments as in struct attribute_spec.handler.  */
+static tree
+riscv_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;
+}
+
+/* Return true if func is a naked function.  */
+static bool
+riscv_naked_function_p (tree func)
+{
+  tree func_decl = func;
+  if (func == NULL_TREE)
+    func_decl = current_function_decl;
+  return NULL_TREE != lookup_attribute ("naked", DECL_ATTRIBUTES (func_decl));
+}
+
+/* Implement TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS.  */
+static bool
+riscv_allocate_stack_slots_for_args ()
+{
+  /* Naked functions should not allocate stack slots for arguments.  */
+  return !riscv_naked_function_p (current_function_decl);
+}
+
+/* Implement TARGET_WARN_FUNC_RETURN.  */
+static bool
+riscv_warn_func_return (tree decl)
+{
+  /* Naked functions are implemented entirely in assembly, including the
+     return sequence, so suppress warnings about this.  */
+  return !riscv_naked_function_p (decl);
+}
+
 /* Implement TARGET_EXPAND_BUILTIN_VA_START.  */
 
 static void
@@ -3202,23 +3276,26 @@  riscv_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 (riscv_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 riscv_for_each_saved_reg.  */
-  if (TARGET_HARD_FLOAT)
-    for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
-      if (riscv_save_reg_p (regno))
-	frame->fmask |= 1 << (regno - FP_REG_FIRST), num_f_saved++;
+  if (!cfun->machine->naked_p)
+    {
+      /* Find out which GPRs we need to save.  */
+      for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+	if (riscv_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 riscv_for_each_saved_reg.  */
+      if (TARGET_HARD_FLOAT)
+	for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
+	  if (riscv_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 = crtl->outgoing_args_size;
@@ -3488,6 +3565,14 @@  riscv_expand_prologue (void)
   unsigned mask = frame->mask;
   rtx insn;
 
+  if (cfun->machine->naked_p)
+    {
+      if (flag_stack_usage_info)
+	current_function_static_stack_size = 0;
+
+      return;
+    }
+
   if (flag_stack_usage_info)
     current_function_static_stack_size = size;
 
@@ -3600,6 +3685,15 @@  riscv_expand_epilogue (bool sibcall_p)
   bool need_barrier_p = (get_frame_size ()
 			 + cfun->machine->frame.arg_pointer_offset) != 0;
 
+  if (cfun->machine->naked_p)
+    {
+      gcc_assert (!sibcall_p);
+
+      emit_jump_insn (gen_return ());
+
+      return;
+    }
+
   if (!sibcall_p && riscv_can_use_return_insn ())
     {
       emit_jump_insn (gen_return ());
@@ -4183,9 +4277,27 @@  riscv_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
   if (TARGET_SAVE_RESTORE)
     return false;
 
+  /* Don't use sibcall for naked function.  */
+  if (cfun->machine->naked_p)
+    return false;
+
   return true;
 }
 
+/* Implement `TARGET_SET_CURRENT_FUNCTION'.  */
+/* Sanity cheching for above function attributes.  */
+static void
+riscv_set_current_function (tree decl)
+{
+  if (decl == NULL_TREE
+      || current_function_decl == NULL_TREE
+      || current_function_decl == error_mark_node
+      || !cfun->machine)
+    return;
+
+  cfun->machine->naked_p = riscv_naked_function_p (decl);
+}
+
 /* Implement TARGET_CANNOT_COPY_INSN_P.  */
 
 static bool
@@ -4241,6 +4353,9 @@  riscv_constant_alignment (const_tree exp, HOST_WIDE_INT align)
 #undef TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL riscv_function_ok_for_sibcall
 
+#undef  TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION riscv_set_current_function
+
 #undef TARGET_REGISTER_MOVE_COST
 #define TARGET_REGISTER_MOVE_COST riscv_register_move_cost
 #undef TARGET_MEMORY_MOVE_COST
@@ -4276,6 +4391,8 @@  riscv_constant_alignment (const_tree exp, HOST_WIDE_INT align)
 
 #undef TARGET_SETUP_INCOMING_VARARGS
 #define TARGET_SETUP_INCOMING_VARARGS riscv_setup_incoming_varargs
+#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
+#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS riscv_allocate_stack_slots_for_args
 #undef TARGET_STRICT_ARGUMENT_NAMING
 #define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true
 #undef TARGET_MUST_PASS_IN_STACK
@@ -4377,6 +4494,12 @@  riscv_constant_alignment (const_tree exp, HOST_WIDE_INT align)
 #undef TARGET_CONSTANT_ALIGNMENT
 #define TARGET_CONSTANT_ALIGNMENT riscv_constant_alignment
 
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE riscv_attribute_table
+
+#undef TARGET_WARN_FUNC_RETURN
+#define TARGET_WARN_FUNC_RETURN riscv_warn_func_return
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-riscv.h"
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 99341fa00b4..bffe78dd837 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -1927,7 +1927,9 @@ 
 (define_insn "simple_return"
   [(simple_return)]
   ""
-  "ret"
+{
+  return riscv_output_return ();
+}
   [(set_attr "type"	"jump")
    (set_attr "mode"	"none")])
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 5f0f4b86cb2..84640f4fde6 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2319,6 +2319,7 @@  GCC plugins may provide their own attributes.
 * Nios II Function Attributes::
 * Nvidia PTX Function Attributes::
 * PowerPC Function Attributes::
+* RISC-V Function Attributes::
 * RL78 Function Attributes::
 * RX Function Attributes::
 * S/390 Function Attributes::
@@ -5093,6 +5094,24 @@  function that has different target options than the caller, unless the
 callee has a subset of the target options of the caller.
 @end table
 
+@node RISC-V Function Attributes
+@subsection RISC-V Function Attributes
+
+These function attributes are supported by the RISC-V back end:
+
+@table @code
+@item naked
+@cindex @code{naked} function attribute, RISC-V
+This attribute allows the compiler to construct the
+requisite function declaration, while allowing the body of the
+function to be assembly code. The specified function will not have
+prologue/epilogue sequences generated by the compiler. Only basic
+@code{asm} statements can safely be included in naked functions
+(@pxref{Basic Asm}). While using extended @code{asm} or a mixture of
+basic @code{asm} and C code may appear to work, they cannot be
+depended upon to work reliably and are not supported.
+@end table
+
 @node RL78 Function Attributes
 @subsection RL78 Function Attributes