RFA, PR fortran/34686: Aliasing bug when returning character pointers

Richard Sandiford rsandifo@nildram.co.uk
Tue Jan 8 10:56:00 GMT 2008


char_pointer_func.f90 is failing at -O3 -mabi=32 on mips64-linux-gnu
because of an aliasing bug.  We have a function sfoo() that returns a
pointer to a string of length 4:

  function sfoo () result (sc1)
    character*4, pointer       :: sc1
    allocate (sc1)
    sc1 = "abcd"
  end function sfoo

It gets the C prototype:

  static void sfoo (character(kind=1)[1:4] * &, integer(kind=4));

However, the first caller is:

  c1 = sfoo ()

and it passes a pointer to a 0-based array, pstr.3:

  {
    character(kind=1)[4] * pstr.3;

    sfoo (&pstr.3, 4);
    __builtin_memmove ((void *) c1, (void *) pstr.3, 4);
  }

So after inlining sfoo(), we get the following code at final_cleanup:

      D.617 = (character(kind=1)[1:4] *) D.616;
 -->  *(character(kind=1)[1:4] * &) &pstr.3 = D.617;
      __builtin_memcpy (D.617, &"abcd"[1]{lb: 1 sz: 1}, 4);
      __builtin_memmove (c1, pstr.3, 4);

where __builtin_memcpy is small enough to open-code; we do not use a call.
The two pointer types (one to a [4]-based array and one to a [1:4]-based
array) have different alias sets, so in the failing case, we then
schedule the read of pstr.3 before the store to
*(character(kind=1)[1:4] * &) &pstr.3.

The problem appears to be in this part of gfc_conv_function_call.
which deliberately ignores the bounds on the return value:

          /* Pass the string length.  */
          type = gfc_get_character_type (ts.kind, ts.cl);
          type = build_pointer_type (type);

          /* Return an address to a char[0:len-1]* temporary for
             character pointers.  */
          if (sym->attr.pointer || sym->attr.allocatable)
            {
              /* Build char[0:len-1] * pstr.  */
              tmp = fold_build2 (MINUS_EXPR, gfc_charlen_type_node, len,
                                 build_int_cst (gfc_charlen_type_node, 1));
              tmp = build_range_type (gfc_array_index_type,
                                      gfc_index_zero_node, tmp);
              tmp = build_array_type (gfc_character1_type_node, tmp);
              var = gfc_create_var (build_pointer_type (tmp), "pstr");

              /* Provide an address expression for the function arguments.  */
              var = build_fold_addr_expr (var);
            }
          else
            var = gfc_conv_string_tmp (se, type, len);

(I believe TYPE is therefore unused in the sym->attr.pointer
|| sym->attr.allocatable case).  It would seem more natural to do:

              var = gfc_create_var (type, "pstr");

              /* Provide an address expression for the function arguments.  */
              var = build_fold_addr_expr (var);

which, in combination with the later:

	      /* Dereference for character pointer results.  */
	      if (sym->attr.pointer || sym->attr.allocatable)
		se->expr = build_fold_indirect_ref (var);
	      else
	        se->expr = var;

makes sure that se->expr always has type TYPE.  This does indeed
fix the problem: pstr.3 is then stored to in its natural type.

(The current 0-based code seems to be mimicking the "str" case of
gfc_conv_string_tmp, but that's creating an on-stack array, rather
than a pointer to an array, and that case still returns a pointer
to a 1-based array.)

Bootstrapped & regression-tested on x86_64-linux-gnu.  Also regression-
tested on mips64-linux-gnu (all 3 ABIs).  OK to install?

Richard


Index: gcc/fortran/trans-expr.c
===================================================================
--- gcc/fortran/trans-expr.c	2008-01-07 10:48:25.000000000 +0000
+++ gcc/fortran/trans-expr.c	2008-01-07 10:48:29.000000000 +0000
@@ -2660,13 +2660,7 @@ gfc_conv_function_call (gfc_se * se, gfc
 	     character pointers.  */
 	  if (sym->attr.pointer || sym->attr.allocatable)
 	    {
-	      /* Build char[0:len-1] * pstr.  */
-	      tmp = fold_build2 (MINUS_EXPR, gfc_charlen_type_node, len,
-				 build_int_cst (gfc_charlen_type_node, 1));
-	      tmp = build_range_type (gfc_array_index_type,
-				      gfc_index_zero_node, tmp);
-	      tmp = build_array_type (gfc_character1_type_node, tmp);
-	      var = gfc_create_var (build_pointer_type (tmp), "pstr");
+	      var = gfc_create_var (type, "pstr");
 
 	      /* Provide an address expression for the function arguments.  */
 	      var = build_fold_addr_expr (var);



More information about the Gcc-patches mailing list