diff mbox

Add -funknown-commons to work around PR/69368 (and others) in SPEC2006

Message ID 1455903754-12572-1-git-send-email-alan.lawrence@arm.com
State New
Headers show

Commit Message

Alan Lawrence Feb. 19, 2016, 5:42 p.m. UTC
This relates to FORTRAN code where different modules give different sizes to the
same array in a COMMON block (contrary to the fortran language specification).
SPEC have refused to patch the source code
(https://www.spec.org/cpu2006/Docs/faq.html#Run.05).

Hence, this patch provides a Fortran-specific option -funknown-commons that
marks such arrays as having unknown size - that is, NULL_TREE for both
TYPE_SIZE and max value of TYPE_DOMAIN. DECL_SIZE is preserved for e.g. output
in varasm.c.

On AArch64, it fixes the 416.gamess issue, and allows compiling 416.gamess
without the -fno-aggressive-loop-optimizations previously required (numerous
other PRs relating to 416.gamess).

I had to fix up a couple of places to deal with null TYPE_SIZE but in most cases
a test for such was already present. (omp-low.c had too many so I gave up: in
practice I think it's OK to just not use the new flag at the same time as
-fopenmp).

Bootstrapped + check-gcc + check-g++ on AArch64 and x86.
Also check-fortran with a variant enabling the option in all cases (to allow
compilation of C): here, only gfortran.dg/gomp/appendix-a/a.24.1.f90 fails,
this uses -fopenmp mentioned above.

Testcase fails both the FIND and one of the assignments if -funknown-commons is removed.

OK for trunk? I'd be happy to move this under -std=legacy if the Fortran folks
prefer. (-funcommon-commons has also been humourously suggested!)

gcc/ChangeLog:

	* expr.c (store_field): Handle null TYPE_SIZE.
	* tree-vect-data-refs.c (vect_analyze_data_refs): Bail out if TYPE_SIZE
	is null.
	* tree-dfa.c (get_ref_base_and_extent): Keep maxsize==-1 when reading
	the whole of a DECL.

gcc/fortran/ChangeLog:

	* lang.opt: Add -funknown-commons.
	* trans-common.ci (build_field): If flag_unknown_commons is on, set
	upper bound and size of arrays in common block to NULL_TREE.
	(create_common): Explicitly set DECL_SIZE and DECL_SIZE_UNIT.

gcc/testsuite/ChangeLog:
	* gfortran.dg/unknown_commons.f: New.
---
 gcc/expr.c                                  |  1 +
 gcc/fortran/lang.opt                        |  4 ++++
 gcc/fortran/trans-common.c                  | 35 +++++++++++++++++++++++++++++
 gcc/testsuite/gfortran.dg/unknown_commons.f | 20 +++++++++++++++++
 gcc/tree-dfa.c                              |  6 ++++-
 gcc/tree-vect-data-refs.c                   |  8 +++++++
 6 files changed, 73 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gfortran.dg/unknown_commons.f

Comments

Jakub Jelinek Feb. 19, 2016, 5:52 p.m. UTC | #1
On Fri, Feb 19, 2016 at 05:42:34PM +0000, Alan Lawrence wrote:
> This relates to FORTRAN code where different modules give different sizes to the
> same array in a COMMON block (contrary to the fortran language specification).
> SPEC have refused to patch the source code
> (https://www.spec.org/cpu2006/Docs/faq.html#Run.05).
> 
> Hence, this patch provides a Fortran-specific option -funknown-commons that
> marks such arrays as having unknown size - that is, NULL_TREE for both
> TYPE_SIZE and max value of TYPE_DOMAIN. DECL_SIZE is preserved for e.g. output
> in varasm.c.
> 
> On AArch64, it fixes the 416.gamess issue, and allows compiling 416.gamess
> without the -fno-aggressive-loop-optimizations previously required (numerous
> other PRs relating to 416.gamess).
> 
> I had to fix up a couple of places to deal with null TYPE_SIZE but in most cases

I think it is wrong to touch TYPE_SIZE/TYPE_SIZE_UNIT, IMHO it is much better just
to ignore DECL_SIZE/DECL_SIZE_UNIT in the selected few places
(get_ref_base_and_extent, the tree-ssa-loop-niters.c analysis) if the switch
is on, for selected decls (aggregates with flexible array members and other
similar trailing arrays, arrays themselves; all only if DECL_COMMON).

> a test for such was already present. (omp-low.c had too many so I gave up: in
> practice I think it's OK to just not use the new flag at the same time as
> -fopenmp).

That will just be an endless source of bugreports when people will report ICEs
with -funknown-commons -fopenmp (or -fopenacc, or -fcilkplus, or
-ftree-parallelize-loops, ...).  For OpenMP the TYPE_SIZE* is essential, if
you privatize variables, you need to know how big variable to allocate etc.

	Jakub
Alan Lawrence Feb. 22, 2016, 11:46 a.m. UTC | #2
On 19/02/16 17:52, Jakub Jelinek wrote:
> On Fri, Feb 19, 2016 at 05:42:34PM +0000, Alan Lawrence wrote:
>> This relates to FORTRAN code where different modules give different sizes to the
>> same array in a COMMON block (contrary to the fortran language specification).
>> SPEC have refused to patch the source code
>> (https://www.spec.org/cpu2006/Docs/faq.html#Run.05).
>>
>> Hence, this patch provides a Fortran-specific option -funknown-commons that
>> marks such arrays as having unknown size - that is, NULL_TREE for both
>> TYPE_SIZE and max value of TYPE_DOMAIN. DECL_SIZE is preserved for e.g. output
>> in varasm.c.
>>
>> On AArch64, it fixes the 416.gamess issue, and allows compiling 416.gamess
>> without the -fno-aggressive-loop-optimizations previously required (numerous
>> other PRs relating to 416.gamess).
>>
>> I had to fix up a couple of places to deal with null TYPE_SIZE but in most cases
>
> I think it is wrong to touch TYPE_SIZE/TYPE_SIZE_UNIT, IMHO it is much better just
> to ignore DECL_SIZE/DECL_SIZE_UNIT in the selected few places
> (get_ref_base_and_extent, the tree-ssa-loop-niters.c analysis) if the switch
> is on, for selected decls (aggregates with flexible array members and other
> similar trailing arrays, arrays themselves; all only if DECL_COMMON).

So do you see...

(a) A global command-line option, which we check alongside DECL_COMMON, in (all 
or some of the) places which currently deal with flexible array members? (I 
guess the flag would have no effect for compiling C code...or
  (b) do we require it to 'enable' the existing flexible array member support?)

(c) A new flag on each DECL, which we check in the places dealing with flexible 
array members, which we set on those decls in the fortran frontend if a 
fortran-frontend-only command-line option is passed?

(d) A new flag on each DECL, which replaces the check for flexible array 
members, plus a global command-line option, which controls both setting the flag 
(on DECL_COMMONs) in the fortran frontend, and also setting it on flexible array 
members in the C frontend? This might be 'cleanest' but is also probably the 
most change and I doubt I'm going to have time to do this for GCC 6...

(e) Some other scheme that I've not understood yet?

Thanks,
Alan
Jakub Jelinek Feb. 22, 2016, 12:03 p.m. UTC | #3
On Mon, Feb 22, 2016 at 11:46:19AM +0000, Alan Lawrence wrote:
> On 19/02/16 17:52, Jakub Jelinek wrote:
> >On Fri, Feb 19, 2016 at 05:42:34PM +0000, Alan Lawrence wrote:
> >>This relates to FORTRAN code where different modules give different sizes to the
> >>same array in a COMMON block (contrary to the fortran language specification).
> >>SPEC have refused to patch the source code
> >>(https://www.spec.org/cpu2006/Docs/faq.html#Run.05).
> >>
> >>Hence, this patch provides a Fortran-specific option -funknown-commons that
> >>marks such arrays as having unknown size - that is, NULL_TREE for both
> >>TYPE_SIZE and max value of TYPE_DOMAIN. DECL_SIZE is preserved for e.g. output
> >>in varasm.c.
> >>
> >>On AArch64, it fixes the 416.gamess issue, and allows compiling 416.gamess
> >>without the -fno-aggressive-loop-optimizations previously required (numerous
> >>other PRs relating to 416.gamess).
> >>
> >>I had to fix up a couple of places to deal with null TYPE_SIZE but in most cases
> >
> >I think it is wrong to touch TYPE_SIZE/TYPE_SIZE_UNIT, IMHO it is much better just
> >to ignore DECL_SIZE/DECL_SIZE_UNIT in the selected few places
> >(get_ref_base_and_extent, the tree-ssa-loop-niters.c analysis) if the switch
> >is on, for selected decls (aggregates with flexible array members and other
> >similar trailing arrays, arrays themselves; all only if DECL_COMMON).
> 
> So do you see...
> 
> (a) A global command-line option, which we check alongside DECL_COMMON, in
> (all or some of the) places which currently deal with flexible array
> members? (I guess the flag would have no effect for compiling C code...or
>  (b) do we require it to 'enable' the existing flexible array member support?)
> 
> (c) A new flag on each DECL, which we check in the places dealing with
> flexible array members, which we set on those decls in the fortran frontend
> if a fortran-frontend-only command-line option is passed?
> 
> (d) A new flag on each DECL, which replaces the check for flexible array
> members, plus a global command-line option, which controls both setting the
> flag (on DECL_COMMONs) in the fortran frontend, and also setting it on
> flexible array members in the C frontend? This might be 'cleanest' but is
> also probably the most change and I doubt I'm going to have time to do this
> for GCC 6...
> 
> (e) Some other scheme that I've not understood yet?

(f) A global command-line option, which we check alongside DECL_COMMON and
further tests (basically, we want only DECL_COMMON decls that either have
ARRAY_TYPE, or some other aggregate type with flexible array member or some
other trailing array in the struct), and use the resulting predicate in
places where we optimize based on DECL_SIZE{,_UNIT} of the decl - if that
predicate returns true, we assume the DECL_SIZE is
"variable"/"unlimited"/whatever.
The two spots known to me that would need to use this new predicate would
be:
tree.c (array_at_struct_end_p):
  /* If the reference is based on a declared entity, the size of the array
     is constrained by its given domain.  */
  if (DECL_P (ref))
    return false;
This would need to do if (DECL_P (ref) && !the_new_predicate (ref)) return false;
tree-dfa.c (get_ref_base_and_extent):
  if (DECL_P (exp))
    {
...
    }
You want to do inside of that block something like
      if (the_new_predicate (exp))
	{
	  /* We need to deal with variable arrays ending structures.  */
	  if (seen_variable_array_ref
	      && maxsize != -1
	      && (TYPE_SIZE (TREE_TYPE (exp)) == NULL_TREE
		  || TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST
		  || (bit_offset + maxsize
		      == wi::to_offset (TYPE_SIZE (TREE_TYPE (exp))))))
	    maxsize = -1;
	  else if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)
	    maxsize = -1;
	}
      /* If maxsize is unknown adjust it according to the size of the
         base decl.  */
      else if ((maxsize == -1
	       && DECL_SIZE (exp)
	       && TREE_CODE (DECL_SIZE (exp)) == INTEGER_CST)
	maxsize = wi::to_offset (DECL_SIZE (exp)) - bit_offset;
Thus, basically for the DECL_COMMON with trailing array readd the
r233153 hunk (though it can be readded in slightly different spot),
somehow deal with the case of ARRAY_TYPE itself, and only apply DEC_SIZE if
the predicate is false.

	Jakub
Alan Lawrence Feb. 23, 2016, 11:42 a.m. UTC | #4
On 22/02/16 12:03, Jakub Jelinek wrote:
>
> (f) A global command-line option, which we check alongside DECL_COMMON and
> further tests (basically, we want only DECL_COMMON decls that either have
> ARRAY_TYPE, or some other aggregate type with flexible array member or some
> other trailing array in the struct), and use the resulting predicate in
> places where we optimize based on DECL_SIZE{,_UNIT} of the decl - if that
> predicate returns true, we assume the DECL_SIZE is
> "variable"/"unlimited"/whatever.
> The two spots known to me that would need to use this new predicate would
> be:
> tree.c (array_at_struct_end_p):
> tree-dfa.c (get_ref_base_and_extent):

Well, with just those two, this fixes 416.gamess, and passes all tests in 
gfortran, with only a few scan-dump/warning tests failing in gcc...

--Alan
diff mbox

Patch

diff --git a/gcc/expr.c b/gcc/expr.c
index f4bac36..609bf59 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6638,6 +6638,7 @@  store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
 	 RHS isn't the same size as the bitfield, we must use bitfield
 	 operations.  */
       || (bitsize >= 0
+	  && TYPE_SIZE (TREE_TYPE (exp))
 	  && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) == INTEGER_CST
 	  && compare_tree_int (TYPE_SIZE (TREE_TYPE (exp)), bitsize) != 0
 	  /* Except for initialization of full bytes from a CONSTRUCTOR, which
diff --git a/gcc/fortran/lang.opt b/gcc/fortran/lang.opt
index 45428d8..b1b4641 100644
--- a/gcc/fortran/lang.opt
+++ b/gcc/fortran/lang.opt
@@ -686,6 +686,10 @@  funderscoring
 Fortran Var(flag_underscoring) Init(1)
 Append underscores to externally visible names.
 
+funknown-commons
+Fortran Var(flag_unknown_commons)
+Treat arrays in common blocks as having unknown size.
+
 fwhole-file
 Fortran Ignore
 Does nothing.  Preserved for backward compatibility.
diff --git a/gcc/fortran/trans-common.c b/gcc/fortran/trans-common.c
index 21d1928..bf99c56 100644
--- a/gcc/fortran/trans-common.c
+++ b/gcc/fortran/trans-common.c
@@ -284,8 +284,41 @@  build_field (segment_info *h, tree union_type, record_layout_info rli)
   unsigned HOST_WIDE_INT desired_align, known_align;
 
   name = get_identifier (h->sym->name);
+
+  gcc_assert (TYPE_P (h->field));
+  tree size = TYPE_SIZE (h->field);
+  tree size_unit = TYPE_SIZE_UNIT (h->field);
+  if (GFC_ARRAY_TYPE_P (h->field)
+      && !h->sym->value
+      && flag_unknown_commons)
+    {
+      gcc_assert (!GFC_DESCRIPTOR_TYPE_P (h->field));
+      gcc_assert (TREE_CODE (h->field) == ARRAY_TYPE);
+
+      /* Could be merged at link time with a larger array whose size we don't
+	 know.  */
+      static tree range_type = NULL_TREE;
+      if (!range_type)
+	range_type = build_range_type (gfc_array_index_type,
+				       gfc_index_zero_node, NULL_TREE);
+      tree type = copy_node (h->field);
+      TYPE_DOMAIN (type) = range_type;
+      TYPE_SIZE (type) = NULL_TREE;
+      TYPE_SIZE_UNIT (type) = NULL_TREE;
+      SET_TYPE_MODE (type, BLKmode);
+      gcc_assert (TYPE_CANONICAL (h->field) == h->field);
+      gcc_assert (TYPE_MAIN_VARIANT (h->field) == h->field);
+      TYPE_CANONICAL (type) = type;
+      TYPE_MAIN_VARIANT (type) = type;
+      layout_type (type);
+      h->field = type;
+    }
+
   field = build_decl (h->sym->declared_at.lb->location,
 		      FIELD_DECL, name, h->field);
+  DECL_SIZE (field) = size;
+  DECL_SIZE_UNIT (field) = size_unit;
+
   known_align = (offset & -offset) * BITS_PER_UNIT;
   if (known_align == 0 || known_align > BIGGEST_ALIGNMENT)
     known_align = BIGGEST_ALIGNMENT;
@@ -703,6 +736,8 @@  create_common (gfc_common_head *com, segment_info *head, bool saw_equiv)
 			     VAR_DECL, DECL_NAME (s->field),
 			     TREE_TYPE (s->field));
       TREE_STATIC (var_decl) = TREE_STATIC (decl);
+      DECL_SIZE (var_decl) = DECL_SIZE (s->field);
+      DECL_SIZE_UNIT (var_decl) = DECL_SIZE_UNIT (s->field);
       /* Mark the variable as used in order to avoid warnings about
 	 unused variables.  */
       TREE_USED (var_decl) = 1;
diff --git a/gcc/testsuite/gfortran.dg/unknown_commons.f b/gcc/testsuite/gfortran.dg/unknown_commons.f
new file mode 100644
index 0000000..97e3ce3
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/unknown_commons.f
@@ -0,0 +1,20 @@ 
+! { dg-do compile }
+! { dg-options "-O3 -funknown-commons -fdump-tree-dom2-details" }
+
+! Test for PR69368: a single-element array in a common block, which will be
+! overridden with a larger size at link time (contrary to language spec).
+! Dominator opts considers accesses to differently-computed elements of X as
+! equivalent, unless -funknown-commons is passed in.
+      SUBROUTINE FOO
+      IMPLICIT DOUBLE PRECISION (X)
+      INTEGER J
+      COMMON /MYCOMMON / X(1)
+      DO 10 J=1,1024
+         X(J+1)=X(J+7)
+  10  CONTINUE
+      RETURN
+      END
+! { dg-final { scan-tree-dump-not "FIND" "dom2" } }
+! We should retain both a read and write of mycommon.x.
+! { dg-final { scan-tree-dump-times "  _\[0-9\]+ = mycommon\\.x\\\[_\[0-9\]+\\\];" 1 "dom2" } }
+! { dg-final { scan-tree-dump-times "  mycommon\\.x\\\[_\[0-9\]+\\\] = _\[0-9\]+;" 1 "dom2" } }
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index 0e98056..340c04c 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -617,7 +617,11 @@  get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
       if (maxsize == -1
 	  && DECL_SIZE (exp)
 	  && TREE_CODE (DECL_SIZE (exp)) == INTEGER_CST)
-	maxsize = wi::to_offset (DECL_SIZE (exp)) - bit_offset;
+	{
+	  maxsize = wi::to_offset (DECL_SIZE (exp)) - bit_offset;
+	  if (maxsize == bitsize)
+	    maxsize = -1;
+	}
     }
   else if (CONSTANT_CLASS_P (exp))
     {
diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index 4c0e135..f8e8dce 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -3412,6 +3412,14 @@  again:
           return false;
         }
 
+      if (TYPE_SIZE (TREE_TYPE (DR_REF (dr))) == NULL_TREE)
+	{
+	  if (dump_enabled_p ())
+	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+			     "not vectorized: data-ref of unknown size\n");
+	  return false;
+	}
+
       stmt = DR_STMT (dr);
       stmt_info = vinfo_for_stmt (stmt);