2015-12-08  Nathan Sidwell  <nathan@acm.org>

	gcc/
	* config/nvptx/nvptx.c (decl_chunk_size, decl_chunk_mode,
	decl_offset, init_part, object_size, object_finished): Replace
	with ...
	(struct init_frag): ... this new struct variable.
	(begin_decl_field, output_decl_chunk): Replace with ...
	(output_init_frag): ... this new function.
	(nvptx_assemble_value): Reimplement.
	(nvptx_assemble_integer, nvptx_output_skip): Adjust.
	(nvptx_assemble_decl_begin, nvptx_assemble_decl_end): Adjust.
	(nvptx_output_aligned_decl): Call nvptx_assemble_decl_end.

	gcc/testsuite/
	* gcc.target/nvptx/trailing-init.c: New.

Index: config/nvptx/nvptx.c
===================================================================
--- config/nvptx/nvptx.c	(revision 231418)
+++ config/nvptx/nvptx.c	(working copy)
@@ -1484,73 +1484,70 @@ nvptx_hard_regno_mode_ok (int regno, mac
   return mode == cfun->machine->ret_reg_mode;
 }
 
-/* Machinery to output constant initializers.  When beginning an initializer,
-   we decide on a chunk size (which is visible in ptx in the type used), and
-   then all initializer data is buffered until a chunk is filled and ready to
-   be written out.  */
-
-/* Used when assembling integers to ensure data is emitted in
-   pieces whose size matches the declaration we printed.  */
-static unsigned int decl_chunk_size;
-static machine_mode decl_chunk_mode;
-/* Used in the same situation, to keep track of the byte offset
-   into the initializer.  */
-static unsigned HOST_WIDE_INT decl_offset;
-/* The initializer part we are currently processing.  */
-static HOST_WIDE_INT init_part;
-/* The total size of the object.  */
-static unsigned HOST_WIDE_INT object_size;
-/* True if we found a skip extending to the end of the object.  Used to
-   assert that no data follows.  */
-static bool object_finished;
-
-/* Write the necessary separator string to begin a new initializer value.  */
+/* Machinery to output constant initializers.  When beginning an
+   initializer, we decide on a fragment size (which is visible in ptx
+   in the type used), and then all initializer data is buffered until
+   a fragment is filled and ready to be written out.  */
+
+static struct
+{
+  unsigned HOST_WIDE_INT mask; /* Mask for storing fragment.  */
+  unsigned HOST_WIDE_INT val; /* Current fragment value.  */
+  unsigned HOST_WIDE_INT remaining; /*  Remaining bytes to be written
+					out.  */
+  unsigned size;  /* Fragment size to accumulate.  */
+  unsigned offset;  /* Offset within current fragment.  */
+  bool started;   /* Whether we've output any initializer.  */
+} init_frag;
+
+/* The current fragment is full,  write it out.  SYM may provide a
+   symbolic reference we should output,  in which case the fragment
+   value is the addend.  */
 
 static void
-begin_decl_field (void)
+output_init_frag (rtx sym)
 {
-  /* We never see decl_offset at zero by the time we get here.  */
-  if (decl_offset == decl_chunk_size)
-    fprintf (asm_out_file, " = { ");
-  else
-    fprintf (asm_out_file, ", ");
-}
+  fprintf (asm_out_file, init_frag.started ? ", " : " = { ");
+  unsigned HOST_WIDE_INT val = init_frag.val;
 
-/* Output the currently stored chunk as an initializer value.  */
+  init_frag.started = true;
+  init_frag.val = 0;
+  init_frag.offset = 0;
+  init_frag.remaining--;
+  
+  if (sym)
+    {
+      fprintf (asm_out_file, "generic(");
+      output_address (VOIDmode, sym);
+      fprintf (asm_out_file, val ? ") + " : ")");
+    }
 
-static void
-output_decl_chunk (void)
-{
-  begin_decl_field ();
-  output_address (VOIDmode, gen_int_mode (init_part, decl_chunk_mode));
-  init_part = 0;
+  if (!sym || val)
+    fprintf (asm_out_file, HOST_WIDE_INT_PRINT_DEC, val);
 }
 
-/* Add value VAL sized SIZE to the data we're emitting, and keep writing
-   out chunks as they fill up.  */
+/* Add value VAL of size SIZE to the data we're emitting, and keep
+   writing out chunks as they fill up.  */
 
 static void
-nvptx_assemble_value (HOST_WIDE_INT val, unsigned int size)
+nvptx_assemble_value (unsigned HOST_WIDE_INT val, unsigned size)
 {
-  unsigned HOST_WIDE_INT chunk_offset = decl_offset % decl_chunk_size;
-  gcc_assert (!object_finished);
-  while (size > 0)
+  val &= ((unsigned  HOST_WIDE_INT)2 << (size * BITS_PER_UNIT - 1)) - 1;
+
+  for (unsigned part = 0; size; size -= part)
     {
-      int this_part = size;
-      if (chunk_offset + this_part > decl_chunk_size)
-	this_part = decl_chunk_size - chunk_offset;
-      HOST_WIDE_INT val_part;
-      HOST_WIDE_INT mask = 2;
-      mask <<= this_part * BITS_PER_UNIT - 1;
-      val_part = val & (mask - 1);
-      init_part |= val_part << (BITS_PER_UNIT * chunk_offset);
-      val >>= BITS_PER_UNIT * this_part;
-      size -= this_part;
-      decl_offset += this_part;
-      if (decl_offset % decl_chunk_size == 0)
-	output_decl_chunk ();
+      val >>= part * BITS_PER_UNIT;
+      part = init_frag.size - init_frag.offset;
+      if (part > size)
+	part = size;
+
+      unsigned HOST_WIDE_INT partial
+	= val << (init_frag.offset * BITS_PER_UNIT);
+      init_frag.val |= partial & init_frag.mask;
+      init_frag.offset += part;
 
-      chunk_offset = 0;
+      if (init_frag.offset == init_frag.size)
+	output_init_frag (NULL);
     }
 }
 
@@ -1567,8 +1564,7 @@ nvptx_assemble_integer (rtx x, unsigned
       gcc_unreachable ();
 
     case CONST_INT:
-      val = INTVAL (x);
-      nvptx_assemble_value (val, size);
+      nvptx_assemble_value (INTVAL (x), size);
       break;
 
     case CONST:
@@ -1580,19 +1576,13 @@ nvptx_assemble_integer (rtx x, unsigned
       /* FALLTHROUGH */
 
     case SYMBOL_REF:
-      gcc_assert (size = decl_chunk_size);
-      if (decl_offset % decl_chunk_size != 0)
+      gcc_assert (size == init_frag.size);
+      if (init_frag.offset)
 	sorry ("cannot emit unaligned pointers in ptx assembly");
-      decl_offset += size;
-      begin_decl_field ();
 
       nvptx_maybe_record_fnsym (x);
-      fprintf (asm_out_file, "generic(");
-      output_address (VOIDmode, x);
-      fprintf (asm_out_file, ")");
-
-      if (val)
-	fprintf (asm_out_file, " + " HOST_WIDE_INT_PRINT_DEC, val);
+      init_frag.val = val;
+      output_init_frag (x);
       break;
     }
 
@@ -1606,21 +1596,28 @@ nvptx_assemble_integer (rtx x, unsigned
 void
 nvptx_output_skip (FILE *, unsigned HOST_WIDE_INT size)
 {
-  if (decl_offset + size >= object_size)
+  /* Finish the current fragment, if it's started.  */
+  if (init_frag.offset)
     {
-      if (decl_offset % decl_chunk_size != 0)
-	nvptx_assemble_value (0, decl_chunk_size);
-      object_finished = true;
-      return;
+      unsigned part = init_frag.size - init_frag.offset;
+      if (part > size)
+	part = (unsigned) size;
+      size -= part;
+      nvptx_assemble_value (0, part);
     }
 
-  while (size > decl_chunk_size)
+  /* If this skip doesn't terminate the initializer, write as many
+     remaining pieces as possible directly.  */
+  if (size < init_frag.remaining * init_frag.size)
     {
-      nvptx_assemble_value (0, decl_chunk_size);
-      size -= decl_chunk_size;
+      while (size >= init_frag.size)
+	{
+	  size -= init_frag.size;
+	  output_init_frag (NULL_RTX);
+	}
+      if (size)
+	nvptx_assemble_value (0, size);
     }
-  while (size-- > 0)
-    nvptx_assemble_value (0, 1);
 }
 
 /* Output a string STR with length SIZE.  As in nvptx_output_skip we
@@ -1662,15 +1659,18 @@ nvptx_assemble_decl_begin (FILE *file, c
 
   elt_size |= GET_MODE_SIZE (elt_mode);
   elt_size &= -elt_size; /* Extract LSB set.  */
-  elt_mode = mode_for_size (elt_size * BITS_PER_UNIT, MODE_INT, 0);
-
-  decl_chunk_size = elt_size;
-  decl_chunk_mode = elt_mode;
-  decl_offset = 0;
-  init_part = 0;
 
-  object_size = size;
-  object_finished = !size;
+  init_frag.size = elt_size;
+  /* Avoid undefined shift behaviour by using '2'.  */
+  init_frag.mask = ((unsigned HOST_WIDE_INT)2
+		    << (elt_size * BITS_PER_UNIT - 1)) - 1;
+  init_frag.val = 0;
+  init_frag.offset = 0;
+  init_frag.started = false;
+  /* Size might not be a multiple of elt size, if there's an
+     initialized trailing struct array with smaller type than
+     elt_size. */
+  init_frag.remaining = (size + elt_size - 1) / elt_size;
 
   fprintf (file, "%s .align %d .u%d ",
 	   section, align / BITS_PER_UNIT,
@@ -1680,8 +1680,7 @@ nvptx_assemble_decl_begin (FILE *file, c
   if (size)
     /* We make everything an array, to simplify any initialization
        emission.  */
-    fprintf (file, "[" HOST_WIDE_INT_PRINT_DEC "]",
-	     (size + elt_size - 1) / elt_size);
+    fprintf (file, "[" HOST_WIDE_INT_PRINT_DEC "]", init_frag.remaining);
 }
 
 /* Called when the initializer for a decl has been completely output through
@@ -1690,14 +1689,10 @@ nvptx_assemble_decl_begin (FILE *file, c
 static void
 nvptx_assemble_decl_end (void)
 {
-  if (decl_offset != 0)
-    {
-      if (!object_finished && decl_offset % decl_chunk_size != 0)
-	nvptx_assemble_value (0, decl_chunk_size);
-
-      fprintf (asm_out_file, " }");
-    }
-  fprintf (asm_out_file, ";\n");
+  if (init_frag.offset)
+    /* This can happen with a packed struct with trailing array member.  */
+    nvptx_assemble_value (0, init_frag.size - init_frag.offset);
+  fprintf (asm_out_file, init_frag.started ? " };\n" : ";\n");
 }
 
 /* Output an uninitialized common or file-scope variable.  */
@@ -1714,7 +1709,7 @@ nvptx_output_aligned_decl (FILE *file, c
 
   nvptx_assemble_decl_begin (file, name, section_for_decl (decl),
 			     TREE_TYPE (decl), size, align);
-  fprintf (file, ";\n");
+  nvptx_assemble_decl_end ();
 }
 
 /* Implement TARGET_ASM_DECLARE_CONSTANT_NAME.  Begin the process of
Index: testsuite/gcc.target/nvptx/trailing-init.c
===================================================================
--- testsuite/gcc.target/nvptx/trailing-init.c	(revision 0)
+++ testsuite/gcc.target/nvptx/trailing-init.c	(working copy)
@@ -0,0 +1,18 @@
+/* { dg-additional-options "-Wno-pedantic" } */
+
+struct trailing 
+{
+  unsigned m;
+  short ary[];
+} trailing = 
+  {.ary = {1}};
+
+struct packed 
+{
+  unsigned m;
+  short ary[];
+} __attribute__ ((packed)) packed = 
+  {.ary = {2}};
+
+/*  { dg-final { scan-assembler ".align 1 .u32 packed\\\[2\\\] = { 0, 2 };" } } */
+/*  { dg-final { scan-assembler ".align 4 .u32 trailing\\\[2\\\] = { 0, 1 };" } } */
