OSDN Git Service

2010-08-19 Daniel Kraft <d@domob.eu>
[pf3gnuchains/gcc-fork.git] / gcc / fortran / trans-expr.c
index 810212b..63e6746 100644 (file)
@@ -4773,21 +4773,46 @@ gfc_trans_pointer_assignment (gfc_expr * expr1, gfc_expr * expr2)
     }
   else
     {
+      gfc_ref* remap;
+      bool rank_remap;
       tree strlen_lhs;
       tree strlen_rhs = NULL_TREE;
 
-      /* Array pointer.  */
+      /* Array pointer.  Find the last reference on the LHS and if it is an
+        array section ref, we're dealing with bounds remapping.  In this case,
+        set it to AR_FULL so that gfc_conv_expr_descriptor does
+        not see it and process the bounds remapping afterwards explicitely.  */
+      for (remap = expr1->ref; remap; remap = remap->next)
+       if (!remap->next && remap->type == REF_ARRAY
+           && remap->u.ar.type == AR_SECTION)
+         {  
+           remap->u.ar.type = AR_FULL;
+           break;
+         }
+      rank_remap = (remap && remap->u.ar.end[0]);
+
       gfc_conv_expr_descriptor (&lse, expr1, lss);
       strlen_lhs = lse.string_length;
-      switch (expr2->expr_type)
+      desc = lse.expr;
+
+      if (expr2->expr_type == EXPR_NULL)
        {
-       case EXPR_NULL:
          /* Just set the data pointer to null.  */
          gfc_conv_descriptor_data_set (&lse.pre, lse.expr, null_pointer_node);
-         break;
-
-       case EXPR_VARIABLE:
-         /* Assign directly to the pointer's descriptor.  */
+       }
+      else if (rank_remap)
+       {
+         /* If we are rank-remapping, just get the RHS's descriptor and
+            process this later on.  */
+         gfc_init_se (&rse, NULL);
+         rse.direct_byref = 1;
+         rse.byref_noassign = 1;
+         gfc_conv_expr_descriptor (&rse, expr2, rss);
+         strlen_rhs = rse.string_length;
+       }
+      else if (expr2->expr_type == EXPR_VARIABLE)
+       {
+         /* Assign directly to the LHS's descriptor.  */
          lse.direct_byref = 1;
          gfc_conv_expr_descriptor (&lse, expr2, rss);
          strlen_rhs = lse.string_length;
@@ -4806,13 +4831,11 @@ gfc_trans_pointer_assignment (gfc_expr * expr1, gfc_expr * expr2)
                gfc_add_block_to_block (&lse.post, &rse.pre);
              gfc_add_modify (&lse.post, GFC_DECL_SPAN(decl), tmp);
            }
-
-         break;
-
-       default:
+       }
+      else
+       {
          /* Assign to a temporary descriptor and then copy that
             temporary to the pointer.  */
-         desc = lse.expr;
          tmp = gfc_create_var (TREE_TYPE (desc), "ptrtemp");
 
          lse.expr = tmp;
@@ -4820,10 +4843,130 @@ gfc_trans_pointer_assignment (gfc_expr * expr1, gfc_expr * expr2)
          gfc_conv_expr_descriptor (&lse, expr2, rss);
          strlen_rhs = lse.string_length;
          gfc_add_modify (&lse.pre, desc, tmp);
-         break;
        }
 
       gfc_add_block_to_block (&block, &lse.pre);
+      if (rank_remap)
+       gfc_add_block_to_block (&block, &rse.pre);
+
+      /* If we do bounds remapping, update LHS descriptor accordingly.  */
+      if (remap)
+       {
+         int dim;
+         gcc_assert (remap->u.ar.dimen == expr1->rank);
+
+         if (rank_remap)
+           {
+             /* Do rank remapping.  We already have the RHS's descriptor
+                converted in rse and now have to build the correct LHS
+                descriptor for it.  */
+
+             tree dtype, data;
+             tree offs, stride;
+             tree lbound, ubound;
+
+             /* Set dtype.  */
+             dtype = gfc_conv_descriptor_dtype (desc);
+             tmp = gfc_get_dtype (TREE_TYPE (desc));
+             gfc_add_modify (&block, dtype, tmp);
+
+             /* Copy data pointer.  */
+             data = gfc_conv_descriptor_data_get (rse.expr);
+             gfc_conv_descriptor_data_set (&block, desc, data);
+
+             /* Copy offset but adjust it such that it would correspond
+                to a lbound of zero.  */
+             offs = gfc_conv_descriptor_offset_get (rse.expr);
+             for (dim = 0; dim < expr2->rank; ++dim)
+               {
+                 stride = gfc_conv_descriptor_stride_get (rse.expr,
+                                                          gfc_rank_cst[dim]);
+                 lbound = gfc_conv_descriptor_lbound_get (rse.expr,
+                                                          gfc_rank_cst[dim]);
+                 tmp = fold_build2 (MULT_EXPR, gfc_array_index_type,
+                                    stride, lbound);
+                 offs = fold_build2 (PLUS_EXPR, gfc_array_index_type,
+                                     offs, tmp);
+               }
+             gfc_conv_descriptor_offset_set (&block, desc, offs);
+
+             /* Set the bounds as declared for the LHS and calculate strides as
+                well as another offset update accordingly.  */
+             stride = gfc_conv_descriptor_stride_get (rse.expr,
+                                                      gfc_rank_cst[0]);
+             for (dim = 0; dim < expr1->rank; ++dim)
+               {
+                 gfc_se lower_se;
+                 gfc_se upper_se;
+
+                 gcc_assert (remap->u.ar.start[dim] && remap->u.ar.end[dim]);
+
+                 /* Convert declared bounds.  */
+                 gfc_init_se (&lower_se, NULL);
+                 gfc_init_se (&upper_se, NULL);
+                 gfc_conv_expr (&lower_se, remap->u.ar.start[dim]);
+                 gfc_conv_expr (&upper_se, remap->u.ar.end[dim]);
+
+                 gfc_add_block_to_block (&block, &lower_se.pre);
+                 gfc_add_block_to_block (&block, &upper_se.pre);
+
+                 lbound = fold_convert (gfc_array_index_type, lower_se.expr);
+                 ubound = fold_convert (gfc_array_index_type, upper_se.expr);
+
+                 lbound = gfc_evaluate_now (lbound, &block);
+                 ubound = gfc_evaluate_now (ubound, &block);
+
+                 gfc_add_block_to_block (&block, &lower_se.post);
+                 gfc_add_block_to_block (&block, &upper_se.post);
+
+                 /* Set bounds in descriptor.  */
+                 gfc_conv_descriptor_lbound_set (&block, desc,
+                                                 gfc_rank_cst[dim], lbound);
+                 gfc_conv_descriptor_ubound_set (&block, desc,
+                                                 gfc_rank_cst[dim], ubound);
+
+                 /* Set stride.  */
+                 stride = gfc_evaluate_now (stride, &block);
+                 gfc_conv_descriptor_stride_set (&block, desc,
+                                                 gfc_rank_cst[dim], stride);
+
+                 /* Update offset.  */
+                 offs = gfc_conv_descriptor_offset_get (desc);
+                 tmp = fold_build2 (MULT_EXPR, gfc_array_index_type,
+                                    lbound, stride);
+                 offs = fold_build2 (MINUS_EXPR, gfc_array_index_type,
+                                     offs, tmp);
+                 offs = gfc_evaluate_now (offs, &block);
+                 gfc_conv_descriptor_offset_set (&block, desc, offs);
+
+                 /* Update stride.  */
+                 tmp = gfc_conv_array_extent_dim (lbound, ubound, NULL);
+                 stride = fold_build2 (MULT_EXPR, gfc_array_index_type,
+                                       stride, tmp);
+               }
+           }
+         else
+           {
+             /* Bounds remapping.  Just shift the lower bounds.  */
+
+             gcc_assert (expr1->rank == expr2->rank);
+
+             for (dim = 0; dim < remap->u.ar.dimen; ++dim)
+               {
+                 gfc_se lbound_se;
+
+                 gcc_assert (remap->u.ar.start[dim]);
+                 gcc_assert (!remap->u.ar.end[dim]);
+                 gfc_init_se (&lbound_se, NULL);
+                 gfc_conv_expr (&lbound_se, remap->u.ar.start[dim]);
+
+                 gfc_add_block_to_block (&block, &lbound_se.pre);
+                 gfc_conv_shift_descriptor_lbound (&block, desc,
+                                                   dim, lbound_se.expr);
+                 gfc_add_block_to_block (&block, &lbound_se.post);
+               }
+           }
+       }
 
       /* Check string lengths if applicable.  The check is only really added
         to the output code if -fbounds-check is enabled.  */
@@ -4835,8 +4978,31 @@ gfc_trans_pointer_assignment (gfc_expr * expr1, gfc_expr * expr2)
                                       strlen_lhs, strlen_rhs, &block);
        }
 
+      /* If rank remapping was done, check with -fcheck=bounds that
+        the target is at least as large as the pointer.  */
+      if (rank_remap && (gfc_option.rtcheck & GFC_RTCHECK_BOUNDS))
+       {
+         tree lsize, rsize;
+         tree fault;
+         const char* msg;
+
+         lsize = gfc_conv_descriptor_size (lse.expr, expr1->rank);
+         rsize = gfc_conv_descriptor_size (rse.expr, expr2->rank);
+
+         lsize = gfc_evaluate_now (lsize, &block);
+         rsize = gfc_evaluate_now (rsize, &block);
+         fault = fold_build2 (LT_EXPR, boolean_type_node, rsize, lsize);
+
+         msg = _("Target of rank remapping is too small (%ld < %ld)");
+         gfc_trans_runtime_check (true, false, fault, &block, &expr2->where,
+                                  msg, rsize, lsize);
+       }
+
       gfc_add_block_to_block (&block, &lse.post);
+      if (rank_remap)
+       gfc_add_block_to_block (&block, &rse.post);
     }
+
   return gfc_finish_block (&block);
 }