[PATCH] PR fortran/31593: Speed up loops with DO variables as procedure arguments

Steven Bosscher stevenb.gcc@gmail.com
Mon Aug 24 21:33:00 GMT 2009


2009/8/24 Tobias Schlüter <tobias.schlueter@physik.uni-muenchen.de>:
> diff -r 395e36e913de gcc/fortran/trans-stmt.c

Patches are often a bit more readable if you use diff -p (shows the
function you're patching)...


> --- a/gcc/fortran/trans-stmt.c  Sun Aug 23 18:13:56 2009 +0000
> +++ b/gcc/fortran/trans-stmt.c  Mon Aug 24 21:43:32 2009 +0200
> @@ -756,6 +756,49 @@
>  }
>
>

Comment here to explain what you are doing (stack of DO loop vars) and
why (show the transformations you intend to do)?

> +typedef struct do_var_stack {
> +  struct do_var_stack *prev;
> +  gfc_symbol *sym;
> +} do_var_stack;
> +
> +static do_var_stack *dvs_top = NULL;
> +
> +
> +static void
> +push_do_var (gfc_symbol *s)

Comment before the function please.  Same for pop_do_var() and gfc_is_do_var()


> +{
> +  do_var_stack *p
> +    = (do_var_stack *)gfc_getmem(sizeof(do_var_stack));

Since you're putting them on a stack -- why not use an obstack of
objects instead? See obstack examples throughout the middle-end.

> +  p->prev = dvs_top;
> +  p->sym = s;
> +  dvs_top = p;
> +}
> +
> +
> +static void
> +pop_do_var (void)
> +{
> +  do_var_stack *p = dvs_top;
> +  gcc_assert (p);
> +  dvs_top = p->prev;
> +  gfc_free (p);
> +}
> +
> +
> +bool
> +gfc_is_do_var (const gfc_symbol* s)
> +{
> +  do_var_stack *p = dvs_top;
> +  while (p)
> +    {
> +      if (p->sym == s)
> +       return true;
> +      p = p->prev;
> +    }
> +  return false;
> +}
> +
> +
>  /* Translate the simple DO construct.  This is where the loop variable has
>    integer type and step +-1.  We can't use this in the general case
>    because integer overflow and floating point errors could give incorrect
> @@ -874,6 +917,8 @@
>   tmp = build1_v (LABEL_EXPR, exit_label);
>   gfc_add_expr_to_block (pblock, tmp);
>
> +  pop_do_var();
> +
>   return gfc_finish_block (pblock);
>  }
>
> @@ -938,6 +983,7 @@
>   gfc_conv_expr_lhs (&se, code->ext.iterator->var);
>   gfc_add_block_to_block (&block, &se.pre);
>   dovar = se.expr;
> +  push_do_var (code->ext.iterator->var->symtree->n.sym);
>   type = TREE_TYPE (dovar);
>
>   gfc_init_se (&se, NULL);
> @@ -1117,6 +1163,8 @@
>   tmp = build1_v (LABEL_EXPR, exit_label);
>   gfc_add_expr_to_block (&block, tmp);
>
> +  pop_do_var();
> +
>   return gfc_finish_block (&block);
>  }
>
> diff -r 395e36e913de gcc/fortran/trans.h
> --- a/gcc/fortran/trans.h       Sun Aug 23 18:13:56 2009 +0000
> +++ b/gcc/fortran/trans.h       Mon Aug 24 21:43:32 2009 +0200
> @@ -498,6 +498,9 @@
>  /* Build a function decl for a library function.  */
>  tree gfc_build_library_function_decl (tree, tree, int, ...);
>
> +/* See if we're looking at the DO variable of an enclosing block.  */
> +bool gfc_is_do_var (const gfc_symbol *);
> +
>  /* somewhere! */
>  tree pushdecl (tree);
>  tree pushdecl_top_level (tree);
> diff -r 395e36e913de gcc/fortran/trans-expr.c
> --- a/gcc/fortran/trans-expr.c  Sun Aug 23 18:13:56 2009 +0000
> +++ b/gcc/fortran/trans-expr.c  Mon Aug 24 21:43:32 2009 +0200
> @@ -4148,6 +4148,30 @@
>
>   if (expr->expr_type == EXPR_VARIABLE)
>     {
> +      /* The DO variable may not be changed inside a loop.  Yet, if it
> +        is used as argument in a procedure call, the optimizers in
> +        their current state have to assume that it changed inside the
> +        procedure and it has to be committed to memory before
> +        subsequent calls to any other procedure, because the
> +        reference could have escaped.  This can severely slow down
> +        the code.  Therefore, we pass a temporary copy instead of the
> +        DO variable itself.  We cannot do this for a variable with
> +        the TARGET attribute, because the callee could set a POINTER
> +        to the variable that is accessed only after the loop has
> +        terminated.  A stricter criterion would be that the
> +        corresponding dummy argument doesn't have the TARGET
> +        attribute, but that information is not available here.  */

Ah, you explained it here.  Maybe just point to here from the other site.


> +      if (gfc_is_do_var (expr->symtree->n.sym)
> +         && !expr->symtree->n.sym->attr.target)

I would put the TARGET check before the gfc_is_do_var() check.


> +       {
> +         se->want_pointer = 0;
> +         gfc_conv_expr (se, expr);
> +         var = gfc_create_var (TREE_TYPE (se->expr), "arg");
> +         gfc_add_modify (&se->pre, var, se->expr);
> +         se->expr = gfc_build_addr_expr (NULL_TREE, var);
> +         gcc_assert (!se->post.head);
> +         return;
> +       }
>       se->want_pointer = 1;
>       gfc_conv_expr (se, expr);
>       if (se->post.head)


This patch speeds up one of my toy codes by ~12%. Nice :-)

Ciao!
Steven



More information about the Fortran mailing list