From a9a7993c26ad84f4ae2f45ce1da92d59f23d1ee8 Mon Sep 17 00:00:00 2001
From: Iain Sandoe <iain@sandoe.co.uk>
Date: Tue, 1 Sep 2020 21:45:33 +0100
Subject: [PATCH] Generic : More mega-hack for va / normal.

(cherry picked from commit 899ff6e14355886789586631c3eff9ab6f30fbfc)
Signed-off-by: Kirill A. Korinsky <kirill@korins.ky>
---
 gcc/calls.c    |  6 ++++--
 gcc/calls.h    | 30 ++++++++++++++++++++++++++----
 gcc/function.c | 27 ++++++++++++++++++++-------
 gcc/function.h |  2 +-
 4 files changed, 51 insertions(+), 14 deletions(-)

diff --git gcc/calls.c gcc/calls.c
index 3c0dff06bc5..e7bb0063381 100644
--- gcc/calls.c
+++ gcc/calls.c
@@ -2310,7 +2310,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	 with those made by function.c.  */
 
       /* See if this argument should be passed by invisible reference.  */
-      function_arg_info arg (type, argpos < n_named_args);
+      function_arg_info arg (type, argpos < n_named_args,
+			     argpos == n_named_args - 1);
       if (pass_by_reference (args_so_far_pnt, arg))
 	{
 	  const bool callee_copies
@@ -2486,7 +2487,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 			     reg_parm_stack_space,
 			     args[i].pass_on_stack ? 0 : args[i].partial,
 			     fndecl, args_size, &args[i].locate,
-			     argpos < n_named_args);
+			     argpos < n_named_args,
+			     argpos == n_named_args - 1);
 #ifdef BLOCK_REG_PADDING
       else
 	/* The argument is passed entirely in registers.  See at which
diff --git gcc/calls.h gcc/calls.h
index 4ee49360777..3c5fbad301a 100644
--- gcc/calls.h
+++ gcc/calls.h
@@ -35,24 +35,43 @@ class function_arg_info
 {
 public:
   function_arg_info ()
-    : type (NULL_TREE), mode (VOIDmode), named (false),
+    : type (NULL_TREE), mode (VOIDmode), named (false), last_named (false),
       pass_by_reference (false)
   {}
 
   /* Initialize an argument of mode MODE, either before or after promotion.  */
   function_arg_info (machine_mode mode, bool named)
-    : type (NULL_TREE), mode (mode), named (named), pass_by_reference (false)
+    : type (NULL_TREE), mode (mode), named (named), last_named (false),
+      pass_by_reference (false)
+  {}
+
+  function_arg_info (machine_mode mode, bool named, bool last_named)
+    : type (NULL_TREE), mode (mode), named (named), last_named (last_named),
+      pass_by_reference (false)
   {}
 
   /* Initialize an unpromoted argument of type TYPE.  */
   function_arg_info (tree type, bool named)
-    : type (type), mode (TYPE_MODE (type)), named (named),
+    : type (type), mode (TYPE_MODE (type)), named (named), last_named (false),
       pass_by_reference (false)
   {}
 
+  /* Initialize an unpromoted argument of type TYPE.  */
+  function_arg_info (tree type, bool named, bool last_named)
+    : type (type), mode (TYPE_MODE (type)), named (named),
+      last_named (last_named), pass_by_reference (false)
+  {}
+
   /* Initialize an argument with explicit properties.  */
   function_arg_info (tree type, machine_mode mode, bool named)
-    : type (type), mode (mode), named (named), pass_by_reference (false)
+    : type (type), mode (mode), named (named), last_named (false),
+      pass_by_reference (false)
+  {}
+
+  /* Initialize an argument with explicit properties.  */
+  function_arg_info (tree type, machine_mode mode, bool named, bool last_named)
+    : type (type), mode (mode), named (named), last_named (last_named),
+      pass_by_reference (false)
   {}
 
   /* Return true if the gimple-level type is an aggregate.  */
@@ -105,6 +124,9 @@ public:
      "...").  See also TARGET_STRICT_ARGUMENT_NAMING.  */
   unsigned int named : 1;
 
+  /* True if this is the last named argument. */
+  unsigned int last_named : 1;
+
   /* True if we have decided to pass the argument by reference, in which case
      the function_arg_info describes a pointer to the original argument.  */
   unsigned int pass_by_reference : 1;
diff --git gcc/function.c gcc/function.c
index 1e4035074dc..4d5689057c6 100644
--- gcc/function.c
+++ gcc/function.c
@@ -2430,7 +2430,10 @@ assign_parm_find_data_types (struct assign_parm_data_all *all, tree parm,
   else if (DECL_CHAIN (parm))
     data->arg.named = 1;  /* Not the last non-variadic parm. */
   else if (targetm.calls.strict_argument_naming (all->args_so_far))
-    data->arg.named = 1;  /* Only variadic ones are unnamed.  */
+    {
+      data->arg.named = 1;  /* Only variadic ones are unnamed.  */
+      data->arg.last_named = 1;
+    }
   else
     data->arg.named = 0;  /* Treat as variadic.  */
 
@@ -2487,6 +2490,7 @@ assign_parms_setup_varargs (struct assign_parm_data_all *all,
 
   function_arg_info last_named_arg = data->arg;
   last_named_arg.named = true;
+  last_named_arg.last_named = true;
   targetm.calls.setup_incoming_varargs (all->args_so_far, last_named_arg,
 					&varargs_pretend_bytes, no_rtl);
 
@@ -2597,7 +2601,7 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
 		       all->reg_parm_stack_space,
 		       entry_parm ? data->partial : 0, current_function_decl,
 		       &all->stack_args_size, &data->locate,
-		       data->arg.named);
+		       data->arg.named, data->arg.last_named);
 
   /* Update parm_stack_boundary if this parameter is passed in the
      stack.  */
@@ -3894,7 +3898,8 @@ gimplify_parameters (gimple_seq *cleanup)
       if (data.arg.pass_by_reference)
 	{
 	  tree type = TREE_TYPE (data.arg.type);
-	  function_arg_info orig_arg (type, data.arg.named);
+	  function_arg_info orig_arg (type, data.arg.named,
+				      data.arg.last_named);
 	  if (reference_callee_copied (&all.args_so_far_v, orig_arg))
 	    {
 	      tree local, t;
@@ -4001,7 +4006,7 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
 		     tree fndecl ATTRIBUTE_UNUSED,
 		     struct args_size *initial_offset_ptr,
 		     struct locate_and_pad_arg_data *locate,
-		     bool named_p)
+		     bool named_p, bool last_named_p)
 {
   tree sizetree;
   pad_direction where_pad;
@@ -4038,10 +4043,18 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
   where_pad = targetm.calls.function_arg_padding (passed_mode, type);
   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
   if (named_p)
-    round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
-							        type);
+    {
+      round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
+							          type);
+      if (last_named_p)
+        round_boundary = PARM_BOUNDARY;
+    }
   else
-    round_boundary = PARM_BOUNDARY;
+    {
+      /* Force everything to be at least aligned to the parm boundary.  */
+      boundary = MAX (boundary, PARM_BOUNDARY);
+      round_boundary = PARM_BOUNDARY;
+    }
 
   locate->where_pad = where_pad;
 
diff --git gcc/function.h gcc/function.h
index 412509af923..faa3135a766 100644
--- gcc/function.h
+++ gcc/function.h
@@ -646,7 +646,7 @@ extern gimple_seq gimplify_parameters (gimple_seq *);
 extern void locate_and_pad_parm (machine_mode, tree, int, int, int,
 				 tree, struct args_size *,
 				 struct locate_and_pad_arg_data *,
-				 bool named_p = true);
+				 bool named_p = true, bool last_named = false);
 extern void generate_setjmp_warnings (void);
 
 /* Identify BLOCKs referenced by more than one NOTE_INSN_BLOCK_{BEG,END},
-- 
2.42.1

