Patchwork [fortran] Make frontend walker more general

login
register
mail settings
Submitter Thomas Koenig
Date Sept. 11, 2010, 7:40 p.m.
Message ID <1284234002.5873.3.camel@linux-fd1f.site>
Download mbox | patch
Permalink /patch/64528/
State New
Headers show

Comments

Thomas Koenig - Sept. 11, 2010, 7:40 p.m.
Hello world,

the current expression walker for front end stuff is not general.

Here's an attempt to make it so.  I have also added a few cases which
were not handled before.

Regression-tested.  No test necessary, because this is only a cleanup.
OK for trunk?

	Thomas

2010-09-11  Thomas Koenig  <tkoenig@gcc.gnu.org>

	* frontend-passes.c (process_functions):  New struct and type.
	(process_namespace):  Rename from optimize_namespace, add
	process_functions * argument.  Call renamed functions.
	Adjust prototype.
	(process_code):  Rename from optimize_code, add
	process_functions * argument.  Call renamed functions.
	Adjust prototype.
	(process_code_node):  Rename from optimize_code_node, add
	process_functions * argument.  Call functions from the
	pass function pointers.  Add code for the EXEC_READ, EXEC_WRITE,
	EXEC_OPEN, EXEC_INQUIRE, EXEC_REWIND, EXEC_ENDFILE,
	EXEC_BACKSPACE, EXEC_CLOSE, EXEC_WAIT.  Adjust prototype.
	(optimize_expr_0):  Return early for NULL expression.
	(optimize_pass):  New struct.
	(gfc_run_passes):  Call process namespace with optimize_pass
	argument instead of optimize_namespace.
Steven Bosscher - Sept. 11, 2010, 7:51 p.m.
Hi,

To make the walker really generally usable, I would do a few things extra:

> +/* A struct for holding the pass-specific functions.  */
> +typedef struct process_functions
> +{
> +  void (*process_assignment) (gfc_code *);
> +  void (*process_expr) (gfc_expr *);

I'd pass the gfc_code where the expr is in also:

  void (*process_expr) (gfc_expr *, gfc_code *);

> +  void (*process_actual_arglist) (gfc_actual_arglist *);
> +  void (*process_pointer_assign) (gfc_code *);
> +} process_functions;


Nit: not gcc code style:
> +struct process_functions optimize_pass =
> +  { optimize_assignment,
> +    optimize_expr_0,
> +    optimize_actual_arglist,
> +    NULL };


I wonder how much of the resolve.c code can be rewritten to also use
this walker...

Ciao!
Steven
Jakub Jelinek - Sept. 11, 2010, 8:09 p.m.
On Sat, Sep 11, 2010 at 09:51:57PM +0200, Steven Bosscher wrote:
> To make the walker really generally usable, I would do a few things extra:
> 
> > +/* A struct for holding the pass-specific functions.  */
> > +typedef struct process_functions
> > +{
> > +  void (*process_assignment) (gfc_code *);
> > +  void (*process_expr) (gfc_expr *);
> 
> I'd pass the gfc_code where the expr is in also:

Furthermore, I think having process_pointer_assign etc. special hooks
is not the right thing for a walker.  IMNSHO you should look e.g. at
walk_tree or perhaps better walk_gimple_stmt.
The walker should call one hook (which takes gfc_code *) at each gfc_code
node, and another hook at each gfc_expr node.  The API should have a way
to stop processing and return some value to the caller, have some void *
pointer passed to the callbacks in which the caller can keep some state,
and have a way how to say that subtrees shouldn't be traversed.

	Jakub
Thomas Koenig - Sept. 11, 2010, 9 p.m.
Am Samstag, den 11.09.2010, 22:09 +0200 schrieb Jakub Jelinek:
> On Sat, Sep 11, 2010 at 09:51:57PM +0200, Steven Bosscher wrote:
> > To make the walker really generally usable, I would do a few things extra:
> > 
> > > +/* A struct for holding the pass-specific functions.  */
> > > +typedef struct process_functions
> > > +{
> > > +  void (*process_assignment) (gfc_code *);
> > > +  void (*process_expr) (gfc_expr *);
> > 
> > I'd pass the gfc_code where the expr is in also:
> 
> Furthermore, I think having process_pointer_assign etc. special hooks
> is not the right thing for a walker.  IMNSHO you should look e.g. at
> walk_tree or perhaps better walk_gimple_stmt.
> The walker should call one hook (which takes gfc_code *) at each gfc_code
> node, and another hook at each gfc_expr node.  The API should have a way
> to stop processing and return some value to the caller, have some void *
> pointer passed to the callbacks in which the caller can keep some state,
> and have a way how to say that subtrees shouldn't be traversed.

Why would this be better?

Given the complexity of Fortran, wouldn't this lead to lots of code
duplication, with a big switch statement both in the statement walker
and the handler for the individual statements?

	Thomas
Jakub Jelinek - Sept. 13, 2010, 9:51 a.m.
On Sat, Sep 11, 2010 at 11:00:30PM +0200, Thomas Koenig wrote:
> > Furthermore, I think having process_pointer_assign etc. special hooks
> > is not the right thing for a walker.  IMNSHO you should look e.g. at
> > walk_tree or perhaps better walk_gimple_stmt.
> > The walker should call one hook (which takes gfc_code *) at each gfc_code
> > node, and another hook at each gfc_expr node.  The API should have a way
> > to stop processing and return some value to the caller, have some void *
> > pointer passed to the callbacks in which the caller can keep some state,
> > and have a way how to say that subtrees shouldn't be traversed.
> 
> Why would this be better?
> 
> Given the complexity of Fortran, wouldn't this lead to lots of code
> duplication, with a big switch statement both in the statement walker
> and the handler for the individual statements?

Because otherwise it isn't a general Fortran FE walker, but quite
specialized one, and each time you need to handle some code that doesn't
have a hook or where the hook doesn't have generic enough argument,
you will need to modify the walker.

I believe in most cases you'll be looking only at a handful of different
codes, so even if you use a switch stmt in the hook, it will be small enough
that it will be implemented using a series of ifs.

	Jakub
Thomas Koenig - Sept. 13, 2010, 5:25 p.m.
Hi Jakub,

> > 
> > Given the complexity of Fortran, wouldn't this lead to lots of code
> > duplication, with a big switch statement both in the statement walker
> > and the handler for the individual statements?
> 
> Because otherwise it isn't a general Fortran FE walker, but quite
> specialized one, and each time you need to handle some code that doesn't
> have a hook or where the hook doesn't have generic enough argument,
> you will need to modify the walker.
> 
> I believe in most cases you'll be looking only at a handful of different
> codes, so even if you use a switch stmt in the hook, it will be small enough
> that it will be implemented using a series of ifs.

OK, I withdraw the patch then.

	Thomas
Mikael Morin - Sept. 13, 2010, 10:08 p.m.
Le 13.09.2010 11:51, Jakub Jelinek a écrit :
>
> On Sat, Sep 11, 2010 at 11:00:30PM +0200, Thomas Koenig wrote:
>>> Furthermore, I think having process_pointer_assign etc. special hooks
>>> is not the right thing for a walker.  IMNSHO you should look e.g. at
>>> walk_tree or perhaps better walk_gimple_stmt.
>>> The walker should call one hook (which takes gfc_code *) at each gfc_code
>>> node, and another hook at each gfc_expr node.  The API should have a way
>>> to stop processing and return some value to the caller, have some void *
>>> pointer passed to the callbacks in which the caller can keep some state,
>>> and have a way how to say that subtrees shouldn't be traversed.
>>
>> Why would this be better?
>>
>> Given the complexity of Fortran, wouldn't this lead to lots of code
>> duplication, with a big switch statement both in the statement walker
>> and the handler for the individual statements?
>
> Because otherwise it isn't a general Fortran FE walker, but quite
> specialized one, and each time you need to handle some code that doesn't
> have a hook or where the hook doesn't have generic enough argument,
> you will need to modify the walker.
>
> I believe in most cases you'll be looking only at a handful of different
> codes, so even if you use a switch stmt in the hook, it will be small enough
> that it will be implemented using a series of ifs.
>
> 	Jakub
>
But even then, you would need a default handler for the remaining cases.

Your walker looks like :

void walk_code (gfc_code *c, void (*handler)(gfc_code *))
{
   if (c == NULL)
     return;

   (*handler) (c);
   walk_code (c->next);
}

your handler like :

void handler (gfc_code *c)
{
   switch (c->op)
     {
     case EXEC_ASSIGN:
       /* Do stuff */
       break;

     case EXEC_CALL:
       /* Do other stuff */
       break;

     default:
       default_handler (c);
       break;
     }
}


and you have a default_handler which is essentially the same as Thomas' 
process_code_node. This is not returning any information and is missing 
the early return that you asked but is it something like this that you 
have in mind ?

About adding state information pointers to the callbacks, I think it can 
be added later if needed.
About the way to say that subtrees shouldn't be traversed, I think that 
a different non-recursive default handler could do the same, so it is 
not necessary (for now at least).

Mikael
Jakub Jelinek - Sept. 14, 2010, 2:37 p.m.
On Tue, Sep 14, 2010 at 12:08:12AM +0200, Mikael Morin wrote:
> Your walker looks like :
> 
> void walk_code (gfc_code *c, void (*handler)(gfc_code *))
> {
>   if (c == NULL)
>     return;
> 
>   (*handler) (c);
>   walk_code (c->next);
> }
> 
> your handler like :
> 
> void handler (gfc_code *c)
> {
>   switch (c->op)
>     {
>     case EXEC_ASSIGN:
>       /* Do stuff */
>       break;
> 
>     case EXEC_CALL:
>       /* Do other stuff */
>       break;
> 
>     default:
>       default_handler (c);
>       break;
>     }
> }

I meant something like:

typedef int (*walk_code_fn_t) (gfc_code **, int *, void *);
typedef int (*walk_expr_fn_t) (gfc_expr **, int *, void *);

#define WALK_SUBEXPR(NODE) do { result = walk_expr (&(NODE), exprfn, data); if (result) return result; } while (0)
#define WALK_SUBEXPR_TAIL(NODE) e = &(NODE), continue

int
walk_expr (gfc_expr **e, walk_expr_fn_t exprfn, void *data)
{
  while (*e)
    {
      int walk_subtrees = 1;
      gfc_actual_arglist *a;
      int result = exprfn (e, &walk_subtrees, data);
      if (result)
	return result;
      if (walk_subtrees)
	switch ((*e)->expr_type)
	  {
	  case EXPR_OP:
	    WALK_SUBEXPR ((*e)->value.op1);
	    WALK_SUBEXPR_TAIL ((*e)->value.op2);
	    break;
	  case EXPR_FUNCTION:
	    for (a = (*e)->value.function.actual; a; a = a->next)
	      WALK_SUBEXPR (a->expr);
	    break;
	  case EXPR_COMPCALL:
	  case EXPR_PPC:
	    for (a = (*e)->value.compcall.actual; a; a = a->next)
	      WALK_SUBEXPR (a->expr);
	    break;
	  default:
	    break;
	  }
      return 0;
    }
}

#define WALK_SUBCODE(NODE) do { result = walk_code (&(NODE), codefn, exprfn, data); if (result) return result; } while (0)

int
walk_code (gfc_code **c, walk_code_fn_t codefn, walk_expr_fn_t exprfn, void *data)
{
  while (*c)
    {
      int walk_subtrees = 1;
      int result = codefn (c, &walk_subtrees, data);
      if (result)
	return result;
      if (walk_subtrees)
	{
	  gfc_code *b;
	  for (b = (*c)->block; b; b = b->block)
	    WALK_SUBCODE (b->next);
	  switch ((*c)->code)
	    {
	    ...
	    case EXEC_DO:
	      WALK_SUBEXPR ((*c)->ext.iterator->start);
	      WALK_SUBEXPR ((*c)->ext.iterator->end);
	      WALK_SUBEXPR ((*c)->ext.iterator->step);
	      break;
	    ...
	    case EXEC_OMP_DO:
	    case EXEC_OMP_PARALLEL:
	    case EXEC_OMP_PARALLEL_DO:
	    case EXEC_OMP_PARALLEL_SECTIONS:
	    case EXEC_OMP_PARALLEL_WORKSHARE:                  
	    case EXEC_OMP_SECTIONS:                            
	    case EXEC_OMP_SINGLE:
	    case EXEC_OMP_WORKSHARE:                           
	    case EXEC_OMP_END_SINGLE:                          
	    case EXEC_OMP_TASK:
	      if ((*c)->ext.omp_clauses)
		{
		  WALK_SUBEXPR ((c)->ext.omp_clauses->if_expr);
		  WALK_SUBEXPR ((c)->ext.omp_clauses->num_threads);
		  WALK_SUBEXPR ((c)->ext.omp_clauses->chunk_size);
		}
	      break;
	    }
	}
      c = &(*c)->next;
    }
}

	Jakub

Patch

Index: frontend-passes.c
===================================================================
--- frontend-passes.c	(Revision 164119)
+++ frontend-passes.c	(Arbeitskopie)
@@ -25,19 +25,35 @@  along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "dependency.h"
 
+
+/* A struct for holding the pass-specific functions.  */
+typedef struct process_functions
+{
+  void (*process_assignment) (gfc_code *);
+  void (*process_expr) (gfc_expr *);
+  void (*process_actual_arglist) (gfc_actual_arglist *);
+  void (*process_pointer_assign) (gfc_code *);
+} process_functions;
+
 /* Forward declarations.  */
 
 static void strip_function_call (gfc_expr *);
-static void optimize_namespace (gfc_namespace *);
+static void process_namespace (gfc_namespace *, process_functions *);
 static void optimize_assignment (gfc_code *);
 static void optimize_expr_0 (gfc_expr *);
 static bool optimize_expr (gfc_expr *);
 static bool optimize_op (gfc_expr *);
 static bool optimize_equality (gfc_expr *, bool);
-static void optimize_code (gfc_code *);
-static void optimize_code_node (gfc_code *);
+static void process_code (gfc_code *, process_functions *);
+static void process_code_node (gfc_code *, process_functions *);
 static void optimize_actual_arglist (gfc_actual_arglist *);
 
+struct process_functions optimize_pass =
+  { optimize_assignment,
+    optimize_expr_0,
+    optimize_actual_arglist,
+    NULL };
+
 /* Entry point - run all passes for a namespace.  So far, only an
    optimization pass is run.  */
 
@@ -45,32 +61,32 @@  void
 gfc_run_passes (gfc_namespace *ns)
 {
   if (optimize)
-    optimize_namespace (ns);
+    process_namespace (ns, &optimize_pass);
 }
 
 /* Optimize a namespace, including all contained namespaces.  */
 
 static void
-optimize_namespace (gfc_namespace *ns)
+process_namespace (gfc_namespace *ns, process_functions *pass)
 {
-  optimize_code (ns->code);
+  process_code (ns->code, pass);
 
   for (ns = ns->contained; ns; ns = ns->sibling)
-    optimize_namespace (ns);
+    process_namespace (ns, pass);
 }
 
 static void
-optimize_code (gfc_code *c)
+process_code (gfc_code *c, process_functions *pass)
 {
   for (; c; c = c->next)
-    optimize_code_node (c);
+    process_code_node (c, pass);
 }
 
 
-/* Do the optimizations for a code node.  */
+/* Handle a code node.  */
 
 static void
-optimize_code_node (gfc_code *c)
+process_code_node (gfc_code *c, process_functions *pass)
 {
 
   gfc_forall_iterator *fa;
@@ -80,17 +96,17 @@  static void
   switch (c->op)
     {
     case EXEC_ASSIGN:
-      optimize_assignment (c);
+      pass->process_assignment (c);
       break;
 
     case EXEC_CALL:
     case EXEC_ASSIGN_CALL:
     case EXEC_CALL_PPC:
-      optimize_actual_arglist (c->ext.actual);
+      pass->process_actual_arglist (c->ext.actual);
       break;
 
     case EXEC_ARITHMETIC_IF:
-      optimize_expr_0 (c->expr1);
+      pass->process_expr (c->expr1);
       break;
 
     case EXEC_PAUSE:
@@ -98,50 +114,48 @@  static void
     case EXEC_ERROR_STOP:
     case EXEC_STOP:
     case EXEC_COMPCALL:
-      optimize_expr_0 (c->expr1);
+      pass->process_expr (c->expr1);
       break;
 
     case EXEC_SYNC_ALL:
     case EXEC_SYNC_MEMORY:
     case EXEC_SYNC_IMAGES:
-      optimize_expr_0 (c->expr2);
+      pass->process_expr (c->expr2);
       break;
 
     case EXEC_IF:
       d = c->block;
-      optimize_expr_0 (d->expr1);
-      optimize_code (d->next);
+      pass->process_expr (d->expr1);
+      process_code (d->next, pass);
 
       for (d = d->block; d; d = d->block)
 	{
-	  optimize_expr_0 (d->expr1);
-
-	  optimize_code (d->next);
+	  pass->process_expr (d->expr1);
+	  process_code (d->next, pass);
 	}
 
-
       break;
 
     case EXEC_SELECT:
     case EXEC_SELECT_TYPE:
       d = c->block;
 
-      optimize_expr_0 (c->expr1);
+      pass->process_expr (c->expr1);
 
       for (; d; d = d->block)
-	optimize_code (d->next);
+	process_code (d->next, pass);
 
       break;
 
     case EXEC_WHERE:
       d = c->block;
-      optimize_expr_0 (d->expr1);
-      optimize_code (d->next);
+      pass->process_expr (d->expr1);
+      process_code (d->next, pass);
 
       for (d = d->block; d; d = d->block)
 	{
-	  optimize_expr_0 (d->expr1);
-	  optimize_code (d->next);
+	  pass->process_expr (d->expr1);
+	  process_code (d->next, pass);
 	}
       break;
 
@@ -149,51 +163,138 @@  static void
 
       for (fa = c->ext.forall_iterator; fa; fa = fa->next)
 	{
-	  optimize_expr_0 (fa->start);
-	  optimize_expr_0 (fa->end);
-	  optimize_expr_0 (fa->stride);
+	  pass->process_expr (fa->start);
+	  pass->process_expr (fa->end);
+	  pass->process_expr (fa->stride);
 	}
 
-      if (c->expr1 != NULL)
-	  optimize_expr_0 (c->expr1);
+      pass->process_expr (c->expr1);
+      process_code (c->block->next, pass);
 
-      optimize_code (c->block->next);
-
       break;
 
     case EXEC_CRITICAL:
-      optimize_code (c->block->next);
+      process_code (c->block->next, pass);
       break;
 
     case EXEC_DO:
-      optimize_expr_0 (c->ext.iterator->start);
-      optimize_expr_0 (c->ext.iterator->end);
-      optimize_expr_0 (c->ext.iterator->step);
-      optimize_code (c->block->next);
-
+      pass->process_expr (c->ext.iterator->start);
+      pass->process_expr (c->ext.iterator->end);
+      pass->process_expr (c->ext.iterator->step);
+      process_code (c->block->next, pass);
       break;
 
     case EXEC_DO_WHILE:
-      optimize_expr_0 (c->expr1);
-      optimize_code (c->block->next);
+      pass->process_expr (c->expr1);
+      process_code (c->block->next, pass);
       break;
 
 
     case EXEC_ALLOCATE:
       for (a = c->ext.alloc.list; a; a = a->next)
-	  optimize_expr_0 (a->expr);
+	pass->process_expr (a->expr);
       break;
 
-      /* Todo:  Some of these may need to be optimized, as well.  */
     case EXEC_WRITE:
     case EXEC_READ:
+      pass->process_expr (c->ext.dt->io_unit);
+      pass->process_expr (c->ext.dt->format_expr);
+      pass->process_expr (c->ext.dt->rec);
+      pass->process_expr (c->ext.dt->advance);
+      pass->process_expr (c->ext.dt->iostat);
+      pass->process_expr (c->ext.dt->size);
+      pass->process_expr (c->ext.dt->iomsg);
+      pass->process_expr (c->ext.dt->id);
+      pass->process_expr (c->ext.dt->pos);
+      pass->process_expr (c->ext.dt->asynchronous);
+      pass->process_expr (c->ext.dt->blank);
+      pass->process_expr (c->ext.dt->decimal);
+      pass->process_expr (c->ext.dt->delim);
+      pass->process_expr (c->ext.dt->pad);
+      pass->process_expr (c->ext.dt->round);
+      pass->process_expr (c->ext.dt->sign);
+      pass->process_expr (c->ext.dt->extra_comma);
+      break;
+
     case EXEC_OPEN:
+      pass->process_expr (c->ext.open->unit);
+      pass->process_expr (c->ext.open->file);
+      pass->process_expr (c->ext.open->status);
+      pass->process_expr (c->ext.open->access);
+      pass->process_expr (c->ext.open->form);
+      pass->process_expr (c->ext.open->recl);
+      pass->process_expr (c->ext.open->blank);
+      pass->process_expr (c->ext.open->position);
+      pass->process_expr (c->ext.open->action);
+      pass->process_expr (c->ext.open->delim);
+      pass->process_expr (c->ext.open->pad);
+      pass->process_expr (c->ext.open->iostat);
+      pass->process_expr (c->ext.open->iomsg);
+      pass->process_expr (c->ext.open->convert);
+      pass->process_expr (c->ext.open->decimal);
+      pass->process_expr (c->ext.open->encoding);
+      pass->process_expr (c->ext.open->round);
+      pass->process_expr (c->ext.open->sign);
+      pass->process_expr (c->ext.open->asynchronous);
+      pass->process_expr (c->ext.open->id);
+      pass->process_expr (c->ext.open->newunit);
+      break;
+
     case EXEC_INQUIRE:
+      pass->process_expr (c->ext.inquire->unit);
+      pass->process_expr (c->ext.inquire->file);
+      pass->process_expr (c->ext.inquire->iomsg);
+      pass->process_expr (c->ext.inquire->iostat);
+      pass->process_expr (c->ext.inquire->exist);
+      pass->process_expr (c->ext.inquire->opened);
+      pass->process_expr (c->ext.inquire->number);
+      pass->process_expr (c->ext.inquire->named);
+      pass->process_expr (c->ext.inquire->name);
+      pass->process_expr (c->ext.inquire->access);
+      pass->process_expr (c->ext.inquire->sequential);
+      pass->process_expr (c->ext.inquire->direct);
+      pass->process_expr (c->ext.inquire->form);
+      pass->process_expr (c->ext.inquire->formatted);
+      pass->process_expr (c->ext.inquire->unformatted);
+      pass->process_expr (c->ext.inquire->recl);
+      pass->process_expr (c->ext.inquire->nextrec);
+      pass->process_expr (c->ext.inquire->blank);
+      pass->process_expr (c->ext.inquire->position);
+      pass->process_expr (c->ext.inquire->action);
+      pass->process_expr (c->ext.inquire->read);
+      pass->process_expr (c->ext.inquire->write);
+      pass->process_expr (c->ext.inquire->readwrite);
+      pass->process_expr (c->ext.inquire->delim);
+      pass->process_expr (c->ext.inquire->encoding);
+      pass->process_expr (c->ext.inquire->pad);
+      pass->process_expr (c->ext.inquire->iolength);
+      pass->process_expr (c->ext.inquire->convert);
+      pass->process_expr (c->ext.inquire->strm_pos);
+      pass->process_expr (c->ext.inquire->asynchronous);
+      pass->process_expr (c->ext.inquire->decimal);
+      pass->process_expr (c->ext.inquire->pending);
+      pass->process_expr (c->ext.inquire->id);
+      pass->process_expr (c->ext.inquire->sign);
+      pass->process_expr (c->ext.inquire->size);
+      pass->process_expr (c->ext.inquire->round);
+      break;
+
     case EXEC_REWIND:
     case EXEC_ENDFILE:
     case EXEC_BACKSPACE:
     case EXEC_CLOSE:
     case EXEC_WAIT:
+      pass->process_expr (c->ext.wait->unit);
+      pass->process_expr (c->ext.wait->iostat);
+      pass->process_expr (c->ext.wait->iomsg);
+      pass->process_expr (c->ext.wait->id);
+      break;
+
+    case EXEC_POINTER_ASSIGN:
+      if (pass->process_pointer_assign)
+	pass->process_pointer_assign (c);
+      break;
+
     case EXEC_TRANSFER:
     case EXEC_FLUSH:
     case EXEC_IOLENGTH:
@@ -203,7 +304,6 @@  static void
     case EXEC_ENTRY:
     case EXEC_INIT_ASSIGN:
     case EXEC_LABEL_ASSIGN:
-    case EXEC_POINTER_ASSIGN:
     case EXEC_GOTO:
     case EXEC_CYCLE:
     case EXEC_EXIT:
@@ -228,9 +328,11 @@  static void
     case EXEC_OMP_END_NOWAIT:
     case EXEC_OMP_END_SINGLE:
     case EXEC_DEALLOCATE:
+      break;
+
     case EXEC_DT_END:
       for (d = c->block; d; d = d->block)
-	optimize_code (d->next);
+	process_code (d->next, pass);
       break;
 
     default:
@@ -338,7 +440,7 @@  optimize_assignment (gfc_code * c)
     optimize_binop_array_assignment (c, &rhs, false);
 
   /* If we insert a statement after the current one, the surrounding loop in
-     optimize_code will call optimize_assignment on the inserted statement
+     process_code will call optimize_assignment on the inserted statement
      anyway, so there is no need to call optimize_assignment again.  */
 
   /* All direct optimizations have been done.  Now it's time
@@ -382,6 +484,9 @@  strip_function_call (gfc_expr *e)
 static void
 optimize_expr_0 (gfc_expr * e)
 {
+  if (e == NULL)
+    return;
+
   if (optimize_expr (e))
     gfc_simplify_expr (e, 0);
 
@@ -389,7 +494,7 @@  optimize_expr_0 (gfc_expr * e)
 }
 
 /* Recursive optimization of expressions.
- TODO:  Make this handle many more things.  */
+   TODO:  Make this handle many more things.  */
 
 static bool
 optimize_expr (gfc_expr *e)