ptx assembly has some unusual requirements, Symbols must be declared
before use, and only functions (but not variables) can have forward
declarations.

This patch deals with getting variable definitions in the right order,
emitting declarations for undefined symbols, and emitting declarations
for all functions, whether defined or not. All of these are required
by the ptx assembler. We don't quite produce everything in the right
order (function declarations are written too late), so this still
needs an extra sorting step after compilation, for which we currently
use a small "assembler" shell script.

	gcc/
	* target.def (need_var_decl_before_use, assemble_undefined_decl,
	predeclare_function): New hooks.
	* hooks.c (hook_void_FILEptr_constcharptr_const_tree): New function.
	* hooks.h (hook_void_FILEptr_constcharptr_const_tree): Declare.
	* doc/tm.texi.in (TARGET_ASM_PREDECLARE_FUNCTION,
	TARGET_ASM_NEED_VAR_DECL_BEFORE_USE,
	TARGET_ASM_ASSEMBLE_UNDEFINED_DECL): Add.
	* doc/tm.texi: Regenerate.
	* cgraph.h (struct symtab_node_base): New flag visited.
	* cgraphunit.c (declare_all_functions): New static function.
	(compile): Call it.
	* output.h (assemble_undefined_decl): Declare.
	(get_fnname_from_decl): Declare.
	* varasm.c (assemble_undefined_decl): New function.
	(get_fnname_from_decl): New function.
	* final.c (rest_of_handle_final): Use it.
	* varpool.c (output_vars_in_decl_initial): New static function.
	(varpool_assemble_decl): Call it if declaration before use is needed.
	Return early if node was visited.  Set visited flag.
	(varpool_output_variables): Call assemble_undefined_decl for nodes
	without a definition.

------------------------------------------------------------------------
Index: gcc/doc/tm.texi
===================================================================
--- gcc/doc/tm.texi.orig
+++ gcc/doc/tm.texi
@@ -8007,6 +8007,12 @@ usual manner as a label (by means of @co
 You may wish to use @code{ASM_OUTPUT_TYPE_DIRECTIVE} in this target hook.
 @end deftypefn
 
+@deftypefn {Target Hook} void TARGET_ASM_PREDECLARE_FUNCTION (FILE *@var{file}, const char *@var{name}, const_tree @var{decl})
+A target hook to output to the stdio stream @var{file} any text necessary
+for declaring the name @var{name} of a function which will be output later on
+in the compilation.  Most targets do not require this hook to be defined.
+@end deftypefn
+
 @defmac ASM_DECLARE_REGISTER_GLOBAL (@var{stream}, @var{decl}, @var{regno}, @var{name})
 A C statement (sans semicolon) to output to the stdio stream
 @var{stream} any text necessary for claiming a register @var{regno}
@@ -8047,6 +8053,20 @@ global; that is, available for reference
 The default implementation uses the TARGET_ASM_GLOBALIZE_LABEL target hook.
 @end deftypefn
 
+@deftypevr {Target Hook} bool TARGET_ASM_NEED_VAR_DECL_BEFORE_USE
+If defined to true, this target hook causes the compiler to try to emit
+variable definitions in an order that ensures that references to a variable
+in an initializer occur only after it has been output. If there are circular
+references, the order is chosen at random.
+@end deftypevr
+
+@deftypefn {Target Hook} void TARGET_ASM_ASSEMBLE_UNDEFINED_DECL (FILE *@var{stream}, const char *@var{name}, const_tree @var{decl})
+This target hook is a function to output to the stdio stream
+@var{stream} some commands that will declare the name associated with
+@var{decl} which is not defined in the current translation unit.  Most
+assemblers do not require anything to be output in this case.
+@end deftypefn
+
 @defmac ASM_WEAKEN_LABEL (@var{stream}, @var{name})
 A C statement (sans semicolon) to output to the stdio stream
 @var{stream} some commands that will make the label @var{name} weak;
Index: gcc/doc/tm.texi.in
===================================================================
--- gcc/doc/tm.texi.in.orig
+++ gcc/doc/tm.texi.in
@@ -5798,6 +5798,8 @@ You may wish to use @code{ASM_OUTPUT_TYP
 
 @hook TARGET_ASM_DECLARE_CONSTANT_NAME
 
+@hook TARGET_ASM_PREDECLARE_FUNCTION
+
 @defmac ASM_DECLARE_REGISTER_GLOBAL (@var{stream}, @var{decl}, @var{regno}, @var{name})
 A C statement (sans semicolon) to output to the stdio stream
 @var{stream} any text necessary for claiming a register @var{regno}
@@ -5825,6 +5827,10 @@ You may wish to use @code{ASM_OUTPUT_SIZ
 
 @hook TARGET_ASM_GLOBALIZE_DECL_NAME
 
+@hook TARGET_ASM_NEED_VAR_DECL_BEFORE_USE
+
+@hook TARGET_ASM_ASSEMBLE_UNDEFINED_DECL
+
 @defmac ASM_WEAKEN_LABEL (@var{stream}, @var{name})
 A C statement (sans semicolon) to output to the stdio stream
 @var{stream} some commands that will make the label @var{name} weak;
Index: gcc/hooks.c
===================================================================
--- gcc/hooks.c.orig
+++ gcc/hooks.c
@@ -139,6 +139,13 @@ hook_void_FILEptr_constcharptr (FILE *a
 {
 }
 
+/* Generic hook that takes (FILE *, const char *, constr_tree *) and does
+   nothing.  */
+void
+hook_void_FILEptr_constcharptr_const_tree (FILE *, const char *, const_tree)
+{
+}
+
 /* Generic hook that takes (FILE *, rtx) and returns false.  */
 bool
 hook_bool_FILEptr_rtx_false (FILE *a ATTRIBUTE_UNUSED,
Index: gcc/hooks.h
===================================================================
--- gcc/hooks.h.orig
+++ gcc/hooks.h
@@ -68,6 +68,8 @@ extern void hook_void_void (void);
 extern void hook_void_constcharptr (const char *);
 extern void hook_void_rtx_int (rtx, int);
 extern void hook_void_FILEptr_constcharptr (FILE *, const char *);
+extern void hook_void_FILEptr_constcharptr_const_tree (FILE *, const char *,
+						       const_tree);
 extern bool hook_bool_FILEptr_rtx_false (FILE *, rtx);
 extern void hook_void_rtx (rtx);
 extern void hook_void_tree (tree);
Index: gcc/target.def
===================================================================
--- gcc/target.def.orig
+++ gcc/target.def
@@ -158,6 +158,24 @@ global; that is, available for reference
 The default implementation uses the TARGET_ASM_GLOBALIZE_LABEL target hook.",
  void, (FILE *stream, tree decl), default_globalize_decl_name)
 
+DEFHOOKPOD
+(need_var_decl_before_use,
+ "If defined to true, this target hook causes the compiler to try to emit\n\
+variable definitions in an order that ensures that references to a variable\n\
+in an initializer occur only after it has been output. If there are circular\n\
+references, the order is chosen at random.",
+ bool, false)
+
+/* Output code that will declare an external variable.  */
+DEFHOOK
+(assemble_undefined_decl,
+ "This target hook is a function to output to the stdio stream\n\
+@var{stream} some commands that will declare the name associated with\n\
+@var{decl} which is not defined in the current translation unit.  Most\n\
+assemblers do not require anything to be output in this case.",
+ void, (FILE *stream, const char *name, const_tree decl),
+ hook_void_FILEptr_constcharptr_const_tree)
+
 /* Output code that will emit a label for unwind info, if this
    target requires such labels.  Second argument is the decl the
    unwind info is associated with, third is a boolean: true if
@@ -258,6 +276,14 @@ You may wish to use @code{ASM_OUTPUT_TYP
  void, (FILE *file, const char *name, const_tree expr, HOST_WIDE_INT size),
  default_asm_declare_constant_name)
 
+/* Output a forward declaration of a function.  */
+DEFHOOK
+(predeclare_function,
+ "A target hook to output to the stdio stream @var{file} any text necessary\n\
+for declaring the name @var{name} of a function which will be output later on\n\
+in the compilation.  Most targets do not require this hook to be defined.",
+ void, (FILE *file, const char *name, const_tree decl), NULL)
+
 /* Emit a ttype table reference to a typeinfo object.  */
 DEFHOOK
 (ttype,
Index: gcc/cgraph.h
===================================================================
--- gcc/cgraph.h.orig
+++ gcc/cgraph.h
@@ -104,6 +104,9 @@ public:
      multiple partitions.  */
   unsigned in_other_partition : 1;
 
+  /* Set when a symbol is being visited.  Used when recursively outputting
+     definitions.  */
+  unsigned visited : 1;
 
 
   /*** other flags.  ***/
Index: gcc/final.c
===================================================================
--- gcc/final.c.orig
+++ gcc/final.c
@@ -4422,17 +4422,7 @@ leaf_renumber_regs_insn (rtx in_rtx)
 static unsigned int
 rest_of_handle_final (void)
 {
-  rtx x;
-  const char *fnname;
-
-  /* Get the function's name, as described by its RTL.  This may be
-     different from the DECL_NAME name used in the source file.  */
-
-  x = DECL_RTL (current_function_decl);
-  gcc_assert (MEM_P (x));
-  x = XEXP (x, 0);
-  gcc_assert (GET_CODE (x) == SYMBOL_REF);
-  fnname = XSTR (x, 0);
+  const char *fnname = get_fnname_from_decl (current_function_decl);
 
   assemble_start_function (current_function_decl, fnname);
   final_start_function (get_insns (), asm_out_file, optimize);
Index: gcc/cgraphunit.c
===================================================================
--- gcc/cgraphunit.c.orig
+++ gcc/cgraphunit.c
@@ -1830,6 +1830,31 @@ expand_function (struct cgraph_node *nod
 }
 
 
+/* For targets that require it, write out extra declarations of all functions
+   encountered, whether defined in this translation unit or not.  */
+
+static void
+declare_all_functions (void)
+{
+  if (targetm.asm_out.predeclare_function == NULL)
+    return;
+
+  struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
+  int order_pos;
+
+  order_pos = ipa_reverse_postorder (order);
+  gcc_assert (order_pos == cgraph_n_nodes);
+
+  for (int i = 0; i < order_pos; i++)
+    {
+      tree decl = order[i]->decl;
+      const char *name = get_fnname_from_decl (decl);
+      targetm.asm_out.predeclare_function (asm_out_file, name, decl);
+    }
+
+  free (order);
+}
+
 /* Expand all functions that must be output.
 
    Attempt to topologically sort the nodes so function is output when
@@ -2210,6 +2235,7 @@ compile (void)
       output_asm_statements ();
 
       expand_all_functions ();
+      declare_all_functions ();
       varpool_output_variables ();
     }
 
Index: gcc/varasm.c
===================================================================
--- gcc/varasm.c.orig
+++ gcc/varasm.c
@@ -1583,6 +1583,18 @@ decide_function_section (tree decl)
   in_cold_section_p = first_function_block_is_cold;
 }
 
+/* Get the function's name, as described by its RTL.  This may be
+   different from the DECL_NAME name used in the source file.  */
+const char *
+get_fnname_from_decl (tree decl)
+{
+  rtx x = DECL_RTL (decl);
+  gcc_assert (MEM_P (x));
+  x = XEXP (x, 0);
+  gcc_assert (GET_CODE (x) == SYMBOL_REF);
+  return XSTR (x, 0);
+}
+
 /* Output assembler code for the constant pool of a function and associated
    with defining the name of the function.  DECL describes the function.
    NAME is the function's name.  For the constant pool, we use the current
@@ -1949,6 +1961,15 @@ assemble_variable_contents (tree decl, c
     }
 }
 
+/* Write out assembly for the variable DECL, which is not defined in
+   the current translation unit.  */
+void
+assemble_undefined_decl (tree decl)
+{
+  const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+  targetm.asm_out.assemble_undefined_decl (asm_out_file, name, decl);
+}
+
 /* Assemble everything that is needed for a variable or function declaration.
    Not used for automatic variables, and not used for function definitions.
    Should not be called for variables of incomplete structure type.
Index: gcc/output.h
===================================================================
--- gcc/output.h.orig
+++ gcc/output.h
@@ -176,6 +176,9 @@ extern void default_assemble_visibility
    for an `asm' keyword used between functions.  */
 extern void assemble_asm (tree);
 
+/* Get the function's name from a decl, as described by its RTL.  */
+extern const char *get_fnname_from_decl (tree);
+
 /* Output assembler code for the constant pool of a function and associated
    with defining the name of the function.  DECL describes the function.
    NAME is the function's name.  For the constant pool, we use the current
@@ -201,6 +204,10 @@ extern void assemble_variable (tree, int
    into the preinit array.  */
 extern void assemble_vtv_preinit_initializer (tree);
 
+/* Assemble everything that is needed for a variable declaration that has
+   no definition in the current translation unit.  */
+extern void assemble_undefined_decl (tree);
+
 /* Compute the alignment of variable specified by DECL.
    DONT_OUTPUT_DATA is from assemble_variable.  */
 extern void align_variable (tree decl, bool dont_output_data);
Index: gcc/varpool.c
===================================================================
--- gcc/varpool.c.orig
+++ gcc/varpool.c
@@ -418,6 +418,27 @@ assemble_aliases (struct varpool_node *n
       }
 }
 
+/* Called through walk_tree to examine an initializer, and output all
+   variables referenced in it.  Used to ensure definition before use for
+   assemblers that require this.  */
+
+static tree
+output_vars_in_decl_initial (tree *xp, int *, void *)
+{
+  tree x = *xp;
+  enum tree_code code = TREE_CODE (x);
+
+  if (code == VAR_DECL)
+    {
+      if (TREE_ASM_WRITTEN (x) || DECL_HAS_VALUE_EXPR_P (x))
+	return NULL;
+
+      struct varpool_node *node = varpool_node_for_decl (x);
+      varpool_assemble_decl (node);
+    }
+  return NULL;
+}
+
 /* Output one variable, if necessary.  Return whether we output it.  */
 
 bool
@@ -430,6 +451,10 @@ varpool_assemble_decl (struct varpool_no
   if (node->alias)
     return false;
 
+  if (node->visited
+      || TREE_ASM_WRITTEN (decl))
+    return false;
+
   /* Constant pool is output from RTL land when the reference
      survive till this level.  */
   if (DECL_IN_CONSTANT_POOL (decl) && TREE_ASM_WRITTEN (decl))
@@ -454,6 +479,13 @@ varpool_assemble_decl (struct varpool_no
   if (!node->in_other_partition
       && !DECL_EXTERNAL (decl))
     {
+      node->visited = true;
+      if (targetm.asm_out.need_var_decl_before_use
+	  && DECL_INITIAL (decl))
+	walk_tree (&DECL_INITIAL (decl),
+		   output_vars_in_decl_initial, NULL, NULL);
+      node->visited = false;
+
       assemble_variable (decl, 0, 1, 0);
       gcc_assert (TREE_ASM_WRITTEN (decl));
       node->definition = true;
@@ -588,6 +620,9 @@ varpool_output_variables (void)
   FOR_EACH_DEFINED_VARIABLE (node)
     if (varpool_assemble_decl (node))
       changed = true;
+  FOR_EACH_VARIABLE (node)
+    if (!node->definition)
+      assemble_undefined_decl (node->decl);
   timevar_pop (TV_VAROUT);
   return changed;
 }
