gfortran.h (gfc_option_t): Remove warn_aliasing,
[gcc.git] / gcc / fortran / frontend-passes.c
index 4d8c77a12694beb6d27190fcc50afeb083fcb819..799d2fedddc210ca34972423381b3754e9fbd363 100644 (file)
@@ -1,5 +1,5 @@
 /* Pass manager for Fortran front end.
-   Copyright (C) 2010 Free Software Foundation, Inc.
+   Copyright (C) 2010-2014 Free Software Foundation, Inc.
    Contributed by Thomas König.
 
 This file is part of GCC.
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
 #include "gfortran.h"
 #include "arith.h"
 #include "flags.h"
@@ -36,16 +37,19 @@ static bool optimize_op (gfc_expr *);
 static bool optimize_comparison (gfc_expr *, gfc_intrinsic_op);
 static bool optimize_trim (gfc_expr *);
 static bool optimize_lexical_comparison (gfc_expr *);
+static void optimize_minmaxloc (gfc_expr **);
+static bool is_empty_string (gfc_expr *e);
+static void doloop_warn (gfc_namespace *);
+static void optimize_reduction (gfc_namespace *);
+static int callback_reduction (gfc_expr **, int *, void *);
 
 /* How deep we are inside an argument list.  */
 
 static int count_arglist;
 
-/* Pointer to an array of gfc_expr ** we operate on, plus its size
-   and counter.  */
+/* Vector of gfc_expr ** we operate on.  */
 
-static gfc_expr ***expr_array;
-static int expr_size, expr_count;
+static vec<gfc_expr **> expr_array;
 
 /* Pointer to the gfc_code we currently work on - to be able to insert
    a block before the statement.  */
@@ -59,24 +63,55 @@ static gfc_code *inserted_block, **changed_statement;
 
 /* The namespace we are currently dealing with.  */
 
-gfc_namespace *current_ns;
+static gfc_namespace *current_ns;
 
-/* Entry point - run all passes for a namespace.  So far, only an
-   optimization pass is run.  */
+/* If we are within any forall loop.  */
+
+static int forall_level;
+
+/* Keep track of whether we are within an OMP workshare.  */
+
+static bool in_omp_workshare;
+
+/* Keep track of iterators for array constructors.  */
+
+static int iterator_level;
+
+/* Keep track of DO loop levels.  */
+
+static vec<gfc_code *> doloop_list;
+
+static int doloop_level;
+
+/* Vector of gfc_expr * to keep track of DO loops.  */
+
+struct my_struct *evec;
+
+/* Keep track of association lists.  */
+
+static bool in_assoc_list;
+
+/* Entry point - run all passes for a namespace.  */
 
 void
 gfc_run_passes (gfc_namespace *ns)
 {
+
+  /* Warn about dubious DO loops where the index might
+     change.  */
+
+  doloop_level = 0;
+  doloop_warn (ns);
+  doloop_list.release ();
+
   if (gfc_option.flag_frontend_optimize)
     {
-      expr_size = 20;
-      expr_array = XNEWVEC(gfc_expr **, expr_size);
-
       optimize_namespace (ns);
+      optimize_reduction (ns);
       if (gfc_option.dump_fortran_optimized)
        gfc_dump_parse_tree (ns, stdout);
 
-      XDELETEVEC (expr_array);
+      expr_array.release ();
     }
 }
 
@@ -98,6 +133,10 @@ optimize_code (gfc_code **c, int *walk_subtrees ATTRIBUTE_UNUSED,
   else
     count_arglist = 0;
 
+  current_code = c;
+  inserted_block = NULL;
+  changed_statement = NULL;
+
   if (op == EXEC_ASSIGN)
     optimize_assignment (*c);
   return 0;
@@ -129,12 +168,179 @@ optimize_expr (gfc_expr **e, int *walk_subtrees ATTRIBUTE_UNUSED,
   if ((*e)->expr_type == EXPR_OP && optimize_op (*e))
     gfc_simplify_expr (*e, 0);
 
+  if ((*e)->expr_type == EXPR_FUNCTION && (*e)->value.function.isym)
+    switch ((*e)->value.function.isym->id)
+      {
+      case GFC_ISYM_MINLOC:
+      case GFC_ISYM_MAXLOC:
+       optimize_minmaxloc (e);
+       break;
+      default:
+       break;
+      }
+
   if (function_expr)
     count_arglist --;
 
   return 0;
 }
 
+/* Auxiliary function to handle the arguments to reduction intrnisics.  If the
+   function is a scalar, just copy it; otherwise returns the new element, the
+   old one can be freed.  */
+
+static gfc_expr *
+copy_walk_reduction_arg (gfc_constructor *c, gfc_expr *fn)
+{
+  gfc_expr *fcn, *e = c->expr;
+
+  fcn = gfc_copy_expr (e);
+  if (c->iterator)
+    {
+      gfc_constructor_base newbase;
+      gfc_expr *new_expr;
+      gfc_constructor *new_c;
+
+      newbase = NULL;
+      new_expr = gfc_get_expr ();
+      new_expr->expr_type = EXPR_ARRAY;
+      new_expr->ts = e->ts;
+      new_expr->where = e->where;
+      new_expr->rank = 1;
+      new_c = gfc_constructor_append_expr (&newbase, fcn, &(e->where));
+      new_c->iterator = c->iterator;
+      new_expr->value.constructor = newbase;
+      c->iterator = NULL;
+
+      fcn = new_expr;
+    }
+
+  if (fcn->rank != 0)
+    {
+      gfc_isym_id id = fn->value.function.isym->id;
+
+      if (id == GFC_ISYM_SUM || id == GFC_ISYM_PRODUCT)
+       fcn = gfc_build_intrinsic_call (current_ns, id,
+                                       fn->value.function.isym->name,
+                                       fn->where, 3, fcn, NULL, NULL);
+      else if (id == GFC_ISYM_ANY || id == GFC_ISYM_ALL)
+       fcn = gfc_build_intrinsic_call (current_ns, id,
+                                       fn->value.function.isym->name,
+                                       fn->where, 2, fcn, NULL);
+      else
+       gfc_internal_error ("Illegal id in copy_walk_reduction_arg");
+
+      fcn->symtree->n.sym->attr.access = ACCESS_PRIVATE;
+    }
+
+  return fcn;
+}
+
+/* Callback function for optimzation of reductions to scalars.  Transform ANY
+   ([f1,f2,f3, ...]) to f1 .or. f2 .or. f3 .or. ..., with ANY, SUM and PRODUCT
+   correspondingly.  Handly only the simple cases without MASK and DIM.  */
+
+static int
+callback_reduction (gfc_expr **e, int *walk_subtrees ATTRIBUTE_UNUSED,
+                   void *data ATTRIBUTE_UNUSED)
+{
+  gfc_expr *fn, *arg;
+  gfc_intrinsic_op op;
+  gfc_isym_id id;
+  gfc_actual_arglist *a;
+  gfc_actual_arglist *dim;
+  gfc_constructor *c;
+  gfc_expr *res, *new_expr;
+  gfc_actual_arglist *mask;
+
+  fn = *e;
+
+  if (fn->rank != 0 || fn->expr_type != EXPR_FUNCTION
+      || fn->value.function.isym == NULL)
+    return 0;
+
+  id = fn->value.function.isym->id;
+
+  if (id != GFC_ISYM_SUM && id != GFC_ISYM_PRODUCT
+      && id != GFC_ISYM_ANY && id != GFC_ISYM_ALL)
+    return 0;
+
+  a = fn->value.function.actual;
+
+  /* Don't handle MASK or DIM.  */
+
+  dim = a->next;
+
+  if (dim->expr != NULL)
+    return 0;
+
+  if (id == GFC_ISYM_SUM || id == GFC_ISYM_PRODUCT)
+    {
+      mask = dim->next;
+      if ( mask->expr != NULL)
+       return 0;
+    }
+
+  arg = a->expr;
+
+  if (arg->expr_type != EXPR_ARRAY)
+    return 0;
+
+  switch (id)
+    {
+    case GFC_ISYM_SUM:
+      op = INTRINSIC_PLUS;
+      break;
+
+    case GFC_ISYM_PRODUCT:
+      op = INTRINSIC_TIMES;
+      break;
+
+    case GFC_ISYM_ANY:
+      op = INTRINSIC_OR;
+      break;
+
+    case GFC_ISYM_ALL:
+      op = INTRINSIC_AND;
+      break;
+
+    default:
+      return 0;
+    }
+
+  c = gfc_constructor_first (arg->value.constructor);
+
+  /* Don't do any simplififcation if we have
+     - no element in the constructor or
+     - only have a single element in the array which contains an
+     iterator.  */
+
+  if (c == NULL)
+    return 0;
+
+  res = copy_walk_reduction_arg (c, fn);
+
+  c = gfc_constructor_next (c);
+  while (c)
+    {
+      new_expr = gfc_get_expr ();
+      new_expr->ts = fn->ts;
+      new_expr->expr_type = EXPR_OP;
+      new_expr->rank = fn->rank;
+      new_expr->where = fn->where;
+      new_expr->value.op.op = op;
+      new_expr->value.op.op1 = res;
+      new_expr->value.op.op2 = copy_walk_reduction_arg (c, fn);
+      res = new_expr;
+      c = gfc_constructor_next (c);
+    }
+
+  gfc_simplify_expr (res, 0);
+  *e = res;
+  gfc_free_expr (fn);
+
+  return 0;
+}
 
 /* Callback function for common function elimination, called from cfe_expr_0.
    Put all eligible function expressions into expr_array.  */
@@ -153,6 +359,19 @@ cfe_register_funcs (gfc_expr **e, int *walk_subtrees ATTRIBUTE_UNUSED,
          || (*e)->ts.u.cl->length->expr_type != EXPR_CONSTANT))
     return 0;
 
+  /* We don't do function elimination within FORALL statements, it can
+     lead to wrong-code in certain circumstances.  */
+
+  if (forall_level > 0)
+    return 0;
+
+  /* Function elimination inside an iterator could lead to functions which
+     depend on iterator variables being moved outside.  FIXME: We should check
+     if the functions do indeed depend on the iterator variable.  */
+
+  if (iterator_level > 0)
+    return 0;
+
   /* If we don't know the shape at compile time, we create an allocatable
      temporary variable to hold the intermediate result, but only if
      allocation on assignment is active.  */
@@ -181,8 +400,8 @@ cfe_register_funcs (gfc_expr **e, int *walk_subtrees ATTRIBUTE_UNUSED,
       /* Conversions are handled on the fly by the middle end,
         transpose during trans-* stages and TRANSFER by the middle end.  */
       if ((*e)->value.function.isym->id == GFC_ISYM_CONVERSION
-         || (*e)->value.function.isym->id == GFC_ISYM_TRANSPOSE
-         || (*e)->value.function.isym->id == GFC_ISYM_TRANSFER)
+         || (*e)->value.function.isym->id == GFC_ISYM_TRANSFER
+         || gfc_inline_intrinsic_function_p (*e))
        return 0;
 
       /* Don't create an array temporary for elemental functions,
@@ -195,21 +414,30 @@ cfe_register_funcs (gfc_expr **e, int *walk_subtrees ATTRIBUTE_UNUSED,
        return 0;
     }
 
-  if (expr_count >= expr_size)
-    {
-      expr_size += expr_size;
-      expr_array = XRESIZEVEC(gfc_expr **, expr_array, expr_size);
-    }
-  expr_array[expr_count] = e;
-  expr_count ++;
+  expr_array.safe_push (e);
   return 0;
 }
 
+/* Auxiliary function to check if an expression is a temporary created by
+   create var.  */
+
+static bool
+is_fe_temp (gfc_expr *e)
+{
+  if (e->expr_type != EXPR_VARIABLE)
+    return false;
+
+  return e->symtree->n.sym->attr.fe_temp;
+}
+
+
 /* Returns a new expression (a variable) to be used in place of the old one,
-   with an an assignment statement before the current statement to set
+   with an assignment statement before the current statement to set
    the value of the variable. Creates a new BLOCK for the statement if
    that hasn't already been done and puts the statement, plus the
-   newly created variables, in that block.  */
+   newly created variables, in that block.  Special cases:  If the
+   expression is constant or a temporary which has already
+   been created, just copy it.  */
 
 static gfc_expr*
 create_var (gfc_expr * e)
@@ -223,6 +451,9 @@ create_var (gfc_expr * e)
   gfc_namespace *ns;
   int i;
 
+  if (e->expr_type == EXPR_CONSTANT || is_fe_temp (e))
+    return gfc_copy_expr (e);
+
   /* If the block hasn't already been created, do so.  */
   if (inserted_block == NULL)
     {
@@ -234,11 +465,22 @@ create_var (gfc_expr * e)
       inserted_block->ext.block.assoc = NULL;
 
       ns->code = *current_code;
+
+      /* If the statement has a label,  make sure it is transferred to
+        the newly created block.  */
+
+      if ((*current_code)->here) 
+       {
+         inserted_block->here = (*current_code)->here;
+         (*current_code)->here = NULL;
+       }
+
       inserted_block->next = (*current_code)->next;
       changed_statement = &(inserted_block->ext.block.ns->code);
       (*current_code)->next = NULL;
       /* Insert the BLOCK at the right position.  */
       *current_code = inserted_block;
+      ns->parent = current_ns;
     }
   else
     ns = inserted_block->ext.block.ns;
@@ -258,7 +500,7 @@ create_var (gfc_expr * e)
       if (e->shape == NULL)
        {
          /* We don't know the shape at compile time, so we use an
-            allocatable. */
+            allocatable.  */
          symbol->as->type = AS_DEFERRED;
          symbol->attr.allocatable = 1;
        }
@@ -286,6 +528,7 @@ create_var (gfc_expr * e)
   symbol->attr.flavor = FL_VARIABLE;
   symbol->attr.referenced = 1;
   symbol->attr.dimension = e->rank > 0;
+  symbol->attr.fe_temp = 1;
   gfc_commit_symbol (symbol);
 
   result = gfc_get_expr ();
@@ -301,8 +544,9 @@ create_var (gfc_expr * e)
       result->ref->type = REF_ARRAY;
       result->ref->u.ar.type = AR_FULL;
       result->ref->u.ar.where = e->where;
-      result->ref->u.ar.as = symbol->as;
-      if (gfc_option.warn_array_temp)
+      result->ref->u.ar.as = symbol->ts.type == BT_CLASS
+                            ? CLASS_DATA (symbol)->as : symbol->as;
+      if (warn_array_temporaries)
        gfc_warning ("Creating array temporary at %L", &(e->where));
     }
 
@@ -321,7 +565,7 @@ create_var (gfc_expr * e)
 /* Warn about function elimination.  */
 
 static void
-warn_function_elimination (gfc_expr *e)
+do_warn_function_elimination (gfc_expr *e)
 {
   if (e->expr_type != EXPR_FUNCTION)
     return;
@@ -343,37 +587,46 @@ cfe_expr_0 (gfc_expr **e, int *walk_subtrees,
 {
   int i,j;
   gfc_expr *newvar;
+  gfc_expr **ei, **ej;
 
-  expr_count = 0;
+  /* Don't do this optimization within OMP workshare.  */
+
+  if (in_omp_workshare)
+    {
+      *walk_subtrees = 0;
+      return 0;
+    }
+
+  expr_array.release ();
 
   gfc_expr_walker (e, cfe_register_funcs, NULL);
 
   /* Walk through all the functions.  */
 
-  for (i=1; i<expr_count; i++)
+  FOR_EACH_VEC_ELT_FROM (expr_array, i, ei, 1)
     {
       /* Skip if the function has been replaced by a variable already.  */
-      if ((*(expr_array[i]))->expr_type == EXPR_VARIABLE)
+      if ((*ei)->expr_type == EXPR_VARIABLE)
        continue;
 
       newvar = NULL;
       for (j=0; j<i; j++)
        {
-         if (gfc_dep_compare_functions(*(expr_array[i]),
-                                       *(expr_array[j]), true) == 0)
+         ej = expr_array[j];
+         if (gfc_dep_compare_functions (*ei, *ej, true) == 0)
            {
              if (newvar == NULL)
-               newvar = create_var (*(expr_array[i]));
+               newvar = create_var (*ei);
 
-             if (gfc_option.warn_function_elimination)
-               warn_function_elimination (*(expr_array[j]));
+             if (warn_function_elimination)
+               do_warn_function_elimination (*ej);
 
-             free (*(expr_array[j]));
-             *(expr_array[j]) = gfc_copy_expr (newvar);
+             free (*ej);
+             *ej = gfc_copy_expr (newvar);
            }
        }
       if (newvar)
-       *(expr_array[i]) = newvar;
+       *ei = newvar;
     }
 
   /* We did all the necessary walking in this function.  */
@@ -386,15 +639,190 @@ cfe_expr_0 (gfc_expr **e, int *walk_subtrees,
    to insert statements as needed.  */
 
 static int
-cfe_code (gfc_code **c, int *walk_subtrees ATTRIBUTE_UNUSED,
-         void *data ATTRIBUTE_UNUSED)
+cfe_code (gfc_code **c, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 {
   current_code = c;
   inserted_block = NULL;
   changed_statement = NULL;
+
+  /* Do not do anything inside a WHERE statement; scalar assignments, BLOCKs
+     and allocation on assigment are prohibited inside WHERE, and finally
+     masking an expression would lead to wrong-code when replacing
+
+     WHERE (a>0)
+       b = sum(foo(a) + foo(a))
+     END WHERE
+
+     with
+
+     WHERE (a > 0)
+       tmp = foo(a)
+       b = sum(tmp + tmp)
+     END WHERE
+*/
+
+  if ((*c)->op == EXEC_WHERE)
+    {
+      *walk_subtrees = 0;
+      return 0;
+    }
+  
+
+  return 0;
+}
+
+/* Dummy function for expression call back, for use when we
+   really don't want to do any walking.  */
+
+static int
+dummy_expr_callback (gfc_expr **e ATTRIBUTE_UNUSED, int *walk_subtrees,
+                    void *data ATTRIBUTE_UNUSED)
+{
+  *walk_subtrees = 0;
+  return 0;
+}
+
+/* Dummy function for code callback, for use when we really
+   don't want to do anything.  */
+int
+gfc_dummy_code_callback (gfc_code **e ATTRIBUTE_UNUSED,
+                        int *walk_subtrees ATTRIBUTE_UNUSED,
+                        void *data ATTRIBUTE_UNUSED)
+{
+  return 0;
+}
+
+/* Code callback function for converting
+   do while(a)
+   end do
+   into the equivalent
+   do
+     if (.not. a) exit
+   end do
+   This is because common function elimination would otherwise place the
+   temporary variables outside the loop.  */
+
+static int
+convert_do_while (gfc_code **c, int *walk_subtrees ATTRIBUTE_UNUSED,
+                 void *data ATTRIBUTE_UNUSED)
+{
+  gfc_code *co = *c;
+  gfc_code *c_if1, *c_if2, *c_exit;
+  gfc_code *loopblock;
+  gfc_expr *e_not, *e_cond;
+
+  if (co->op != EXEC_DO_WHILE)
+    return 0;
+
+  if (co->expr1 == NULL || co->expr1->expr_type == EXPR_CONSTANT)
+    return 0;
+
+  e_cond = co->expr1;
+
+  /* Generate the condition of the if statement, which is .not. the original
+     statement.  */
+  e_not = gfc_get_expr ();
+  e_not->ts = e_cond->ts;
+  e_not->where = e_cond->where;
+  e_not->expr_type = EXPR_OP;
+  e_not->value.op.op = INTRINSIC_NOT;
+  e_not->value.op.op1 = e_cond;
+
+  /* Generate the EXIT statement.  */
+  c_exit = XCNEW (gfc_code);
+  c_exit->op = EXEC_EXIT;
+  c_exit->ext.which_construct = co;
+  c_exit->loc = co->loc;
+
+  /* Generate the IF statement.  */
+  c_if2 = XCNEW (gfc_code);
+  c_if2->op = EXEC_IF;
+  c_if2->expr1 = e_not;
+  c_if2->next = c_exit;
+  c_if2->loc = co->loc;
+
+  /* ... plus the one to chain it to.  */
+  c_if1 = XCNEW (gfc_code);
+  c_if1->op = EXEC_IF;
+  c_if1->block = c_if2;
+  c_if1->loc = co->loc;
+
+  /* Make the DO WHILE loop into a DO block by replacing the condition
+     with a true constant.  */
+  co->expr1 = gfc_get_logical_expr (gfc_default_integer_kind, &co->loc, true);
+
+  /* Hang the generated if statement into the loop body.  */
+
+  loopblock = co->block->next;
+  co->block->next = c_if1;
+  c_if1->next = loopblock;
+
   return 0;
 }
 
+/* Code callback function for converting
+   if (a) then
+   ...
+   else if (b) then
+   end if
+
+   into
+   if (a) then
+   else
+     if (b) then
+     end if
+   end if
+
+   because otherwise common function elimination would place the BLOCKs
+   into the wrong place.  */
+
+static int
+convert_elseif (gfc_code **c, int *walk_subtrees ATTRIBUTE_UNUSED,
+               void *data ATTRIBUTE_UNUSED)
+{
+  gfc_code *co = *c;
+  gfc_code *c_if1, *c_if2, *else_stmt;
+
+  if (co->op != EXEC_IF)
+    return 0;
+
+  /* This loop starts out with the first ELSE statement.  */
+  else_stmt = co->block->block;
+
+  while (else_stmt != NULL)
+    {
+      gfc_code *next_else;
+
+      /* If there is no condition, we're done.  */
+      if (else_stmt->expr1 == NULL)
+       break;
+
+      next_else = else_stmt->block;
+
+      /* Generate the new IF statement.  */
+      c_if2 = XCNEW (gfc_code);
+      c_if2->op = EXEC_IF;
+      c_if2->expr1 = else_stmt->expr1;
+      c_if2->next = else_stmt->next;
+      c_if2->loc = else_stmt->loc;
+      c_if2->block = next_else;
+
+      /* ... plus the one to chain it to.  */
+      c_if1 = XCNEW (gfc_code);
+      c_if1->op = EXEC_IF;
+      c_if1->block = c_if2;
+      c_if1->loc = else_stmt->loc;
+
+      /* Insert the new IF after the ELSE.  */
+      else_stmt->expr1 = NULL;
+      else_stmt->next = c_if1;
+      else_stmt->block = NULL;
+
+      else_stmt = next_else;
+    }
+  /*  Don't walk subtrees.  */
+  return 0;
+}
 /* Optimize a namespace, including all contained namespaces.  */
 
 static void
@@ -402,12 +830,37 @@ optimize_namespace (gfc_namespace *ns)
 {
 
   current_ns = ns;
+  forall_level = 0;
+  iterator_level = 0;
+  in_assoc_list = false;
+  in_omp_workshare = false;
 
+  gfc_code_walker (&ns->code, convert_do_while, dummy_expr_callback, NULL);
+  gfc_code_walker (&ns->code, convert_elseif, dummy_expr_callback, NULL);
   gfc_code_walker (&ns->code, cfe_code, cfe_expr_0, NULL);
   gfc_code_walker (&ns->code, optimize_code, optimize_expr, NULL);
 
+  /* BLOCKs are handled in the expression walker below.  */
   for (ns = ns->contained; ns; ns = ns->sibling)
-    optimize_namespace (ns);
+    {
+      if (ns->code == NULL || ns->code->op != EXEC_BLOCK)
+       optimize_namespace (ns);
+    }
+}
+
+static void
+optimize_reduction (gfc_namespace *ns)
+{
+  current_ns = ns;
+  gfc_code_walker (&ns->code, gfc_dummy_code_callback,
+                  callback_reduction, NULL);
+
+/* BLOCKs are handled in the expression walker below.  */
+  for (ns = ns->contained; ns; ns = ns->sibling)
+    {
+      if (ns->code == NULL || ns->code->op != EXEC_BLOCK)
+       optimize_reduction (ns);
+    }
 }
 
 /* Replace code like
@@ -439,6 +892,10 @@ optimize_binop_array_assignment (gfc_code *c, gfc_expr **rhs, bool seen_op)
            return true;
          break;
 
+       case INTRINSIC_CONCAT:
+         /* Do not do string concatenations.  */
+         break;
+
        default:
          /* Binary operators.  */
          if (optimize_binop_array_assignment (c, &e->value.op.op1, true))
@@ -459,7 +916,8 @@ optimize_binop_array_assignment (gfc_code *c, gfc_expr **rhs, bool seen_op)
           && ! (e->value.function.isym
                 && (e->value.function.isym->elemental
                     || e->ts.type != c->expr1->ts.type
-                    || e->ts.kind != c->expr1->ts.kind)))
+                    || e->ts.kind != c->expr1->ts.kind))
+          && ! gfc_inline_intrinsic_function_p (e))
     {
 
       gfc_code *n;
@@ -525,10 +983,15 @@ optimize_assignment (gfc_code * c)
   lhs = c->expr1;
   rhs = c->expr2;
 
-  /* Optimize away a = trim(b), where a is a character variable.  */
+  if (lhs->ts.type == BT_CHARACTER && !lhs->ts.deferred)
+    {
+      /* Optimize  a = trim(b)  to  a = b.  */
+      remove_trim (rhs);
 
-  if (lhs->ts.type == BT_CHARACTER)
-    remove_trim (rhs);
+      /* Replace a = '   ' by a = '' to optimize away a memcpy.  */
+      if (is_empty_string (rhs))
+       rhs->value.character.length = 0;
+    }
 
   if (lhs->rank > 0 && gfc_check_dependency (lhs, rhs, true) == 0)
     optimize_binop_array_assignment (c, &rhs, false);
@@ -590,28 +1053,228 @@ optimize_lexical_comparison (gfc_expr *e)
   return false;
 }
 
+/* Combine stuff like [a]>b into [a>b], for easier optimization later.  Do not
+   do CHARACTER because of possible pessimization involving character
+   lengths.  */
+
+static bool
+combine_array_constructor (gfc_expr *e)
+{
+
+  gfc_expr *op1, *op2;
+  gfc_expr *scalar;
+  gfc_expr *new_expr;
+  gfc_constructor *c, *new_c;
+  gfc_constructor_base oldbase, newbase;
+  bool scalar_first;
+
+  /* Array constructors have rank one.  */
+  if (e->rank != 1)
+    return false;
+
+  /* Don't try to combine association lists, this makes no sense
+     and leads to an ICE.  */
+  if (in_assoc_list)
+    return false;
+
+  op1 = e->value.op.op1;
+  op2 = e->value.op.op2;
+
+  if (op1->expr_type == EXPR_ARRAY && op2->rank == 0)
+    scalar_first = false;
+  else if (op2->expr_type == EXPR_ARRAY && op1->rank == 0)
+    {
+      scalar_first = true;
+      op1 = e->value.op.op2;
+      op2 = e->value.op.op1;
+    }
+  else
+    return false;
+
+  if (op2->ts.type == BT_CHARACTER)
+    return false;
+
+  scalar = create_var (gfc_copy_expr (op2));
+
+  oldbase = op1->value.constructor;
+  newbase = NULL;
+  e->expr_type = EXPR_ARRAY;
+
+  for (c = gfc_constructor_first (oldbase); c;
+       c = gfc_constructor_next (c))
+    {
+      new_expr = gfc_get_expr ();
+      new_expr->ts = e->ts;
+      new_expr->expr_type = EXPR_OP;
+      new_expr->rank = c->expr->rank;
+      new_expr->where = c->where;
+      new_expr->value.op.op = e->value.op.op;
+
+      if (scalar_first)
+       {
+         new_expr->value.op.op1 = gfc_copy_expr (scalar);
+         new_expr->value.op.op2 = gfc_copy_expr (c->expr);
+       }
+      else
+       {
+         new_expr->value.op.op1 = gfc_copy_expr (c->expr);
+         new_expr->value.op.op2 = gfc_copy_expr (scalar);
+       }
+
+      new_c = gfc_constructor_append_expr (&newbase, new_expr, &(e->where));
+      new_c->iterator = c->iterator;
+      c->iterator = NULL;
+    }
+
+  gfc_free_expr (op1);
+  gfc_free_expr (op2);
+  gfc_free_expr (scalar);
+
+  e->value.constructor = newbase;
+  return true;
+}
+
+/* Change (-1)**k into 1-ishift(iand(k,1),1) and
+ 2**k into ishift(1,k) */
+
+static bool
+optimize_power (gfc_expr *e)
+{
+  gfc_expr *op1, *op2;
+  gfc_expr *iand, *ishft;
+
+  if (e->ts.type != BT_INTEGER)
+    return false;
+
+  op1 = e->value.op.op1;
+
+  if (op1 == NULL || op1->expr_type != EXPR_CONSTANT)
+    return false;
+
+  if (mpz_cmp_si (op1->value.integer, -1L) == 0)
+    {
+      gfc_free_expr (op1);
+
+      op2 = e->value.op.op2;
+
+      if (op2 == NULL)
+       return false;
+
+      iand = gfc_build_intrinsic_call (current_ns, GFC_ISYM_IAND,
+                                      "_internal_iand", e->where, 2, op2,
+                                      gfc_get_int_expr (e->ts.kind,
+                                                        &e->where, 1));
+                                  
+      ishft = gfc_build_intrinsic_call (current_ns, GFC_ISYM_ISHFT,
+                                       "_internal_ishft", e->where, 2, iand,
+                                       gfc_get_int_expr (e->ts.kind,
+                                                         &e->where, 1));
+
+      e->value.op.op = INTRINSIC_MINUS;
+      e->value.op.op1 = gfc_get_int_expr (e->ts.kind, &e->where, 1);
+      e->value.op.op2 = ishft;
+      return true;
+    }
+  else if (mpz_cmp_si (op1->value.integer, 2L) == 0)
+    {
+      gfc_free_expr (op1);
+
+      op2 = e->value.op.op2;
+      if (op2 == NULL)
+       return false;
+
+      ishft = gfc_build_intrinsic_call (current_ns, GFC_ISYM_ISHFT,
+                                       "_internal_ishft", e->where, 2,
+                                       gfc_get_int_expr (e->ts.kind,
+                                                         &e->where, 1),
+                                       op2);
+      *e = *ishft;
+      return true;
+    }
+
+  else if (mpz_cmp_si (op1->value.integer, 1L) == 0)
+    {
+      op2 = e->value.op.op2;
+      if (op2 == NULL)
+       return false;
+
+      gfc_free_expr (op1);
+      gfc_free_expr (op2);
+
+      e->expr_type = EXPR_CONSTANT;
+      e->value.op.op1 = NULL;
+      e->value.op.op2 = NULL;
+      mpz_init_set_si (e->value.integer, 1);
+      /* Typespec and location are still OK.  */
+      return true;
+    }
+
+  return false;
+}
+
 /* Recursive optimization of operators.  */
 
 static bool
 optimize_op (gfc_expr *e)
 {
+  bool changed;
+
   gfc_intrinsic_op op = e->value.op.op;
 
+  changed = false;
+
+  /* Only use new-style comparisons.  */
+  switch(op)
+    {
+    case INTRINSIC_EQ_OS:
+      op = INTRINSIC_EQ;
+      break;
+
+    case INTRINSIC_GE_OS:
+      op = INTRINSIC_GE;
+      break;
+
+    case INTRINSIC_LE_OS:
+      op = INTRINSIC_LE;
+      break;
+
+    case INTRINSIC_NE_OS:
+      op = INTRINSIC_NE;
+      break;
+
+    case INTRINSIC_GT_OS:
+      op = INTRINSIC_GT;
+      break;
+
+    case INTRINSIC_LT_OS:
+      op = INTRINSIC_LT;
+      break;
+
+    default:
+      break;
+    }
+
   switch (op)
     {
     case INTRINSIC_EQ:
-    case INTRINSIC_EQ_OS:
     case INTRINSIC_GE:
-    case INTRINSIC_GE_OS:
     case INTRINSIC_LE:
-    case INTRINSIC_LE_OS:
     case INTRINSIC_NE:
-    case INTRINSIC_NE_OS:
     case INTRINSIC_GT:
-    case INTRINSIC_GT_OS:
     case INTRINSIC_LT:
-    case INTRINSIC_LT_OS:
-      return optimize_comparison (e, op);
+      changed = optimize_comparison (e, op);
+
+      /* Fall through */
+      /* Look at array constructors.  */
+    case INTRINSIC_PLUS:
+    case INTRINSIC_MINUS:
+    case INTRINSIC_TIMES:
+    case INTRINSIC_DIVIDE:
+      return combine_array_constructor (e) || changed;
+
+    case INTRINSIC_POWER:
+      return optimize_power (e);
+      break;
 
     default:
       break;
@@ -620,6 +1283,63 @@ optimize_op (gfc_expr *e)
   return false;
 }
 
+
+/* Return true if a constant string contains only blanks.  */
+
+static bool
+is_empty_string (gfc_expr *e)
+{
+  int i;
+
+  if (e->ts.type != BT_CHARACTER || e->expr_type != EXPR_CONSTANT)
+    return false;
+
+  for (i=0; i < e->value.character.length; i++)
+    {
+      if (e->value.character.string[i] != ' ')
+       return false;
+    }
+
+  return true;
+}
+
+
+/* Insert a call to the intrinsic len_trim. Use a different name for
+   the symbol tree so we don't run into trouble when the user has
+   renamed len_trim for some reason.  */
+
+static gfc_expr*
+get_len_trim_call (gfc_expr *str, int kind)
+{
+  gfc_expr *fcn;
+  gfc_actual_arglist *actual_arglist, *next;
+
+  fcn = gfc_get_expr ();
+  fcn->expr_type = EXPR_FUNCTION;
+  fcn->value.function.isym = gfc_intrinsic_function_by_id (GFC_ISYM_LEN_TRIM);
+  actual_arglist = gfc_get_actual_arglist ();
+  actual_arglist->expr = str;
+  next = gfc_get_actual_arglist ();
+  next->expr = gfc_get_int_expr (gfc_default_integer_kind, NULL, kind);
+  actual_arglist->next = next;
+
+  fcn->value.function.actual = actual_arglist;
+  fcn->where = str->where;
+  fcn->ts.type = BT_INTEGER;
+  fcn->ts.kind = gfc_charlen_int_kind;
+
+  gfc_get_sym_tree ("__internal_len_trim", current_ns, &fcn->symtree, false);
+  fcn->symtree->n.sym->ts = fcn->ts;
+  fcn->symtree->n.sym->attr.flavor = FL_PROCEDURE;
+  fcn->symtree->n.sym->attr.function = 1;
+  fcn->symtree->n.sym->attr.elemental = 1;
+  fcn->symtree->n.sym->attr.referenced = 1;
+  fcn->symtree->n.sym->attr.access = ACCESS_PRIVATE;
+  gfc_commit_symbol (fcn->symtree->n.sym);
+
+  return fcn;
+}
+
 /* Optimize expressions for equality.  */
 
 static bool
@@ -640,7 +1360,7 @@ optimize_comparison (gfc_expr *e, gfc_intrinsic_op op)
     }
   else if (e->expr_type == EXPR_FUNCTION)
     {
-      /* One of the lexical comparision functions.  */
+      /* One of the lexical comparison functions.  */
       firstarg = e->value.function.actual;
       secondarg = firstarg->next;
       op1 = firstarg->expr;
@@ -663,6 +1383,45 @@ optimize_comparison (gfc_expr *e, gfc_intrinsic_op op)
   if (e->rank > 0)
     return change;
 
+  /* Replace a == '' with len_trim(a) == 0 and a /= '' with
+     len_trim(a) != 0 */
+  if (op1->ts.type == BT_CHARACTER && op2->ts.type == BT_CHARACTER
+      && (op == INTRINSIC_EQ || op == INTRINSIC_NE))
+    {
+      bool empty_op1, empty_op2;
+      empty_op1 = is_empty_string (op1);
+      empty_op2 = is_empty_string (op2);
+
+      if (empty_op1 || empty_op2)
+       {
+         gfc_expr *fcn;
+         gfc_expr *zero;
+         gfc_expr *str;
+
+         /* This can only happen when an error for comparing
+            characters of different kinds has already been issued.  */
+         if (empty_op1 && empty_op2)
+           return false;
+
+         zero = gfc_get_int_expr (gfc_charlen_int_kind, &e->where, 0);
+         str = empty_op1 ? op2 : op1;
+
+         fcn = get_len_trim_call (str, gfc_charlen_int_kind);
+
+
+         if (empty_op1)
+           gfc_free_expr (op1);
+         else
+           gfc_free_expr (op2);
+
+         op1 = fcn;
+         op2 = zero;
+         e->value.op.op1 = fcn;
+         e->value.op.op2 = zero;
+       }
+    }
+
+
   /* Don't compare REAL or COMPLEX expressions when honoring NaNs.  */
 
   if (flag_finite_math_only
@@ -670,12 +1429,14 @@ optimize_comparison (gfc_expr *e, gfc_intrinsic_op op)
          && op1->ts.type != BT_COMPLEX && op2->ts.type != BT_COMPLEX))
     {
       eq = gfc_dep_compare_expr (op1, op2);
-      if (eq == -2)
+      if (eq <= -2)
        {
          /* Replace A // B < A // C with B < C, and A // B < C // B
             with A < C.  */
          if (op1->ts.type == BT_CHARACTER && op2->ts.type == BT_CHARACTER
+             && op1->expr_type == EXPR_OP
              && op1->value.op.op == INTRINSIC_CONCAT
+             && op2->expr_type == EXPR_OP
              && op2->value.op.op == INTRINSIC_CONCAT)
            {
              gfc_expr *op1_left = op1->value.op.op1;
@@ -736,32 +1497,26 @@ optimize_comparison (gfc_expr *e, gfc_intrinsic_op op)
          switch (op)
            {
            case INTRINSIC_EQ:
-           case INTRINSIC_EQ_OS:
              result = eq == 0;
              break;
              
            case INTRINSIC_GE:
-           case INTRINSIC_GE_OS:
              result = eq >= 0;
              break;
 
            case INTRINSIC_LE:
-           case INTRINSIC_LE_OS:
              result = eq <= 0;
              break;
 
            case INTRINSIC_NE:
-           case INTRINSIC_NE_OS:
              result = eq != 0;
              break;
 
            case INTRINSIC_GT:
-           case INTRINSIC_GT_OS:
              result = eq > 0;
              break;
 
            case INTRINSIC_LT:
-           case INTRINSIC_LT_OS:
              result = eq < 0;
              break;
              
@@ -793,7 +1548,6 @@ optimize_trim (gfc_expr *e)
   gfc_expr *a;
   gfc_ref *ref;
   gfc_expr *fcn;
-  gfc_actual_arglist *actual_arglist, *next;
   gfc_ref **rr = NULL;
 
   /* Don't do this optimization within an argument list, because
@@ -840,28 +1594,219 @@ optimize_trim (gfc_expr *e)
 
   ref->u.ss.start = gfc_get_int_expr (gfc_default_integer_kind, NULL, 1);
 
-  /* Build the function call to len_trim(x, gfc_defaul_integer_kind).  */
+  /* Build the function call to len_trim(x, gfc_default_integer_kind).  */
 
-  fcn = gfc_get_expr ();
-  fcn->expr_type = EXPR_FUNCTION;
-  fcn->value.function.isym =
-    gfc_intrinsic_function_by_id (GFC_ISYM_LEN_TRIM);
-  actual_arglist = gfc_get_actual_arglist ();
-  actual_arglist->expr = gfc_copy_expr (e);
-  next = gfc_get_actual_arglist ();
-  next->expr = gfc_get_int_expr (gfc_default_integer_kind, NULL,
-                                gfc_default_integer_kind);
-  actual_arglist->next = next;
-  fcn->value.function.actual = actual_arglist;
+  fcn = get_len_trim_call (gfc_copy_expr (e), gfc_default_integer_kind);
 
   /* Set the end of the reference to the call to len_trim.  */
 
   ref->u.ss.end = fcn;
-  gcc_assert (*rr == NULL);
+  gcc_assert (rr != NULL && *rr == NULL);
   *rr = ref;
   return true;
 }
 
+/* Optimize minloc(b), where b is rank 1 array, into
+   (/ minloc(b, dim=1) /), and similarly for maxloc,
+   as the latter forms are expanded inline.  */
+
+static void
+optimize_minmaxloc (gfc_expr **e)
+{
+  gfc_expr *fn = *e;
+  gfc_actual_arglist *a;
+  char *name, *p;
+
+  if (fn->rank != 1
+      || fn->value.function.actual == NULL
+      || fn->value.function.actual->expr == NULL
+      || fn->value.function.actual->expr->rank != 1)
+    return;
+
+  *e = gfc_get_array_expr (fn->ts.type, fn->ts.kind, &fn->where);
+  (*e)->shape = fn->shape;
+  fn->rank = 0;
+  fn->shape = NULL;
+  gfc_constructor_append_expr (&(*e)->value.constructor, fn, &fn->where);
+
+  name = XALLOCAVEC (char, strlen (fn->value.function.name) + 1);
+  strcpy (name, fn->value.function.name);
+  p = strstr (name, "loc0");
+  p[3] = '1';
+  fn->value.function.name = gfc_get_string (name);
+  if (fn->value.function.actual->next)
+    {
+      a = fn->value.function.actual->next;
+      gcc_assert (a->expr == NULL);
+    }
+  else
+    {
+      a = gfc_get_actual_arglist ();
+      fn->value.function.actual->next = a;
+    }
+  a->expr = gfc_get_constant_expr (BT_INTEGER, gfc_default_integer_kind,
+                                  &fn->where);
+  mpz_set_ui (a->expr->value.integer, 1);
+}
+
+/* Callback function for code checking that we do not pass a DO variable to an
+   INTENT(OUT) or INTENT(INOUT) dummy variable.  */
+
+static int
+doloop_code (gfc_code **c, int *walk_subtrees ATTRIBUTE_UNUSED,
+        void *data ATTRIBUTE_UNUSED)
+{
+  gfc_code *co;
+  int i;
+  gfc_formal_arglist *f;
+  gfc_actual_arglist *a;
+  gfc_code *cl;
+
+  co = *c;
+
+  /* If the doloop_list grew, we have to truncate it here.  */
+
+  if ((unsigned) doloop_level < doloop_list.length())
+    doloop_list.truncate (doloop_level);
+
+  switch (co->op)
+    {
+    case EXEC_DO:
+
+      if (co->ext.iterator && co->ext.iterator->var)
+       doloop_list.safe_push (co);
+      else
+       doloop_list.safe_push ((gfc_code *) NULL);
+      break;
+
+    case EXEC_CALL:
+
+      if (co->resolved_sym == NULL)
+       break;
+
+      f = gfc_sym_get_dummy_args (co->resolved_sym);
+
+      /* Withot a formal arglist, there is only unknown INTENT,
+        which we don't check for.  */
+      if (f == NULL)
+       break;
+
+      a = co->ext.actual;
+
+      while (a && f)
+       {
+         FOR_EACH_VEC_ELT (doloop_list, i, cl)
+           {
+             gfc_symbol *do_sym;
+             
+             if (cl == NULL)
+               break;
+
+             do_sym = cl->ext.iterator->var->symtree->n.sym;
+             
+             if (a->expr && a->expr->symtree
+                 && a->expr->symtree->n.sym == do_sym)
+               {
+                 if (f->sym->attr.intent == INTENT_OUT)
+                   gfc_error_now_1 ("Variable '%s' at %L set to undefined "
+                                    "value inside loop  beginning at %L as "
+                                    "INTENT(OUT) argument to subroutine '%s'",
+                                    do_sym->name, &a->expr->where,
+                                    &doloop_list[i]->loc,
+                                    co->symtree->n.sym->name);
+                 else if (f->sym->attr.intent == INTENT_INOUT)
+                   gfc_error_now_1 ("Variable '%s' at %L not definable inside "
+                                    "loop beginning at %L as INTENT(INOUT) "
+                                    "argument to subroutine '%s'",
+                                    do_sym->name, &a->expr->where,
+                                    &doloop_list[i]->loc,
+                                    co->symtree->n.sym->name);
+               }
+           }
+         a = a->next;
+         f = f->next;
+       }
+      break;
+
+    default:
+      break;
+    }
+  return 0;
+}
+
+/* Callback function for functions checking that we do not pass a DO variable
+   to an INTENT(OUT) or INTENT(INOUT) dummy variable.  */
+
+static int
+do_function (gfc_expr **e, int *walk_subtrees ATTRIBUTE_UNUSED,
+            void *data ATTRIBUTE_UNUSED)
+{
+  gfc_formal_arglist *f;
+  gfc_actual_arglist *a;
+  gfc_expr *expr;
+  gfc_code *dl;
+  int i;
+
+  expr = *e;
+  if (expr->expr_type != EXPR_FUNCTION)
+    return 0;
+
+  /* Intrinsic functions don't modify their arguments.  */
+
+  if (expr->value.function.isym)
+    return 0;
+
+  f = gfc_sym_get_dummy_args (expr->symtree->n.sym);
+
+  /* Without a formal arglist, there is only unknown INTENT,
+     which we don't check for.  */
+  if (f == NULL)
+    return 0;
+
+  a = expr->value.function.actual;
+
+  while (a && f)
+    {
+      FOR_EACH_VEC_ELT (doloop_list, i, dl)
+       {
+         gfc_symbol *do_sym;
+
+         if (dl == NULL)
+           break;
+
+         do_sym = dl->ext.iterator->var->symtree->n.sym;
+         
+         if (a->expr && a->expr->symtree
+             && a->expr->symtree->n.sym == do_sym)
+           {
+             if (f->sym->attr.intent == INTENT_OUT)
+               gfc_error_now_1 ("Variable '%s' at %L set to undefined value "
+                                "inside loop beginning at %L as INTENT(OUT) "
+                                "argument to function '%s'", do_sym->name,
+                                &a->expr->where, &doloop_list[i]->loc,
+                                expr->symtree->n.sym->name);
+             else if (f->sym->attr.intent == INTENT_INOUT)
+               gfc_error_now_1 ("Variable '%s' at %L not definable inside loop"
+                                " beginning at %L as INTENT(INOUT) argument to"
+                                " function '%s'", do_sym->name,
+                                &a->expr->where, &doloop_list[i]->loc,
+                                expr->symtree->n.sym->name);
+           }
+       }
+      a = a->next;
+      f = f->next;
+    }
+
+  return 0;
+}
+
+static void
+doloop_warn (gfc_namespace *ns)
+{
+  gfc_code_walker (&ns->code, doloop_code, do_function, NULL);
+}
+
+
 #define WALK_SUBEXPR(NODE) \
   do                                                   \
     {                                                  \
@@ -910,9 +1855,13 @@ gfc_expr_walker (gfc_expr **e, walk_expr_fn_t exprfn, void *data)
            for (c = gfc_constructor_first ((*e)->value.constructor); c;
                 c = gfc_constructor_next (c))
              {
-               WALK_SUBEXPR (c->expr);
-               if (c->iterator != NULL)
+               if (c->iterator == NULL)
+                 WALK_SUBEXPR (c->expr);
+               else
                  {
+                   iterator_level ++;
+                   WALK_SUBEXPR (c->expr);
+                   iterator_level --;
                    WALK_SUBEXPR (c->iterator->var);
                    WALK_SUBEXPR (c->iterator->start);
                    WALK_SUBEXPR (c->iterator->end);
@@ -997,15 +1946,35 @@ gfc_code_walker (gfc_code **c, walk_code_fn_t codefn, walk_expr_fn_t exprfn,
          gfc_code *b;
          gfc_actual_arglist *a;
          gfc_code *co;
+         gfc_association_list *alist;
+         bool saved_in_omp_workshare;
 
          /* There might be statement insertions before the current code,
             which must not affect the expression walker.  */
 
          co = *c;
+         saved_in_omp_workshare = in_omp_workshare;
 
          switch (co->op)
            {
+
+           case EXEC_BLOCK:
+             WALK_SUBCODE (co->ext.block.ns->code);
+             if (co->ext.block.assoc)
+               {
+                 bool saved_in_assoc_list = in_assoc_list;
+
+                 in_assoc_list = true;
+                 for (alist = co->ext.block.assoc; alist; alist = alist->next)
+                   WALK_SUBEXPR (alist->target);
+
+                 in_assoc_list = saved_in_assoc_list;
+               }
+
+             break;
+
            case EXEC_DO:
+             doloop_level ++;
              WALK_SUBEXPR (co->ext.iterator->var);
              WALK_SUBEXPR (co->ext.iterator->start);
              WALK_SUBEXPR (co->ext.iterator->end);
@@ -1048,6 +2017,7 @@ gfc_code_walker (gfc_code **c, walk_code_fn_t codefn, walk_expr_fn_t exprfn,
              }
 
            case EXEC_FORALL:
+           case EXEC_DO_CONCURRENT:
              {
                gfc_forall_iterator *fa;
                for (fa = co->ext.forall_iterator; fa; fa = fa->next)
@@ -1057,6 +2027,8 @@ gfc_code_walker (gfc_code **c, walk_code_fn_t codefn, walk_expr_fn_t exprfn,
                    WALK_SUBEXPR (fa->end);
                    WALK_SUBEXPR (fa->stride);
                  }
+               if (co->op == EXEC_FORALL)
+                 forall_level ++;
                break;
              }
 
@@ -1167,21 +2139,77 @@ gfc_code_walker (gfc_code **c, walk_code_fn_t codefn, walk_expr_fn_t exprfn,
              WALK_SUBEXPR (co->ext.dt->extra_comma);
              break;
 
-           case EXEC_OMP_DO:
            case EXEC_OMP_PARALLEL:
            case EXEC_OMP_PARALLEL_DO:
+           case EXEC_OMP_PARALLEL_DO_SIMD:
            case EXEC_OMP_PARALLEL_SECTIONS:
+
+             in_omp_workshare = false;
+
+             /* This goto serves as a shortcut to avoid code
+                duplication or a larger if or switch statement.  */
+             goto check_omp_clauses;
+             
+           case EXEC_OMP_WORKSHARE:
            case EXEC_OMP_PARALLEL_WORKSHARE:
+
+             in_omp_workshare = true;
+
+             /* Fall through  */
+
+           case EXEC_OMP_DISTRIBUTE:
+           case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
+           case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
+           case EXEC_OMP_DISTRIBUTE_SIMD:
+           case EXEC_OMP_DO:
+           case EXEC_OMP_DO_SIMD:
            case EXEC_OMP_SECTIONS:
            case EXEC_OMP_SINGLE:
-           case EXEC_OMP_WORKSHARE:
            case EXEC_OMP_END_SINGLE:
+           case EXEC_OMP_SIMD:
+           case EXEC_OMP_TARGET:
+           case EXEC_OMP_TARGET_DATA:
+           case EXEC_OMP_TARGET_TEAMS:
+           case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE:
+           case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO:
+           case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
+           case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD:
+           case EXEC_OMP_TARGET_UPDATE:
            case EXEC_OMP_TASK:
+           case EXEC_OMP_TEAMS:
+           case EXEC_OMP_TEAMS_DISTRIBUTE:
+           case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO:
+           case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
+           case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
+
+             /* Come to this label only from the
+                EXEC_OMP_PARALLEL_* cases above.  */
+
+           check_omp_clauses:
+
              if (co->ext.omp_clauses)
                {
+                 gfc_omp_namelist *n;
+                 static int list_types[]
+                   = { OMP_LIST_ALIGNED, OMP_LIST_LINEAR, OMP_LIST_DEPEND,
+                       OMP_LIST_MAP, OMP_LIST_TO, OMP_LIST_FROM };
+                 size_t idx;
                  WALK_SUBEXPR (co->ext.omp_clauses->if_expr);
+                 WALK_SUBEXPR (co->ext.omp_clauses->final_expr);
                  WALK_SUBEXPR (co->ext.omp_clauses->num_threads);
                  WALK_SUBEXPR (co->ext.omp_clauses->chunk_size);
+                 WALK_SUBEXPR (co->ext.omp_clauses->safelen_expr);
+                 WALK_SUBEXPR (co->ext.omp_clauses->simdlen_expr);
+                 WALK_SUBEXPR (co->ext.omp_clauses->num_teams);
+                 WALK_SUBEXPR (co->ext.omp_clauses->device);
+                 WALK_SUBEXPR (co->ext.omp_clauses->thread_limit);
+                 WALK_SUBEXPR (co->ext.omp_clauses->dist_chunk_size);
+                 for (idx = 0;
+                      idx < sizeof (list_types) / sizeof (list_types[0]);
+                      idx++)
+                   for (n = co->ext.omp_clauses->lists[list_types[idx]];
+                        n; n = n->next)
+                     WALK_SUBEXPR (n->expr);
                }
              break;
            default:
@@ -1198,6 +2226,14 @@ gfc_code_walker (gfc_code **c, walk_code_fn_t codefn, walk_expr_fn_t exprfn,
              WALK_SUBEXPR (b->expr2);
              WALK_SUBCODE (b->next);
            }
+
+         if (co->op == EXEC_FORALL)
+           forall_level --;
+
+         if (co->op == EXEC_DO)
+           doloop_level --;
+
+         in_omp_workshare = saved_in_omp_workshare;
        }
     }
   return 0;