tree.h (enum tree_code_class): Add tcc_vl_exp.
[platform/upstream/gcc.git] / gcc / fortran / trans-expr.c
index 875092f..839d768 100644 (file)
@@ -153,7 +153,8 @@ gfc_conv_missing_dummy (gfc_se * se, gfc_expr * arg, gfc_typespec ts)
 
   present = gfc_conv_expr_present (arg->symtree->n.sym);
   tmp = build3 (COND_EXPR, TREE_TYPE (se->expr), present, se->expr,
-               build_int_cst (TREE_TYPE (se->expr), 0));
+               fold_convert (TREE_TYPE (se->expr), integer_zero_node));
+
   tmp = gfc_evaluate_now (tmp, &se->pre);
   se->expr = tmp;
   if (ts.type == BT_CHARACTER)
@@ -234,13 +235,16 @@ gfc_trans_init_string_length (gfc_charlen * cl, stmtblock_t * pblock)
 
 
 static void
-gfc_conv_substring (gfc_se * se, gfc_ref * ref, int kind)
+gfc_conv_substring (gfc_se * se, gfc_ref * ref, int kind,
+                   const char *name, locus *where)
 {
   tree tmp;
   tree type;
   tree var;
+  tree fault;
   gfc_se start;
   gfc_se end;
+  char *msg;
 
   type = gfc_get_character_type (kind, ref->u.ss.length);
   type = build_pointer_type (type);
@@ -272,6 +276,40 @@ gfc_conv_substring (gfc_se * se, gfc_ref * ref, int kind)
       gfc_conv_expr_type (&end, ref->u.ss.end, gfc_charlen_type_node);
       gfc_add_block_to_block (&se->pre, &end.pre);
     }
+  if (flag_bounds_check)
+    {
+      tree nonempty = fold_build2 (LE_EXPR, boolean_type_node,
+                                  start.expr, end.expr);
+
+      /* Check lower bound.  */
+      fault = fold_build2 (LT_EXPR, boolean_type_node, start.expr,
+                           build_int_cst (gfc_charlen_type_node, 1));
+      fault = fold_build2 (TRUTH_ANDIF_EXPR, boolean_type_node,
+                          nonempty, fault);
+      if (name)
+       asprintf (&msg, "Substring out of bounds: lower bound of '%s' "
+                 "is less than one", name);
+      else
+       asprintf (&msg, "Substring out of bounds: lower bound "
+                 "is less than one");
+      gfc_trans_runtime_check (fault, msg, &se->pre, where);
+      gfc_free (msg);
+
+      /* Check upper bound.  */
+      fault = fold_build2 (GT_EXPR, boolean_type_node, end.expr,
+                           se->string_length);
+      fault = fold_build2 (TRUTH_ANDIF_EXPR, boolean_type_node,
+                          nonempty, fault);
+      if (name)
+       asprintf (&msg, "Substring out of bounds: upper bound of '%s' "
+                 "exceeds string length", name);
+      else
+       asprintf (&msg, "Substring out of bounds: upper bound "
+                 "exceeds string length");
+      gfc_trans_runtime_check (fault, msg, &se->pre, where);
+      gfc_free (msg);
+    }
+
   tmp = fold_build2 (MINUS_EXPR, gfc_charlen_type_node,
                     build_int_cst (gfc_charlen_type_node, 1),
                     start.expr);
@@ -416,15 +454,21 @@ gfc_conv_variable (gfc_se * se, gfc_expr * expr)
         separately.  */
       if (sym->ts.type == BT_CHARACTER)
        {
-          /* Dereference character pointer dummy arguments
+         /* Dereference character pointer dummy arguments
             or results.  */
          if ((sym->attr.pointer || sym->attr.allocatable)
              && (sym->attr.dummy
                  || sym->attr.function
                  || sym->attr.result))
            se->expr = build_fold_indirect_ref (se->expr);
+
+         /* A character with VALUE attribute needs an address
+            expression.  */
+         if (sym->attr.value)
+           se->expr = build_fold_addr_expr (se->expr);
+
        }
-      else
+      else if (!sym->attr.value)
        {
           /* Dereference non-character scalar dummy arguments.  */
          if (sym->attr.dummy && !sym->attr.dimension)
@@ -485,7 +529,8 @@ gfc_conv_variable (gfc_se * se, gfc_expr * expr)
          break;
 
        case REF_SUBSTRING:
-         gfc_conv_substring (se, ref, expr->ts.kind);
+         gfc_conv_substring (se, ref, expr->ts.kind,
+                             expr->symtree->name, &expr->where);
          break;
 
        default:
@@ -703,7 +748,6 @@ gfc_conv_power_op (gfc_se * se, gfc_expr * expr)
   gfc_se lse;
   gfc_se rse;
   tree fndecl;
-  tree tmp;
 
   gfc_init_se (&lse, se);
   gfc_conv_expr_val (&lse, expr->value.op.op1);
@@ -842,9 +886,7 @@ gfc_conv_power_op (gfc_se * se, gfc_expr * expr)
       break;
     }
 
-  tmp = gfc_chainon_list (NULL_TREE, lse.expr);
-  tmp = gfc_chainon_list (tmp, rse.expr);
-  se->expr = build_function_call_expr (fndecl, tmp);
+  se->expr = build_call_expr (fndecl, 2, lse.expr, rse.expr);
 }
 
 
@@ -855,7 +897,6 @@ gfc_conv_string_tmp (gfc_se * se, tree type, tree len)
 {
   tree var;
   tree tmp;
-  tree args;
 
   gcc_assert (TREE_TYPE (len) == gfc_charlen_type_node);
 
@@ -873,15 +914,13 @@ gfc_conv_string_tmp (gfc_se * se, tree type, tree len)
     {
       /* Allocate a temporary to hold the result.  */
       var = gfc_create_var (type, "pstr");
-      args = gfc_chainon_list (NULL_TREE, len);
-      tmp = build_function_call_expr (gfor_fndecl_internal_malloc, args);
+      tmp = build_call_expr (gfor_fndecl_internal_malloc, 1, len);
       tmp = convert (type, tmp);
       gfc_add_modify_expr (&se->pre, var, tmp);
 
       /* Free the temporary afterwards.  */
       tmp = convert (pvoid_type_node, var);
-      args = gfc_chainon_list (NULL_TREE, tmp);
-      tmp = build_function_call_expr (gfor_fndecl_internal_free, args);
+      tmp = build_call_expr (gfor_fndecl_internal_free, 1, tmp);
       gfc_add_expr_to_block (&se->post, tmp);
     }
 
@@ -900,7 +939,6 @@ gfc_conv_concat_op (gfc_se * se, gfc_expr * expr)
   tree len;
   tree type;
   tree var;
-  tree args;
   tree tmp;
 
   gcc_assert (expr->value.op.op1->ts.type == BT_CHARACTER
@@ -929,14 +967,10 @@ gfc_conv_concat_op (gfc_se * se, gfc_expr * expr)
   var = gfc_conv_string_tmp (se, type, len);
 
   /* Do the actual concatenation.  */
-  args = NULL_TREE;
-  args = gfc_chainon_list (args, len);
-  args = gfc_chainon_list (args, var);
-  args = gfc_chainon_list (args, lse.string_length);
-  args = gfc_chainon_list (args, lse.expr);
-  args = gfc_chainon_list (args, rse.string_length);
-  args = gfc_chainon_list (args, rse.expr);
-  tmp = build_function_call_expr (gfor_fndecl_concat_string, args);
+  tmp = build_call_expr (gfor_fndecl_concat_string, 6,
+                        len, var,
+                        lse.string_length, lse.expr,
+                        rse.string_length, rse.expr);
   gfc_add_expr_to_block (&se->pre, tmp);
 
   /* Add the cleanup for the operands.  */
@@ -1160,17 +1194,9 @@ gfc_build_compare_string (tree len1, tree str1, tree len2, tree str2)
       tmp = fold_build2 (MINUS_EXPR, type, sc1, sc2);
     }
    else
-    {
-      tmp = NULL_TREE;
-      tmp = gfc_chainon_list (tmp, len1);
-      tmp = gfc_chainon_list (tmp, str1);
-      tmp = gfc_chainon_list (tmp, len2);
-      tmp = gfc_chainon_list (tmp, str2);
-
-      /* Build a call for the comparison.  */
-      tmp = build_function_call_expr (gfor_fndecl_compare_string, tmp);
-    }
-
+     /* Build a call for the comparison.  */
+     tmp = build_call_expr (gfor_fndecl_compare_string, 4,
+                           len1, str1, len2, str2);
   return tmp;
 }
 
@@ -1204,6 +1230,48 @@ gfc_conv_function_val (gfc_se * se, gfc_symbol * sym)
 }
 
 
+/* Translate the call for an elemental subroutine call used in an operator
+   assignment.  This is a simplified version of gfc_conv_function_call.  */
+
+tree
+gfc_conv_operator_assign (gfc_se *lse, gfc_se *rse, gfc_symbol *sym)
+{
+  tree args;
+  tree tmp;
+  gfc_se se;
+  stmtblock_t block;
+
+  /* Only elemental subroutines with two arguments.  */
+  gcc_assert (sym->attr.elemental && sym->attr.subroutine);
+  gcc_assert (sym->formal->next->next == NULL);
+
+  gfc_init_block (&block);
+
+  gfc_add_block_to_block (&block, &lse->pre);
+  gfc_add_block_to_block (&block, &rse->pre);
+
+  /* Build the argument list for the call, including hidden string lengths.  */
+  args = gfc_chainon_list (NULL_TREE, build_fold_addr_expr (lse->expr));
+  args = gfc_chainon_list (args, build_fold_addr_expr (rse->expr));
+  if (lse->string_length != NULL_TREE)
+    args = gfc_chainon_list (args, lse->string_length);
+  if (rse->string_length != NULL_TREE)
+    args = gfc_chainon_list (args, rse->string_length);    
+
+  /* Build the function call.  */
+  gfc_init_se (&se, NULL);
+  gfc_conv_function_val (&se, sym);
+  tmp = TREE_TYPE (TREE_TYPE (TREE_TYPE (se.expr)));
+  tmp = build_call_list (tmp, se.expr, args);
+  gfc_add_expr_to_block (&block, tmp);
+
+  gfc_add_block_to_block (&block, &lse->post);
+  gfc_add_block_to_block (&block, &rse->post);
+
+  return gfc_finish_block (&block);
+}
+
+
 /* Initialize MAPPING.  */
 
 void
@@ -1296,10 +1364,17 @@ gfc_set_interface_mapping_bounds (stmtblock_t * block, tree type, tree desc)
   offset = gfc_index_zero_node;
   for (n = 0; n < GFC_TYPE_ARRAY_RANK (type); n++)
     {
+      dim = gfc_rank_cst[n];
       GFC_TYPE_ARRAY_STRIDE (type, n) = gfc_conv_array_stride (desc, n);
-      if (GFC_TYPE_ARRAY_UBOUND (type, n) == NULL_TREE)
+      if (GFC_TYPE_ARRAY_LBOUND (type, n) == NULL_TREE)
+       {
+         GFC_TYPE_ARRAY_LBOUND (type, n)
+               = gfc_conv_descriptor_lbound (desc, dim);
+         GFC_TYPE_ARRAY_UBOUND (type, n)
+               = gfc_conv_descriptor_ubound (desc, dim);
+       }
+      else if (GFC_TYPE_ARRAY_UBOUND (type, n) == NULL_TREE)
        {
-         dim = gfc_rank_cst[n];
          tmp = fold_build2 (MINUS_EXPR, gfc_array_index_type,
                             gfc_conv_descriptor_ubound (desc, dim),
                             gfc_conv_descriptor_lbound (desc, dim));
@@ -1588,9 +1663,9 @@ gfc_apply_interface_mapping (gfc_interface_mapping * mapping,
    an actual argument derived type array is copied and then returned
    after the function call.
    TODO Get rid of this kludge, when array descriptors are capable of
-   handling aliased arrays.  */
+   handling arrays with a bigger stride in bytes than size.  */
 
-static void
+void
 gfc_conv_aliased_arg (gfc_se * parmse, gfc_expr * expr,
                      int g77, sym_intent intent)
 {
@@ -1639,7 +1714,7 @@ gfc_conv_aliased_arg (gfc_se * parmse, gfc_expr * expr,
     {
       gfc_ref *char_ref = expr->ref;
 
-      for (; expr->ts.cl == NULL && char_ref; char_ref = char_ref->next)
+      for (; char_ref; char_ref = char_ref->next)
        if (char_ref->type == REF_SUBSTRING)
          {
            gfc_se tmp_se;
@@ -1708,9 +1783,14 @@ gfc_conv_aliased_arg (gfc_se * parmse, gfc_expr * expr,
     }
   else
     {
-      /* Make sure that the temporary declaration survives.  */
-      tmp = gfc_finish_block (&body);
-      gfc_add_expr_to_block (&loop.pre, tmp);
+      /* Make sure that the temporary declaration survives by merging
+       all the loop declarations into the current context.  */
+      for (n = 0; n < loop.dimen; n++)
+       {
+         gfc_merge_block_scope (&body);
+         body = loop.code[loop.order[n]];
+       }
+      gfc_merge_block_scope (&body);
     }
 
   /* Add the post block after the second loop, so that any
@@ -1826,9 +1906,10 @@ gfc_conv_aliased_arg (gfc_se * parmse, gfc_expr * expr,
   return;
 }
 
-/* Is true if the last array reference is followed by a component reference.  */
+/* Is true if an array reference is followed by a component or substring
+   reference.  */
 
-static bool
+bool
 is_aliased_array (gfc_expr * e)
 {
   gfc_ref * ref;
@@ -1837,23 +1918,75 @@ is_aliased_array (gfc_expr * e)
   seen_array = false;  
   for (ref = e->ref; ref; ref = ref->next)
     {
-      if (ref->type == REF_ARRAY)
+      if (ref->type == REF_ARRAY
+           && ref->u.ar.type != AR_ELEMENT)
        seen_array = true;
 
-      if (ref->next == NULL
+      if (seen_array
            && ref->type != REF_ARRAY)
        return seen_array;
     }
   return false;
 }
 
+/* Generate the code for argument list functions.  */
+
+static void
+conv_arglist_function (gfc_se *se, gfc_expr *expr, const char *name)
+{
+  tree type = NULL_TREE;
+  /* Pass by value for g77 %VAL(arg), pass the address
+     indirectly for %LOC, else by reference.  Thus %REF
+     is a "do-nothing" and %LOC is the same as an F95
+     pointer.  */
+  if (strncmp (name, "%VAL", 4) == 0)
+    {
+      gfc_conv_expr (se, expr);
+      /* %VAL converts argument to default kind.  */
+      switch (expr->ts.type)
+       {
+         case BT_REAL:
+           type = gfc_get_real_type (gfc_default_real_kind);
+           se->expr = fold_convert (type, se->expr);
+           break;
+         case BT_COMPLEX:
+           type = gfc_get_complex_type (gfc_default_complex_kind);
+           se->expr = fold_convert (type, se->expr);
+           break;
+         case BT_INTEGER:
+           type = gfc_get_int_type (gfc_default_integer_kind);
+           se->expr = fold_convert (type, se->expr);
+           break;
+         case BT_LOGICAL:
+           type = gfc_get_logical_type (gfc_default_logical_kind);
+           se->expr = fold_convert (type, se->expr);
+           break;
+         /* This should have been resolved away.  */
+         case BT_UNKNOWN: case BT_CHARACTER: case BT_DERIVED:
+         case BT_PROCEDURE: case BT_HOLLERITH:
+           gfc_internal_error ("Bad type in conv_arglist_function");
+       }
+         
+    }
+  else if (strncmp (name, "%LOC", 4) == 0)
+    {
+      gfc_conv_expr_reference (se, expr);
+      se->expr = gfc_build_addr_expr (NULL, se->expr);
+    }
+  else if (strncmp (name, "%REF", 4) == 0)
+    gfc_conv_expr_reference (se, expr);
+  else
+    gfc_error ("Unknown argument list function at %L", &expr->where);
+}
+
+
 /* Generate code for a procedure call.  Note can return se->post != NULL.
    If se->direct_byref is set then se->expr contains the return parameter.
    Return nonzero, if the call has alternate specifiers.  */
 
 int
 gfc_conv_function_call (gfc_se * se, gfc_symbol * sym,
-                       gfc_actual_arglist * arg)
+                       gfc_actual_arglist * arg, tree append_args)
 {
   gfc_interface_mapping mapping;
   tree arglist;
@@ -1959,19 +2092,30 @@ gfc_conv_function_call (gfc_se * se, gfc_symbol * sym,
          argss = gfc_walk_expr (e);
 
          if (argss == gfc_ss_terminator)
-            {
-             gfc_conv_expr_reference (&parmse, e);
+           {
              parm_kind = SCALAR;
-              if (fsym && fsym->attr.pointer
-                 && e->expr_type != EXPR_NULL)
-                {
-                  /* Scalar pointer dummy args require an extra level of
-                 indirection. The null pointer already contains
-                 this level of indirection.  */
-                 parm_kind = SCALAR_POINTER;
-                  parmse.expr = build_fold_addr_expr (parmse.expr);
-                }
-            }
+             if (fsym && fsym->attr.value)
+               {
+                 gfc_conv_expr (&parmse, e);
+               }
+             else if (arg->name && arg->name[0] == '%')
+               /* Argument list functions %VAL, %LOC and %REF are signalled
+                  through arg->name.  */
+               conv_arglist_function (&parmse, arg->expr, arg->name);
+             else
+               {
+                 gfc_conv_expr_reference (&parmse, e);
+                 if (fsym && fsym->attr.pointer
+                       && e->expr_type != EXPR_NULL)
+                   {
+                     /* Scalar pointer dummy args require an extra level of
+                        indirection. The null pointer already contains
+                        this level of indirection.  */
+                     parm_kind = SCALAR_POINTER;
+                     parmse.expr = build_fold_addr_expr (parmse.expr);
+                   }
+               }
+           }
          else
            {
               /* If the procedure requires an explicit interface, the actual
@@ -2002,9 +2146,7 @@ gfc_conv_function_call (gfc_se * se, gfc_symbol * sym,
               if (fsym && fsym->attr.allocatable
                   && fsym->attr.intent == INTENT_OUT)
                 {
-                 tmp = e->symtree->n.sym->backend_decl;
-                 if (e->symtree->n.sym->attr.dummy)
-                    tmp = build_fold_indirect_ref (tmp);
+                  tmp = build_fold_indirect_ref (parmse.expr);
                   tmp = gfc_trans_dealloc_allocated (tmp);
                   gfc_add_expr_to_block (&se->pre, tmp);
                 }
@@ -2031,7 +2173,7 @@ gfc_conv_function_call (gfc_se * se, gfc_symbol * sym,
                    && fsym->value)
                {
                  gcc_assert (!fsym->attr.allocatable);
-                 tmp = gfc_trans_assignment (e, fsym->value);
+                 tmp = gfc_trans_assignment (e, fsym->value, false);
                  gfc_add_expr_to_block (&se->pre, tmp);
                }
 
@@ -2171,8 +2313,7 @@ gfc_conv_function_call (gfc_se * se, gfc_symbol * sym,
             mustn't be deallocated.  */
          callee_alloc = sym->attr.allocatable || sym->attr.pointer;
          gfc_trans_create_temp_array (&se->pre, &se->post, se->loop, info, tmp,
-                                      false, !sym->attr.pointer, callee_alloc,
-                                      true);
+                                      false, !sym->attr.pointer, callee_alloc);
 
          /* Pass the temporary as the first argument.  */
          tmp = info->descriptor;
@@ -2226,6 +2367,11 @@ gfc_conv_function_call (gfc_se * se, gfc_symbol * sym,
   /* Add the hidden string length parameters to the arguments.  */
   arglist = chainon (arglist, stringargs);
 
+  /* We may want to append extra arguments here.  This is used e.g. for
+     calls to libgfortran_matmul_??, which need extra information.  */
+  if (append_args != NULL_TREE)
+    arglist = chainon (arglist, append_args);
+
   /* Generate the actual call.  */
   gfc_conv_function_val (se, sym);
   /* If there are alternate return labels, function type should be
@@ -2242,8 +2388,7 @@ gfc_conv_function_call (gfc_se * se, gfc_symbol * sym,
     }
 
   fntype = TREE_TYPE (TREE_TYPE (se->expr));
-  se->expr = build3 (CALL_EXPR, TREE_TYPE (fntype), se->expr,
-                    arglist, NULL_TREE);
+  se->expr = build_call_list (TREE_TYPE (fntype), se->expr, arglist);
 
   /* If we have a pointer function, but we don't want a pointer, e.g.
      something like
@@ -2377,26 +2522,21 @@ gfc_trans_string_copy (stmtblock_t * block, tree dlength, tree dest,
   
   /* Truncate string if source is too long.  */
   cond2 = fold_build2 (GE_EXPR, boolean_type_node, slen, dlen);
-  tmp2 = gfc_chainon_list (NULL_TREE, dest);
-  tmp2 = gfc_chainon_list (tmp2, src);
-  tmp2 = gfc_chainon_list (tmp2, dlen);
-  tmp2 = build_function_call_expr (built_in_decls[BUILT_IN_MEMMOVE], tmp2);
+  tmp2 = build_call_expr (built_in_decls[BUILT_IN_MEMMOVE],
+                         3, dest, src, dlen);
 
   /* Else copy and pad with spaces.  */
-  tmp3 = gfc_chainon_list (NULL_TREE, dest);
-  tmp3 = gfc_chainon_list (tmp3, src);
-  tmp3 = gfc_chainon_list (tmp3, slen);
-  tmp3 = build_function_call_expr (built_in_decls[BUILT_IN_MEMMOVE], tmp3);
+  tmp3 = build_call_expr (built_in_decls[BUILT_IN_MEMMOVE],
+                         3, dest, src, slen);
 
   tmp4 = fold_build2 (PLUS_EXPR, pchar_type_node, dest,
                      fold_convert (pchar_type_node, slen));
-  tmp4 = gfc_chainon_list (NULL_TREE, tmp4);
-  tmp4 = gfc_chainon_list (tmp4, build_int_cst
-                                  (gfc_get_int_type (gfc_c_int_kind),
-                                   lang_hooks.to_target_charset (' ')));
-  tmp4 = gfc_chainon_list (tmp4, fold_build2 (MINUS_EXPR, TREE_TYPE(dlen),
-                                             dlen, slen));
-  tmp4 = build_function_call_expr (built_in_decls[BUILT_IN_MEMSET], tmp4);
+  tmp4 = build_call_expr (built_in_decls[BUILT_IN_MEMSET], 3,
+                         tmp4, 
+                         build_int_cst (gfc_get_int_type (gfc_c_int_kind),
+                                        lang_hooks.to_target_charset (' ')),
+                         fold_build2 (MINUS_EXPR, TREE_TYPE(dlen),
+                                      dlen, slen));
 
   gfc_init_block (&tempblock);
   gfc_add_expr_to_block (&tempblock, tmp3);
@@ -2545,7 +2685,7 @@ gfc_conv_function_expr (gfc_se * se, gfc_expr * expr)
   sym = expr->value.function.esym;
   if (!sym)
     sym = expr->symtree->n.sym;
-  gfc_conv_function_call (se, sym, expr->value.function.actual);
+  gfc_conv_function_call (se, sym, expr->value.function.actual, NULL_TREE);
 }
 
 
@@ -2939,7 +3079,7 @@ gfc_conv_substring_expr (gfc_se * se, gfc_expr * expr)
   se->string_length = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (se->expr)));
   TYPE_STRING_FLAG (TREE_TYPE (se->expr))=1;
 
-  gfc_conv_substring(se,ref,expr->ts.kind);
+  gfc_conv_substring(se,ref,expr->ts.kind,NULL,&expr->where);
 }
 
 
@@ -3083,8 +3223,11 @@ gfc_conv_expr_reference (gfc_se * se, gfc_expr * expr)
   /* Create a temporary var to hold the value.  */
   if (TREE_CONSTANT (se->expr))
     {
-      var = build_decl (CONST_DECL, NULL, TREE_TYPE (se->expr));
-      DECL_INITIAL (var) = se->expr;
+      tree tmp = se->expr;
+      STRIP_TYPE_NOPS (tmp);
+      var = build_decl (CONST_DECL, NULL, TREE_TYPE (tmp));
+      DECL_INITIAL (var) = tmp;
+      TREE_STATIC (var) = 1;
       pushdecl (var);
     }
   else
@@ -3149,7 +3292,7 @@ gfc_trans_pointer_assignment (gfc_expr * expr1, gfc_expr * expr2)
        {
        case EXPR_NULL:
          /* Just set the data pointer to null.  */
-         gfc_conv_descriptor_data_set (&block, lse.expr, null_pointer_node);
+         gfc_conv_descriptor_data_set (&lse.pre, lse.expr, null_pointer_node);
          break;
 
        case EXPR_VARIABLE:
@@ -3315,6 +3458,23 @@ gfc_trans_arrayfunc_assign (gfc_expr * expr1, gfc_expr * expr2)
       || expr2->symtree->n.sym->attr.allocatable)
     return NULL;
 
+  /* Character array functions need temporaries unless the
+     character lengths are the same.  */
+  if (expr2->ts.type == BT_CHARACTER && expr2->rank > 0)
+    {
+      if (expr1->ts.cl->length == NULL
+           || expr1->ts.cl->length->expr_type != EXPR_CONSTANT)
+       return NULL;
+
+      if (expr2->ts.cl->length == NULL
+           || expr2->ts.cl->length->expr_type != EXPR_CONSTANT)
+       return NULL;
+
+      if (mpz_cmp (expr1->ts.cl->length->value.integer,
+                    expr2->ts.cl->length->value.integer) != 0)
+       return NULL;
+    }
+
   /* Check that no LHS component references appear during an array
      reference. This is needed because we do not have the means to
      span any arbitrary stride with an array descriptor. This check
@@ -3358,12 +3518,206 @@ gfc_trans_arrayfunc_assign (gfc_expr * expr1, gfc_expr * expr2)
   return gfc_finish_block (&se.pre);
 }
 
+/* Determine whether the given EXPR_CONSTANT is a zero initializer.  */
+
+static bool
+is_zero_initializer_p (gfc_expr * expr)
+{
+  if (expr->expr_type != EXPR_CONSTANT)
+    return false;
+  /* We ignore Hollerith constants for the time being.  */
+  if (expr->from_H)
+    return false;
+
+  switch (expr->ts.type)
+    {
+    case BT_INTEGER:
+      return mpz_cmp_si (expr->value.integer, 0) == 0;
+
+    case BT_REAL:
+      return mpfr_zero_p (expr->value.real)
+            && MPFR_SIGN (expr->value.real) >= 0;
 
-/* Translate an assignment.  Most of the code is concerned with
-   setting up the scalarizer.  */
+    case BT_LOGICAL:
+      return expr->value.logical == 0;
 
-tree
-gfc_trans_assignment (gfc_expr * expr1, gfc_expr * expr2)
+    case BT_COMPLEX:
+      return mpfr_zero_p (expr->value.complex.r)
+            && MPFR_SIGN (expr->value.complex.r) >= 0
+             && mpfr_zero_p (expr->value.complex.i)
+            && MPFR_SIGN (expr->value.complex.i) >= 0;
+
+    default:
+      break;
+    }
+  return false;
+}
+
+/* Try to efficiently translate array(:) = 0.  Return NULL if this
+   can't be done.  */
+
+static tree
+gfc_trans_zero_assign (gfc_expr * expr)
+{
+  tree dest, len, type;
+  tree tmp;
+  gfc_symbol *sym;
+
+  sym = expr->symtree->n.sym;
+  dest = gfc_get_symbol_decl (sym);
+
+  type = TREE_TYPE (dest);
+  if (POINTER_TYPE_P (type))
+    type = TREE_TYPE (type);
+  if (!GFC_ARRAY_TYPE_P (type))
+    return NULL_TREE;
+
+  /* Determine the length of the array.  */
+  len = GFC_TYPE_ARRAY_SIZE (type);
+  if (!len || TREE_CODE (len) != INTEGER_CST)
+    return NULL_TREE;
+
+  len = fold_build2 (MULT_EXPR, gfc_array_index_type, len,
+                     TYPE_SIZE_UNIT (gfc_get_element_type (type)));
+
+  /* Convert arguments to the correct types.  */
+  if (!POINTER_TYPE_P (TREE_TYPE (dest)))
+    dest = gfc_build_addr_expr (pvoid_type_node, dest);
+  else
+    dest = fold_convert (pvoid_type_node, dest);
+  len = fold_convert (size_type_node, len);
+
+  /* Construct call to __builtin_memset.  */
+  tmp = build_call_expr (built_in_decls[BUILT_IN_MEMSET],
+                        3, dest, integer_zero_node, len);
+  return fold_convert (void_type_node, tmp);
+}
+
+
+/* Helper for gfc_trans_array_copy and gfc_trans_array_constructor_copy
+   that constructs the call to __builtin_memcpy.  */
+
+static tree
+gfc_build_memcpy_call (tree dst, tree src, tree len)
+{
+  tree tmp;
+
+  /* Convert arguments to the correct types.  */
+  if (!POINTER_TYPE_P (TREE_TYPE (dst)))
+    dst = gfc_build_addr_expr (pvoid_type_node, dst);
+  else
+    dst = fold_convert (pvoid_type_node, dst);
+
+  if (!POINTER_TYPE_P (TREE_TYPE (src)))
+    src = gfc_build_addr_expr (pvoid_type_node, src);
+  else
+    src = fold_convert (pvoid_type_node, src);
+
+  len = fold_convert (size_type_node, len);
+
+  /* Construct call to __builtin_memcpy.  */
+  tmp = build_call_expr (built_in_decls[BUILT_IN_MEMCPY], 3, dst, src, len);
+  return fold_convert (void_type_node, tmp);
+}
+
+
+/* Try to efficiently translate dst(:) = src(:).  Return NULL if this
+   can't be done.  EXPR1 is the destination/lhs and EXPR2 is the
+   source/rhs, both are gfc_full_array_ref_p which have been checked for
+   dependencies.  */
+
+static tree
+gfc_trans_array_copy (gfc_expr * expr1, gfc_expr * expr2)
+{
+  tree dst, dlen, dtype;
+  tree src, slen, stype;
+
+  dst = gfc_get_symbol_decl (expr1->symtree->n.sym);
+  src = gfc_get_symbol_decl (expr2->symtree->n.sym);
+
+  dtype = TREE_TYPE (dst);
+  if (POINTER_TYPE_P (dtype))
+    dtype = TREE_TYPE (dtype);
+  stype = TREE_TYPE (src);
+  if (POINTER_TYPE_P (stype))
+    stype = TREE_TYPE (stype);
+
+  if (!GFC_ARRAY_TYPE_P (dtype) || !GFC_ARRAY_TYPE_P (stype))
+    return NULL_TREE;
+
+  /* Determine the lengths of the arrays.  */
+  dlen = GFC_TYPE_ARRAY_SIZE (dtype);
+  if (!dlen || TREE_CODE (dlen) != INTEGER_CST)
+    return NULL_TREE;
+  dlen = fold_build2 (MULT_EXPR, gfc_array_index_type, dlen,
+                     TYPE_SIZE_UNIT (gfc_get_element_type (dtype)));
+
+  slen = GFC_TYPE_ARRAY_SIZE (stype);
+  if (!slen || TREE_CODE (slen) != INTEGER_CST)
+    return NULL_TREE;
+  slen = fold_build2 (MULT_EXPR, gfc_array_index_type, slen,
+                     TYPE_SIZE_UNIT (gfc_get_element_type (stype)));
+
+  /* Sanity check that they are the same.  This should always be
+     the case, as we should already have checked for conformance.  */
+  if (!tree_int_cst_equal (slen, dlen))
+    return NULL_TREE;
+
+  return gfc_build_memcpy_call (dst, src, dlen);
+}
+
+
+/* Try to efficiently translate array(:) = (/ ... /).  Return NULL if
+   this can't be done.  EXPR1 is the destination/lhs for which
+   gfc_full_array_ref_p is true, and EXPR2 is the source/rhs.  */
+
+static tree
+gfc_trans_array_constructor_copy (gfc_expr * expr1, gfc_expr * expr2)
+{
+  unsigned HOST_WIDE_INT nelem;
+  tree dst, dtype;
+  tree src, stype;
+  tree len;
+
+  nelem = gfc_constant_array_constructor_p (expr2->value.constructor);
+  if (nelem == 0)
+    return NULL_TREE;
+
+  dst = gfc_get_symbol_decl (expr1->symtree->n.sym);
+  dtype = TREE_TYPE (dst);
+  if (POINTER_TYPE_P (dtype))
+    dtype = TREE_TYPE (dtype);
+  if (!GFC_ARRAY_TYPE_P (dtype))
+    return NULL_TREE;
+
+  /* Determine the lengths of the array.  */
+  len = GFC_TYPE_ARRAY_SIZE (dtype);
+  if (!len || TREE_CODE (len) != INTEGER_CST)
+    return NULL_TREE;
+
+  /* Confirm that the constructor is the same size.  */
+  if (compare_tree_int (len, nelem) != 0)
+    return NULL_TREE;
+
+  len = fold_build2 (MULT_EXPR, gfc_array_index_type, len,
+                    TYPE_SIZE_UNIT (gfc_get_element_type (dtype)));
+
+  stype = gfc_typenode_for_spec (&expr2->ts);
+  src = gfc_build_constant_array_constructor (expr2, stype);
+
+  stype = TREE_TYPE (src);
+  if (POINTER_TYPE_P (stype))
+    stype = TREE_TYPE (stype);
+
+  return gfc_build_memcpy_call (dst, src, len);
+}
+
+
+/* Subroutine of gfc_trans_assignment that actually scalarizes the
+   assignment.  EXPR1 is the destination/RHS and EXPR2 is the source/LHS.  */
+
+static tree
+gfc_trans_assignment_1 (gfc_expr * expr1, gfc_expr * expr2, bool init_flag)
 {
   gfc_se lse;
   gfc_se rse;
@@ -3376,14 +3730,6 @@ gfc_trans_assignment (gfc_expr * expr1, gfc_expr * expr2)
   stmtblock_t body;
   bool l_is_temp;
 
-  /* Special case a single function returning an array.  */
-  if (expr2->expr_type == EXPR_FUNCTION && expr2->rank > 0)
-    {
-      tmp = gfc_trans_arrayfunc_assign (expr1, expr2);
-      if (tmp)
-       return tmp;
-    }
-
   /* Assignment of the form lhs = rhs.  */
   gfc_start_block (&block);
 
@@ -3466,7 +3812,8 @@ gfc_trans_assignment (gfc_expr * expr1, gfc_expr * expr2)
   else
     gfc_conv_expr (&lse, expr1);
 
-  tmp = gfc_trans_scalar_assign (&lse, &rse, expr1->ts, l_is_temp,
+  tmp = gfc_trans_scalar_assign (&lse, &rse, expr1->ts,
+                                l_is_temp || init_flag,
                                 expr2->expr_type == EXPR_VARIABLE);
   gfc_add_expr_to_block (&body, tmp);
 
@@ -3500,7 +3847,8 @@ gfc_trans_assignment (gfc_expr * expr1, gfc_expr * expr2)
          gcc_assert (lse.ss == gfc_ss_terminator
                      && rse.ss == gfc_ss_terminator);
 
-         tmp = gfc_trans_scalar_assign (&lse, &rse, expr1->ts, false, false);
+         tmp = gfc_trans_scalar_assign (&lse, &rse, expr1->ts,
+                                        false, false);
          gfc_add_expr_to_block (&body, tmp);
        }
 
@@ -3517,8 +3865,104 @@ gfc_trans_assignment (gfc_expr * expr1, gfc_expr * expr2)
   return gfc_finish_block (&block);
 }
 
+
+/* Check whether EXPR, which is an EXPR_VARIABLE, is a copyable array.  */
+
+static bool
+copyable_array_p (gfc_expr * expr)
+{
+  /* First check it's an array.  */
+  if (expr->rank < 1 || !expr->ref)
+    return false;
+
+  /* Next check that it's of a simple enough type.  */
+  switch (expr->ts.type)
+    {
+    case BT_INTEGER:
+    case BT_REAL:
+    case BT_COMPLEX:
+    case BT_LOGICAL:
+      return true;
+
+    case BT_CHARACTER:
+      return false;
+
+    case BT_DERIVED:
+      return !expr->ts.derived->attr.alloc_comp;
+
+    default:
+      break;
+    }
+
+  return false;
+}
+
+/* Translate an assignment.  */
+
+tree
+gfc_trans_assignment (gfc_expr * expr1, gfc_expr * expr2, bool init_flag)
+{
+  tree tmp;
+
+  /* Special case a single function returning an array.  */
+  if (expr2->expr_type == EXPR_FUNCTION && expr2->rank > 0)
+    {
+      tmp = gfc_trans_arrayfunc_assign (expr1, expr2);
+      if (tmp)
+       return tmp;
+    }
+
+  /* Special case assigning an array to zero.  */
+  if (expr1->expr_type == EXPR_VARIABLE
+      && expr1->rank > 0
+      && expr1->ref
+      && gfc_full_array_ref_p (expr1->ref)
+      && is_zero_initializer_p (expr2))
+    {
+      tmp = gfc_trans_zero_assign (expr1);
+      if (tmp)
+        return tmp;
+    }
+
+  /* Special case copying one array to another.  */
+  if (expr1->expr_type == EXPR_VARIABLE
+      && copyable_array_p (expr1)
+      && gfc_full_array_ref_p (expr1->ref)
+      && expr2->expr_type == EXPR_VARIABLE
+      && copyable_array_p (expr2)
+      && gfc_full_array_ref_p (expr2->ref)
+      && gfc_compare_types (&expr1->ts, &expr2->ts)
+      && !gfc_check_dependency (expr1, expr2, 0))
+    {
+      tmp = gfc_trans_array_copy (expr1, expr2);
+      if (tmp)
+        return tmp;
+    }
+
+  /* Special case initializing an array from a constant array constructor.  */
+  if (expr1->expr_type == EXPR_VARIABLE
+      && copyable_array_p (expr1)
+      && gfc_full_array_ref_p (expr1->ref)
+      && expr2->expr_type == EXPR_ARRAY
+      && gfc_compare_types (&expr1->ts, &expr2->ts))
+    {
+      tmp = gfc_trans_array_constructor_copy (expr1, expr2);
+      if (tmp)
+       return tmp;
+    }
+
+  /* Fallback to the scalarizer to generate explicit loops.  */
+  return gfc_trans_assignment_1 (expr1, expr2, init_flag);
+}
+
+tree
+gfc_trans_init_assign (gfc_code * code)
+{
+  return gfc_trans_assignment (code->expr, code->expr2, true);
+}
+
 tree
 gfc_trans_assign (gfc_code * code)
 {
-  return gfc_trans_assignment (code->expr, code->expr2);
+  return gfc_trans_assignment (code->expr, code->expr2, false);
 }