OSDN Git Service

PR c/35652
[pf3gnuchains/gcc-fork.git] / gcc / tree-vect-analyze.c
index c9f559b..729ad79 100644 (file)
@@ -41,29 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "toplev.h"
 #include "recog.h"
 
-/* Main analysis functions.  */
-static loop_vec_info vect_analyze_loop_form (struct loop *);
-static bool vect_analyze_data_refs (loop_vec_info);
-static bool vect_mark_stmts_to_be_vectorized (loop_vec_info);
-static void vect_analyze_scalar_cycles (loop_vec_info);
-static bool vect_analyze_data_ref_accesses (loop_vec_info);
-static bool vect_analyze_data_ref_dependences (loop_vec_info);
-static bool vect_analyze_data_refs_alignment (loop_vec_info);
-static bool vect_compute_data_refs_alignment (loop_vec_info);
-static bool vect_enhance_data_refs_alignment (loop_vec_info);
-static bool vect_analyze_operations (loop_vec_info);
-static bool vect_determine_vectorization_factor (loop_vec_info);
-
-/* Utility functions for the analyses.  */
-static bool exist_non_indexing_operands_for_use_p (tree, tree);
-static tree vect_get_loop_niters (struct loop *, tree *);
-static bool vect_analyze_data_ref_dependence
-  (struct data_dependence_relation *, loop_vec_info);
-static bool vect_compute_data_ref_alignment (struct data_reference *); 
-static bool vect_analyze_data_ref_access (struct data_reference *);
 static bool vect_can_advance_ivs_p (loop_vec_info);
-static void vect_update_misalignment_for_peel
-  (struct data_reference *, struct data_reference *, int npeel);
 
 /* Function vect_determine_vectorization_factor
 
@@ -242,7 +220,8 @@ vect_determine_vectorization_factor (loop_vec_info loop_vinfo)
              operation = GIMPLE_STMT_OPERAND (stmt, 1);
              if (TREE_CODE (operation) == NOP_EXPR
                  || TREE_CODE (operation) == CONVERT_EXPR
-                 || TREE_CODE (operation) == WIDEN_MULT_EXPR)
+                 || TREE_CODE (operation) == WIDEN_MULT_EXPR
+                 || TREE_CODE (operation) == FLOAT_EXPR)
                {
                  tree rhs_type = TREE_TYPE (TREE_OPERAND (operation, 0));
                  if (TREE_INT_CST_LOW (TYPE_SIZE_UNIT (rhs_type)) < 
@@ -481,7 +460,10 @@ vect_analyze_operations (loop_vec_info loop_vinfo)
              need_to_vectorize = true;
            }
 
-         ok = (vectorizable_type_promotion (stmt, NULL, NULL)
+         ok = true;
+         if (STMT_VINFO_RELEVANT_P (stmt_info)
+             || STMT_VINFO_DEF_TYPE (stmt_info) == vect_reduction_def)
+           ok = (vectorizable_type_promotion (stmt, NULL, NULL)
                || vectorizable_type_demotion (stmt, NULL, NULL)
                || vectorizable_conversion (stmt, NULL, NULL, NULL)
                || vectorizable_operation (stmt, NULL, NULL, NULL)
@@ -492,17 +474,29 @@ vect_analyze_operations (loop_vec_info loop_vinfo)
                || vectorizable_condition (stmt, NULL, NULL)
                || vectorizable_reduction (stmt, NULL, NULL));
 
+         if (!ok)
+           {
+             if (vect_print_dump_info (REPORT_UNVECTORIZED_LOOPS))
+               {
+                 fprintf (vect_dump, "not vectorized: relevant stmt not ");
+                 fprintf (vect_dump, "supported: ");
+                 print_generic_expr (vect_dump, stmt, TDF_SLIM);
+               }
+             return false;
+           }
+
          /* Stmts that are (also) "live" (i.e. - that are used out of the loop)
             need extra handling, except for vectorizable reductions.  */
          if (STMT_VINFO_LIVE_P (stmt_info)
              && STMT_VINFO_TYPE (stmt_info) != reduc_vec_info_type) 
-           ok |= vectorizable_live_operation (stmt, NULL, NULL);
+           ok = vectorizable_live_operation (stmt, NULL, NULL);
 
          if (!ok)
            {
              if (vect_print_dump_info (REPORT_UNVECTORIZED_LOOPS))
                {
-                 fprintf (vect_dump, "not vectorized: stmt not supported: ");
+                 fprintf (vect_dump, "not vectorized: live stmt not ");
+                 fprintf (vect_dump, "supported: ");
                  print_generic_expr (vect_dump, stmt, TDF_SLIM);
                }
              return false;
@@ -587,6 +581,7 @@ vect_analyze_operations (loop_vec_info loop_vinfo)
 
   min_profitable_iters = vect_estimate_min_profitable_iters (loop_vinfo);
   LOOP_VINFO_COST_MODEL_MIN_ITERS (loop_vinfo) = min_profitable_iters;
+
   if (min_profitable_iters < 0)
     {
       if (vect_print_dump_info (REPORT_UNVECTORIZED_LOOPS))
@@ -1065,7 +1060,9 @@ vect_check_interleaving (struct data_reference *dra,
   type_size_b = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (drb))));
 
   if (type_size_a != type_size_b
-      || tree_int_cst_compare (DR_STEP (dra), DR_STEP (drb)))
+      || tree_int_cst_compare (DR_STEP (dra), DR_STEP (drb))
+      || !types_compatible_p (TREE_TYPE (DR_REF (dra)), 
+                              TREE_TYPE (DR_REF (drb))))
     return;
 
   init_a = TREE_INT_CST_LOW (DR_INIT (dra));
@@ -1118,11 +1115,85 @@ vect_check_interleaving (struct data_reference *dra,
     }
 }
 
+/* Check if data references pointed by DR_I and DR_J are same or
+   belong to same interleaving group.  Return FALSE if drs are
+   different, otherwise return TRUE.  */
+
+static bool
+vect_same_range_drs (data_reference_p dr_i, data_reference_p dr_j)
+{
+  tree stmt_i = DR_STMT (dr_i);
+  tree stmt_j = DR_STMT (dr_j);
+
+  if (operand_equal_p (DR_REF (dr_i), DR_REF (dr_j), 0)
+      || (DR_GROUP_FIRST_DR (vinfo_for_stmt (stmt_i))
+           && DR_GROUP_FIRST_DR (vinfo_for_stmt (stmt_j))
+           && (DR_GROUP_FIRST_DR (vinfo_for_stmt (stmt_i))
+               == DR_GROUP_FIRST_DR (vinfo_for_stmt (stmt_j)))))
+    return true;
+  else
+    return false;
+}
+
+/* If address ranges represented by DDR_I and DDR_J are equal,
+   return TRUE, otherwise return FALSE.  */
+
+static bool
+vect_vfa_range_equal (ddr_p ddr_i, ddr_p ddr_j)
+{
+  if ((vect_same_range_drs (DDR_A (ddr_i), DDR_A (ddr_j))
+       && vect_same_range_drs (DDR_B (ddr_i), DDR_B (ddr_j)))
+      || (vect_same_range_drs (DDR_A (ddr_i), DDR_B (ddr_j))
+         && vect_same_range_drs (DDR_B (ddr_i), DDR_A (ddr_j))))
+    return true;
+  else
+    return false;
+}
+
+/* Insert DDR into LOOP_VINFO list of ddrs that may alias and need to be
+   tested at run-time.  Return TRUE if DDR was successfully inserted.
+   Return false if versioning is not supported.  */
+
+static bool
+vect_mark_for_runtime_alias_test (ddr_p ddr, loop_vec_info loop_vinfo)
+{
+  struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
+
+  if ((unsigned) PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS) == 0)
+    return false;
+
+  if (vect_print_dump_info (REPORT_DR_DETAILS))
+    {
+      fprintf (vect_dump, "mark for run-time aliasing test between ");
+      print_generic_expr (vect_dump, DR_REF (DDR_A (ddr)), TDF_SLIM);
+      fprintf (vect_dump, " and ");
+      print_generic_expr (vect_dump, DR_REF (DDR_B (ddr)), TDF_SLIM);
+    }
+
+  if (optimize_size)
+    {
+      if (vect_print_dump_info (REPORT_DR_DETAILS))
+       fprintf (vect_dump, "versioning not supported when optimizing for size.");
+      return false;
+    }
+
+  /* FORNOW: We don't support versioning with outer-loop vectorization.  */
+  if (loop->inner)
+    {
+      if (vect_print_dump_info (REPORT_DR_DETAILS))
+       fprintf (vect_dump, "versioning not yet supported for outer-loops.");
+      return false;
+    }
+
+  VEC_safe_push (ddr_p, heap, LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo), ddr);
+  return true;
+}
 
 /* Function vect_analyze_data_ref_dependence.
 
    Return TRUE if there (might) exist a dependence between a memory-reference
-   DRA and a memory-reference DRB.  */
+   DRA and a memory-reference DRB.  When versioning for alias may check a
+   dependence at run-time, return FALSE.  */
       
 static bool
 vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
@@ -1160,7 +1231,8 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
           fprintf (vect_dump, " and ");
           print_generic_expr (vect_dump, DR_REF (drb), TDF_SLIM);
         }
-      return true;
+      /* Add to list of ddrs that need to be tested at run-time.  */
+      return !vect_mark_for_runtime_alias_test (ddr, loop_vinfo);
     }
 
   if (DDR_NUM_DIST_VECTS (ddr) == 0)
@@ -1172,7 +1244,8 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
           fprintf (vect_dump, " and ");
           print_generic_expr (vect_dump, DR_REF (drb), TDF_SLIM);
         }
-      return true;
+      /* Add to list of ddrs that need to be tested at run-time.  */
+      return !vect_mark_for_runtime_alias_test (ddr, loop_vinfo);
     }    
 
   loop_depth = index_in_loop_nest (loop->num, DDR_LOOP_NEST (ddr));
@@ -1224,10 +1297,10 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
          continue;
        }
 
-      if (vect_print_dump_info (REPORT_DR_DETAILS))
+      if (vect_print_dump_info (REPORT_UNVECTORIZED_LOOPS))
        {
          fprintf (vect_dump,
-                  "versioning for alias required: possible dependence "
+                  "not vectorized, possible dependence "
                   "between data-refs ");
          print_generic_expr (vect_dump, DR_REF (dra), TDF_SLIM);
          fprintf (vect_dump, " and ");
@@ -1240,88 +1313,6 @@ vect_analyze_data_ref_dependence (struct data_dependence_relation *ddr,
   return false;
 }
 
-/* Return TRUE if DDR_NEW is already found in MAY_ALIAS_DDRS list.  */
-
-static bool
-vect_is_duplicate_ddr (VEC (ddr_p, heap) * may_alias_ddrs, ddr_p ddr_new)
-{
-  unsigned i;
-  ddr_p ddr;
-
-  for (i = 0; VEC_iterate (ddr_p, may_alias_ddrs, i, ddr); i++)
-    {
-      tree dref_A_i, dref_B_i, dref_A_j, dref_B_j;
-
-      dref_A_i = DR_REF (DDR_A (ddr));
-      dref_B_i = DR_REF (DDR_B (ddr));
-      dref_A_j = DR_REF (DDR_A (ddr_new));
-      dref_B_j = DR_REF (DDR_B (ddr_new));
-
-      if ((operand_equal_p (dref_A_i, dref_A_j, 0)
-          && operand_equal_p (dref_B_i, dref_B_j, 0))
-         || (operand_equal_p (dref_A_i, dref_B_j, 0)
-             && operand_equal_p (dref_B_i, dref_A_j, 0)))
-       {
-         if (vect_print_dump_info (REPORT_DR_DETAILS))
-           {
-             fprintf (vect_dump, "found same pair of data references ");
-             print_generic_expr (vect_dump, dref_A_i, TDF_SLIM);
-             fprintf (vect_dump, " and ");
-             print_generic_expr (vect_dump, dref_B_i, TDF_SLIM);
-           }
-         return true;
-       }
-    }
-  return false;
-}
-
-/* Save DDR in LOOP_VINFO list of ddrs that may alias and need to be
-   tested at run-time.  Returns false if number of run-time checks
-   inserted by vectorizer is greater than maximum defined by
-   PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS.  */
-static bool
-vect_mark_for_runtime_alias_test (ddr_p ddr, loop_vec_info loop_vinfo)
-{
-  struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
-
-  if (vect_print_dump_info (REPORT_DR_DETAILS))
-    {
-      fprintf (vect_dump, "mark for run-time aliasing test between ");
-      print_generic_expr (vect_dump, DR_REF (DDR_A (ddr)), TDF_SLIM);
-      fprintf (vect_dump, " and ");
-      print_generic_expr (vect_dump, DR_REF (DDR_B (ddr)), TDF_SLIM);
-    }
-
-  /* FORNOW: We don't support versioning with outer-loop vectorization.  */
-  if (loop->inner)
-    {
-      if (vect_print_dump_info (REPORT_DR_DETAILS))
-       fprintf (vect_dump, "versioning not yet supported for outer-loops.");
-      return false;
-    }
-
-  /* Do not add to the list duplicate ddrs.  */
-  if (vect_is_duplicate_ddr (LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo), ddr))
-    return true;
-
-  if (VEC_length (ddr_p, LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo))
-      >= (unsigned) PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS))
-    {
-      if (vect_print_dump_info (REPORT_DR_DETAILS))
-       {
-         fprintf (vect_dump,
-                  "disable versioning for alias - max number of generated "
-                  "checks exceeded.");
-       }
-
-      VEC_truncate (ddr_p, LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo), 0);
-
-      return false;
-    }
-  VEC_safe_push (ddr_p, heap, LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo), ddr);
-  return true;
-}
-
 /* Function vect_analyze_data_ref_dependences.
           
    Examine all the data references in the loop, and make sure there do not
@@ -1339,11 +1330,7 @@ vect_analyze_data_ref_dependences (loop_vec_info loop_vinfo)
      
   for (i = 0; VEC_iterate (ddr_p, ddrs, i, ddr); i++)
     if (vect_analyze_data_ref_dependence (ddr, loop_vinfo))
-      {
-       /* Add to list of ddrs that need to be tested at run-time.  */
-       if (!vect_mark_for_runtime_alias_test (ddr, loop_vinfo))
       return false;
-      }
 
   return true;
 }
@@ -2241,11 +2228,16 @@ vect_analyze_group_access (struct data_reference *dr)
 
       /* Check that the size of the interleaving is equal to STEP for stores,
          i.e., that there are no gaps.  */
-      if (!DR_IS_READ (dr) && dr_step != count_in_bytes)
+      if (dr_step != count_in_bytes)
         {
-          if (vect_print_dump_info (REPORT_DETAILS))
-            fprintf (vect_dump, "interleaved store with gaps");
-          return false;
+          if (DR_IS_READ (dr))
+            slp_impossible = true;
+          else
+            {
+              if (vect_print_dump_info (REPORT_DETAILS))
+                fprintf (vect_dump, "interleaved store with gaps");
+              return false;
+            }
         }
 
       /* Check that STEP is a multiple of type size.  */
@@ -2289,7 +2281,7 @@ vect_analyze_group_access (struct data_reference *dr)
 
 
 /* Analyze the access pattern of the data-reference DR.
-   In case of non-consecutive accesse call vect_analyze_group_access() to
+   In case of non-consecutive accesses call vect_analyze_group_access() to
    analyze groups of strided accesses.  */
 
 static bool
@@ -2316,6 +2308,10 @@ vect_analyze_data_ref_access (struct data_reference *dr)
 
   if (nested_in_vect_loop_p (loop, stmt))
     {
+      /* Interleaved accesses are not yet supported within outer-loop
+        vectorization for references in the inner-loop.  */
+      DR_GROUP_FIRST_DR (vinfo_for_stmt (stmt)) = NULL_TREE;
+
       /* For the rest of the analysis we use the outer-loop step.  */
       step = STMT_VINFO_DR_STEP (stmt_info);
       dr_step = TREE_INT_CST_LOW (step);
@@ -2381,6 +2377,77 @@ vect_analyze_data_ref_accesses (loop_vec_info loop_vinfo)
   return true;
 }
 
+/* Function vect_prune_runtime_alias_test_list.
+
+   Prune a list of ddrs to be tested at run-time by versioning for alias.
+   Return FALSE if resulting list of ddrs is longer then allowed by
+   PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS, otherwise return TRUE.  */
+
+static bool
+vect_prune_runtime_alias_test_list (loop_vec_info loop_vinfo)
+{
+  VEC (ddr_p, heap) * ddrs =
+    LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo);
+  unsigned i, j;
+
+  if (vect_print_dump_info (REPORT_DETAILS))
+    fprintf (vect_dump, "=== vect_prune_runtime_alias_test_list ===");
+
+  for (i = 0; i < VEC_length (ddr_p, ddrs); )
+    {
+      bool found;
+      ddr_p ddr_i;
+
+      ddr_i = VEC_index (ddr_p, ddrs, i);
+      found = false;
+
+      for (j = 0; j < i; j++)
+        {
+         ddr_p ddr_j = VEC_index (ddr_p, ddrs, j);
+
+         if (vect_vfa_range_equal (ddr_i, ddr_j))
+           {
+             if (vect_print_dump_info (REPORT_DR_DETAILS))
+               {
+                 fprintf (vect_dump, "found equal ranges ");
+                 print_generic_expr (vect_dump, DR_REF (DDR_A (ddr_i)), TDF_SLIM);
+                 fprintf (vect_dump, ", ");
+                 print_generic_expr (vect_dump, DR_REF (DDR_B (ddr_i)), TDF_SLIM);
+                 fprintf (vect_dump, " and ");
+                 print_generic_expr (vect_dump, DR_REF (DDR_A (ddr_j)), TDF_SLIM);
+                 fprintf (vect_dump, ", ");
+                 print_generic_expr (vect_dump, DR_REF (DDR_B (ddr_j)), TDF_SLIM);
+               }
+             found = true;
+             break;
+           }
+       }
+      
+      if (found)
+      {
+       VEC_ordered_remove (ddr_p, ddrs, i);
+       continue;
+      }
+      i++;
+    }
+
+  if (VEC_length (ddr_p, ddrs) >
+       (unsigned) PARAM_VALUE (PARAM_VECT_MAX_VERSION_FOR_ALIAS_CHECKS))
+    {
+      if (vect_print_dump_info (REPORT_DR_DETAILS))
+       {
+         fprintf (vect_dump,
+                  "disable versioning for alias - max number of generated "
+                  "checks exceeded.");
+       }
+
+      VEC_truncate (ddr_p, LOOP_VINFO_MAY_ALIAS_DDRS (loop_vinfo), 0);
+
+      return false;
+    }
+
+  return true;
+}
 
 /* Recursively free the memory allocated for the SLP tree rooted at NODE.  */
 
@@ -2601,6 +2668,16 @@ vect_build_slp_tree (loop_vec_info loop_vinfo, slp_tree *node,
 
       scalar_type = TREE_TYPE (GIMPLE_STMT_OPERAND (stmt, 0));
       vectype = get_vectype_for_scalar_type (scalar_type);
+      if (!vectype)
+        {
+          if (vect_print_dump_info (REPORT_SLP))
+            {
+              fprintf (vect_dump, "Build SLP failed: unsupported data-type ");
+              print_generic_expr (vect_dump, scalar_type, TDF_SLIM);
+            }
+          return false;
+        }
+
       gcc_assert (LOOP_VINFO_VECT_FACTOR (loop_vinfo));
       vectorization_factor = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
       ncopies = vectorization_factor / TYPE_VECTOR_SUBPARTS (vectype);
@@ -2635,6 +2712,13 @@ vect_build_slp_tree (loop_vec_info loop_vinfo, slp_tree *node,
                  return false;
                }
              icode = (int) optab->handlers[(int) vec_mode].insn_code;
+             if (icode == CODE_FOR_nothing)
+               {
+                 if (vect_print_dump_info (REPORT_SLP))
+                   fprintf (vect_dump,
+                            "Build SLP failed: op not supported by target.");
+                 return false;
+               }
              optab_op2_mode = insn_data[icode].operand[2].mode;
              if (!VECTOR_MODE_P (optab_op2_mode))
                {
@@ -2899,6 +2983,16 @@ vect_analyze_slp_instance (loop_vec_info loop_vinfo, tree stmt)
   /* FORNOW: multiple types are not supported.  */
   scalar_type = TREE_TYPE (DR_REF (STMT_VINFO_DATA_REF (vinfo_for_stmt (stmt))));
   vectype = get_vectype_for_scalar_type (scalar_type);
+  if (!vectype)
+    {
+      if (vect_print_dump_info (REPORT_SLP))
+        {
+          fprintf (vect_dump, "Build SLP failed: unsupported data-type ");
+          print_generic_expr (vect_dump, scalar_type, TDF_SLIM);
+        }
+      return false;
+    }
+
   nunits = TYPE_VECTOR_SUBPARTS (vectype);
   vectorization_factor = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
   ncopies = vectorization_factor / nunits;
@@ -3179,9 +3273,11 @@ vect_analyze_data_refs (loop_vec_info loop_vinfo)
 
          /* Build a reference to the first location accessed by the 
             inner-loop: *(BASE+INIT). (The first location is actually
-            BASE+INIT+OFFSET, but we add OFFSET separately later.  */
-         tree inner_base = build_fold_indirect_ref 
-                               (fold_build2 (PLUS_EXPR, TREE_TYPE (base), base, init));
+            BASE+INIT+OFFSET, but we add OFFSET separately later).  */
+          tree inner_base = build_fold_indirect_ref
+                                (fold_build2 (POINTER_PLUS_EXPR,
+                                              TREE_TYPE (base), base, 
+                                              fold_convert (sizetype, init)));
 
          if (vect_print_dump_info (REPORT_DETAILS))
            {
@@ -3888,7 +3984,7 @@ vect_analyze_loop_1 (struct loop *loop)
    - the loop exit condition is simple enough, and the number of iterations
      can be analyzed (a countable loop).  */
 
-static loop_vec_info
+loop_vec_info
 vect_analyze_loop_form (struct loop *loop)
 {
   loop_vec_info loop_vinfo;
@@ -4107,6 +4203,7 @@ vect_analyze_loop_form (struct loop *loop)
 
   loop_vinfo = new_loop_vec_info (loop);
   LOOP_VINFO_NITERS (loop_vinfo) = number_of_iterations;
+  LOOP_VINFO_NITERS_UNCHANGED (loop_vinfo) = number_of_iterations;
 
   STMT_VINFO_TYPE (vinfo_for_stmt (loop_cond)) = loop_exit_ctrl_vec_info_type;
 
@@ -4231,6 +4328,19 @@ vect_analyze_loop (struct loop *loop)
       return NULL;
     }
 
+  /* Prune the list of ddrs to be tested at run-time by versioning for alias.
+     It is important to call pruning after vect_analyze_data_ref_accesses,
+     since we use grouping information gathered by interleaving analysis.  */
+  ok = vect_prune_runtime_alias_test_list (loop_vinfo);
+  if (!ok)
+    {
+      if (vect_print_dump_info (REPORT_DETAILS))
+       fprintf (vect_dump, "too long list of versioning for alias "
+                           "run-time tests.");
+      destroy_loop_vec_info (loop_vinfo, true);
+      return NULL;
+    }
+
   /* Check the SLP opportunities in the loop, analyze and build SLP trees.  */
   ok = vect_analyze_slp (loop_vinfo);
   if (ok)