]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/internal-fn.c
Update copyright years.
[thirdparty/gcc.git] / gcc / internal-fn.c
index 2329e3db22f5fc2715ea373c3219590fbcc12bf8..996f0fb6c67766496f001dba503c96beeb31a8b0 100644 (file)
@@ -1,5 +1,5 @@
 /* Internal functions.
-   Copyright (C) 2011-2020 Free Software Foundation, Inc.
+   Copyright (C) 2011-2021 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-phinodes.h"
 #include "ssa-iterators.h"
 #include "explow.h"
+#include "rtl-iter.h"
 
 /* The names of each internal function, indexed by function number.  */
 const char *const internal_fn_name_array[] = {
@@ -93,7 +94,7 @@ init_internal_fns ()
 {
 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) \
   if (FNSPEC) internal_fn_fnspec_array[IFN_##CODE] = \
-    build_string ((int) sizeof (FNSPEC), FNSPEC ? FNSPEC : "");
+    build_string ((int) sizeof (FNSPEC) - 1, FNSPEC ? FNSPEC : "");
 #include "internal-fn.def"
   internal_fn_fnspec_array[IFN_LAST] = 0;
 }
@@ -105,6 +106,7 @@ init_internal_fns ()
 #define load_lanes_direct { -1, -1, false }
 #define mask_load_lanes_direct { -1, -1, false }
 #define gather_load_direct { 3, 1, false }
+#define len_load_direct { -1, -1, false }
 #define mask_store_direct { 3, 2, false }
 #define store_lanes_direct { 0, 0, false }
 #define mask_store_lanes_direct { 0, 0, false }
@@ -113,6 +115,8 @@ init_internal_fns ()
 #define vec_condu_direct { 0, 0, false }
 #define vec_condeq_direct { 0, 0, false }
 #define scatter_store_direct { 3, 1, false }
+#define len_store_direct { 3, 3, false }
+#define vec_set_direct { 3, 3, false }
 #define unary_direct { 0, 0, true }
 #define binary_direct { 0, 0, true }
 #define ternary_direct { 0, 0, true }
@@ -464,6 +468,111 @@ expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 
 /* This should get expanded in the sanopt pass.  */
 
+static void
+expand_HWASAN_CHECK (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
+/* For hwasan stack tagging:
+   Clear tags on the dynamically allocated space.
+   For use after an object dynamically allocated on the stack goes out of
+   scope.  */
+static void
+expand_HWASAN_ALLOCA_UNPOISON (internal_fn, gcall *gc)
+{
+  gcc_assert (Pmode == ptr_mode);
+  tree restored_position = gimple_call_arg (gc, 0);
+  rtx restored_rtx = expand_expr (restored_position, NULL_RTX, VOIDmode,
+                                 EXPAND_NORMAL);
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  rtx off = expand_simple_binop (Pmode, MINUS, restored_rtx,
+                                stack_pointer_rtx, NULL_RTX, 0,
+                                OPTAB_WIDEN);
+  emit_library_call_value (func, NULL_RTX, LCT_NORMAL, VOIDmode,
+                          virtual_stack_dynamic_rtx, Pmode,
+                          HWASAN_STACK_BACKGROUND, QImode,
+                          off, Pmode);
+}
+
+/* For hwasan stack tagging:
+   Return a tag to be used for a dynamic allocation.  */
+static void
+expand_HWASAN_CHOOSE_TAG (internal_fn, gcall *gc)
+{
+  tree tag = gimple_call_lhs (gc);
+  rtx target = expand_expr (tag, NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  machine_mode mode = GET_MODE (target);
+  gcc_assert (mode == QImode);
+
+  rtx base_tag = targetm.memtag.extract_tag (hwasan_frame_base (), NULL_RTX);
+  gcc_assert (base_tag);
+  rtx tag_offset = gen_int_mode (hwasan_current_frame_tag (), QImode);
+  rtx chosen_tag = expand_simple_binop (QImode, PLUS, base_tag, tag_offset,
+                                       target, /* unsignedp = */1,
+                                       OPTAB_WIDEN);
+  chosen_tag = hwasan_truncate_to_tag_size (chosen_tag, target);
+
+  /* Really need to put the tag into the `target` RTX.  */
+  if (chosen_tag != target)
+    {
+      rtx temp = chosen_tag;
+      gcc_assert (GET_MODE (chosen_tag) == mode);
+      emit_move_insn (target, temp);
+    }
+
+  hwasan_increment_frame_tag ();
+}
+
+/* For hwasan stack tagging:
+   Tag a region of space in the shadow stack according to the base pointer of
+   an object on the stack.  N.b. the length provided in the internal call is
+   required to be aligned to HWASAN_TAG_GRANULE_SIZE.  */
+static void
+expand_HWASAN_MARK (internal_fn, gcall *gc)
+{
+  gcc_assert (ptr_mode == Pmode);
+  HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
+  bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
+
+  tree base = gimple_call_arg (gc, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  rtx base_rtx = expand_normal (base);
+
+  rtx tag = is_poison ? HWASAN_STACK_BACKGROUND
+    : targetm.memtag.extract_tag (base_rtx, NULL_RTX);
+  rtx address = targetm.memtag.untagged_pointer (base_rtx, NULL_RTX);
+
+  tree len = gimple_call_arg (gc, 2);
+  rtx r_len = expand_normal (len);
+
+  rtx func = init_one_libfunc ("__hwasan_tag_memory");
+  emit_library_call (func, LCT_NORMAL, VOIDmode, address, Pmode,
+                    tag, QImode, r_len, Pmode);
+}
+
+/* For hwasan stack tagging:
+   Store a tag into a pointer.  */
+static void
+expand_HWASAN_SET_TAG (internal_fn, gcall *gc)
+{
+  gcc_assert (ptr_mode == Pmode);
+  tree g_target = gimple_call_lhs (gc);
+  tree g_ptr = gimple_call_arg (gc, 0);
+  tree g_tag = gimple_call_arg (gc, 1);
+
+  rtx ptr = expand_normal (g_ptr);
+  rtx tag = expand_expr (g_tag, NULL_RTX, QImode, EXPAND_NORMAL);
+  rtx target = expand_normal (g_target);
+
+  rtx untagged = targetm.memtag.untagged_pointer (ptr, target);
+  rtx tagged_value = targetm.memtag.set_tag (untagged, tag, target);
+  if (tagged_value != target)
+    emit_move_insn (target, tagged_value);
+}
+
+/* This should get expanded in the sanopt pass.  */
+
 static void
 expand_ASAN_CHECK (internal_fn, gcall *)
 {
@@ -549,6 +658,16 @@ get_min_precision (tree arg, signop sign)
       if (++cnt > 30)
        return prec + (orig_sign != sign);
     }
+  if (CONVERT_EXPR_P (arg)
+      && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (arg, 0)))
+      && TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg, 0))) > prec)
+    {
+      /* We have e.g. (unsigned short) y_2 where int y_2 = (int) x_1(D);
+        If y_2's min precision is smaller than prec, return that.  */
+      int oprec = get_min_precision (TREE_OPERAND (arg, 0), sign);
+      if (oprec < prec)
+       return oprec + (orig_sign != sign);
+    }
   if (TREE_CODE (arg) != SSA_NAME)
     return prec + (orig_sign != sign);
   wide_int arg_min, arg_max;
@@ -679,7 +798,7 @@ expand_ubsan_result_store (rtx target, rtx res)
 /* Add sub/add overflow checking to the statement STMT.
    CODE says whether the operation is +, or -.  */
 
-static void
+void
 expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
                        tree arg0, tree arg1, bool unsr_p, bool uns0_p,
                        bool uns1_p, bool is_ubsan, tree *datap)
@@ -1353,6 +1472,37 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
                                   NULL, done_label, profile_probability::very_likely ());
          goto do_error_label;
        case 3:
+         if (get_min_precision (arg1, UNSIGNED)
+             + get_min_precision (arg0, SIGNED) <= GET_MODE_PRECISION (mode))
+           {
+             /* If the first operand is sign extended from narrower type, the
+                second operand is zero extended from narrower type and
+                the sum of the two precisions is smaller or equal to the
+                result precision: if the first argument is at runtime
+                non-negative, maximum result will be 0x7e81 or 0x7f..fe80..01
+                and there will be no overflow, if the first argument is
+                negative and the second argument zero, the result will be
+                0 and there will be no overflow, if the first argument is
+                negative and the second argument positive, the result when
+                treated as signed will be negative (minimum -0x7f80 or
+                -0x7f..f80..0) there there will be always overflow.  So, do
+                res = (U) (s1 * u2)
+                ovf = (S) res < 0  */
+             struct separate_ops ops;
+             ops.code = MULT_EXPR;
+             ops.type
+               = build_nonstandard_integer_type (GET_MODE_PRECISION (mode),
+                                                 1);
+             ops.op0 = make_tree (ops.type, op0);
+             ops.op1 = make_tree (ops.type, op1);
+             ops.op2 = NULL_TREE;
+             ops.location = loc;
+             res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
+             do_compare_rtx_and_jump (res, const0_rtx, GE, false,
+                                      mode, NULL_RTX, NULL, done_label,
+                                      profile_probability::very_likely ());
+             goto do_error_label;
+           }
          rtx_code_label *do_main_label;
          do_main_label = gen_label_rtx ();
          do_compare_rtx_and_jump (op0, const0_rtx, GE, false, mode, NULL_RTX,
@@ -1370,7 +1520,16 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
   /* u1 * u2 -> sr  */
   if (uns0_p && uns1_p && !unsr_p)
     {
-      uns = true;
+      if ((pos_neg0 | pos_neg1) == 1)
+       {
+         /* If both arguments are zero extended from narrower types,
+            the MSB will be clear on both and so we can pretend it is
+            a normal s1 * s2 -> sr multiplication.  */
+         uns0_p = false;
+         uns1_p = false;
+       }
+      else
+       uns = true;
       /* Rest of handling of this case after res is computed.  */
       goto do_main;
     }
@@ -1451,6 +1610,37 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
                                       profile_probability::very_likely ());
              goto do_error_label;
            }
+         if (get_min_precision (arg0, SIGNED)
+             + get_min_precision (arg1, SIGNED) <= GET_MODE_PRECISION (mode))
+           {
+             /* If both operands are sign extended from narrower types and
+                the sum of the two precisions is smaller or equal to the
+                result precision: if both arguments are at runtime
+                non-negative, maximum result will be 0x3f01 or 0x3f..f0..01
+                and there will be no overflow, if both arguments are negative,
+                maximum result will be 0x40..00 and there will be no overflow
+                either, if one argument is positive and the other argument
+                negative, the result when treated as signed will be negative
+                and there will be always overflow, and if one argument is
+                zero and the other negative the result will be zero and no
+                overflow.  So, do
+                res = (U) (s1 * s2)
+                ovf = (S) res < 0  */
+             struct separate_ops ops;
+             ops.code = MULT_EXPR;
+             ops.type
+               = build_nonstandard_integer_type (GET_MODE_PRECISION (mode),
+                                                 1);
+             ops.op0 = make_tree (ops.type, op0);
+             ops.op1 = make_tree (ops.type, op1);
+             ops.op2 = NULL_TREE;
+             ops.location = loc;
+             res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
+             do_compare_rtx_and_jump (res, const0_rtx, GE, false,
+                                      mode, NULL_RTX, NULL, done_label,
+                                      profile_probability::very_likely ());
+             goto do_error_label;
+           }
          /* The general case, do all the needed comparisons at runtime.  */
          rtx_code_label *do_main_label, *after_negate_label;
          rtx rop0, rop1;
@@ -1627,6 +1817,9 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
                                     profile_probability::very_likely ());
          else
            {
+             /* RES is used more than once, place it in a pseudo.  */
+             res = force_reg (mode, res);
+
              rtx signbit = expand_shift (RSHIFT_EXPR, mode, res, prec - 1,
                                          NULL_RTX, 0);
              /* RES is low half of the double width result, HIPART
@@ -2483,10 +2676,10 @@ expand_call_mem_ref (tree type, gcall *stmt, int index)
   return fold_build2 (MEM_REF, type, addr, build_int_cst (alias_ptr_type, 0));
 }
 
-/* Expand MASK_LOAD{,_LANES} call STMT using optab OPTAB.  */
+/* Expand MASK_LOAD{,_LANES} or LEN_LOAD call STMT using optab OPTAB.  */
 
 static void
-expand_mask_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+expand_partial_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 {
   class expand_operand ops[3];
   tree type, lhs, rhs, maskt;
@@ -2502,6 +2695,8 @@ expand_mask_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 
   if (optab == vec_mask_load_lanes_optab)
     icode = get_multi_vector_move (type, optab);
+  else if (optab == len_load_optab)
+    icode = direct_optab_handler (optab, TYPE_MODE (type));
   else
     icode = convert_optab_handler (optab, TYPE_MODE (type),
                                   TYPE_MODE (TREE_TYPE (maskt)));
@@ -2512,18 +2707,24 @@ expand_mask_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
   target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
   create_output_operand (&ops[0], target, TYPE_MODE (type));
   create_fixed_operand (&ops[1], mem);
-  create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
+  if (optab == len_load_optab)
+    create_convert_operand_from (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)),
+                                TYPE_UNSIGNED (TREE_TYPE (maskt)));
+  else
+    create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
   expand_insn (icode, 3, ops);
   if (!rtx_equal_p (target, ops[0].value))
     emit_move_insn (target, ops[0].value);
 }
 
+#define expand_mask_load_optab_fn expand_partial_load_optab_fn
 #define expand_mask_load_lanes_optab_fn expand_mask_load_optab_fn
+#define expand_len_load_optab_fn expand_partial_load_optab_fn
 
-/* Expand MASK_STORE{,_LANES} call STMT using optab OPTAB.  */
+/* Expand MASK_STORE{,_LANES} or LEN_STORE call STMT using optab OPTAB.  */
 
 static void
-expand_mask_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+expand_partial_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 {
   class expand_operand ops[3];
   tree type, lhs, rhs, maskt;
@@ -2537,6 +2738,8 @@ expand_mask_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 
   if (optab == vec_mask_store_lanes_optab)
     icode = get_multi_vector_move (type, optab);
+  else if (optab == len_store_optab)
+    icode = direct_optab_handler (optab, TYPE_MODE (type));
   else
     icode = convert_optab_handler (optab, TYPE_MODE (type),
                                   TYPE_MODE (TREE_TYPE (maskt)));
@@ -2547,11 +2750,17 @@ expand_mask_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
   reg = expand_normal (rhs);
   create_fixed_operand (&ops[0], mem);
   create_input_operand (&ops[1], reg, TYPE_MODE (type));
-  create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
+  if (optab == len_store_optab)
+    create_convert_operand_from (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)),
+                                TYPE_UNSIGNED (TREE_TYPE (maskt)));
+  else
+    create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
   expand_insn (icode, 3, ops);
 }
 
+#define expand_mask_store_optab_fn expand_partial_store_optab_fn
 #define expand_mask_store_lanes_optab_fn expand_mask_store_optab_fn
+#define expand_len_store_optab_fn expand_partial_store_optab_fn
 
 /* Expand VCOND, VCONDU and VCONDEQ optab internal functions.
    The expansion of STMT happens based on OPTAB table associated.  */
@@ -2623,7 +2832,7 @@ expand_vect_cond_mask_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
   rtx_op2 = expand_normal (op2);
 
   mask = force_reg (mask_mode, mask);
-  rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1);
+  rtx_op1 = force_reg (mode, rtx_op1);
 
   rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
   create_output_operand (&ops[0], target, mode);
@@ -2637,6 +2846,45 @@ expand_vect_cond_mask_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 
 #define expand_vec_cond_mask_optab_fn expand_vect_cond_mask_optab_fn
 
+/* Expand VEC_SET internal functions.  */
+
+static void
+expand_vec_set_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+{
+  tree lhs = gimple_call_lhs (stmt);
+  tree op0 = gimple_call_arg (stmt, 0);
+  tree op1 = gimple_call_arg (stmt, 1);
+  tree op2 = gimple_call_arg (stmt, 2);
+  rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+  rtx src = expand_normal (op0);
+
+  machine_mode outermode = TYPE_MODE (TREE_TYPE (op0));
+  scalar_mode innermode = GET_MODE_INNER (outermode);
+
+  rtx value = expand_normal (op1);
+  rtx pos = expand_normal (op2);
+
+  class expand_operand ops[3];
+  enum insn_code icode = optab_handler (optab, outermode);
+
+  if (icode != CODE_FOR_nothing)
+    {
+      rtx temp = gen_reg_rtx (outermode);
+      emit_move_insn (temp, src);
+
+      create_fixed_operand (&ops[0], temp);
+      create_input_operand (&ops[1], value, innermode);
+      create_convert_operand_from (&ops[2], pos, TYPE_MODE (TREE_TYPE (op2)),
+                                  true);
+      if (maybe_expand_insn (icode, 3, ops))
+       {
+         emit_move_insn (target, temp);
+         return;
+       }
+    }
+  gcc_unreachable ();
+}
+
 static void
 expand_ABNORMAL_DISPATCHER (internal_fn, gcall *)
 {
@@ -2924,6 +3172,32 @@ expand_gather_load_optab_fn (internal_fn, gcall *stmt, direct_optab optab)
     emit_move_insn (lhs_rtx, ops[0].value);
 }
 
+/* Helper for expand_DIVMOD.  Return true if the sequence starting with
+   INSN contains any call insns or insns with {,U}{DIV,MOD} rtxes.  */
+
+static bool
+contains_call_div_mod (rtx_insn *insn)
+{
+  subrtx_iterator::array_type array;
+  for (; insn; insn = NEXT_INSN (insn))
+    if (CALL_P (insn))
+      return true;
+    else if (INSN_P (insn))
+      FOR_EACH_SUBRTX (iter, array, PATTERN (insn), NONCONST)
+       switch (GET_CODE (*iter))
+         {
+         case CALL:
+         case DIV:
+         case UDIV:
+         case MOD:
+         case UMOD:
+           return true;
+         default:
+           break;
+         }
+  return false;
+ }
+
 /* Expand DIVMOD() using:
  a) optab handler for udivmod/sdivmod if it is available.
  b) If optab_handler doesn't exist, generate call to
@@ -2946,10 +3220,85 @@ expand_DIVMOD (internal_fn, gcall *call_stmt)
   rtx op1 = expand_normal (arg1);
   rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
 
-  rtx quotient, remainder, libfunc;
+  rtx quotient = NULL_RTX, remainder = NULL_RTX;
+  rtx_insn *insns = NULL;
+
+  if (TREE_CODE (arg1) == INTEGER_CST)
+    {
+      /* For DIVMOD by integral constants, there could be efficient code
+        expanded inline e.g. using shifts and plus/minus.  Try to expand
+        the division and modulo and if it emits any library calls or any
+        {,U}{DIV,MOD} rtxes throw it away and use a divmod optab or
+        divmod libcall.  */
+      scalar_int_mode int_mode;
+      if (remainder == NULL_RTX
+         && optimize
+         && CONST_INT_P (op1)
+         && !pow2p_hwi (INTVAL (op1))
+         && is_int_mode (TYPE_MODE (type), &int_mode)
+         && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
+         && optab_handler (and_optab, word_mode) != CODE_FOR_nothing
+         && optab_handler (add_optab, word_mode) != CODE_FOR_nothing
+         && optimize_insn_for_speed_p ())
+       {
+         rtx_insn *last = get_last_insn ();
+         remainder = NULL_RTX;
+         quotient = expand_doubleword_divmod (int_mode, op0, op1, &remainder,
+                                              TYPE_UNSIGNED (type));
+         if (quotient != NULL_RTX)
+           {
+             if (optab_handler (mov_optab, int_mode) != CODE_FOR_nothing)
+               {
+                 rtx_insn *move = emit_move_insn (quotient, quotient);
+                 set_dst_reg_note (move, REG_EQUAL,
+                                   gen_rtx_fmt_ee (TYPE_UNSIGNED (type)
+                                                   ? UDIV : DIV, int_mode,
+                                                   copy_rtx (op0), op1),
+                                   quotient);
+                 move = emit_move_insn (remainder, remainder);
+                 set_dst_reg_note (move, REG_EQUAL,
+                                   gen_rtx_fmt_ee (TYPE_UNSIGNED (type)
+                                                   ? UMOD : MOD, int_mode,
+                                                   copy_rtx (op0), op1),
+                                   quotient);
+               }
+           }
+         else
+           delete_insns_since (last);
+       }
+
+      if (remainder == NULL_RTX)
+       {
+         struct separate_ops ops;
+         ops.code = TRUNC_DIV_EXPR;
+         ops.type = type;
+         ops.op0 = make_tree (ops.type, op0);
+         ops.op1 = arg1;
+         ops.op2 = NULL_TREE;
+         ops.location = gimple_location (call_stmt);
+         start_sequence ();
+         quotient = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
+         if (contains_call_div_mod (get_insns ()))
+           quotient = NULL_RTX;
+         else
+           {
+             ops.code = TRUNC_MOD_EXPR;
+             remainder = expand_expr_real_2 (&ops, NULL_RTX, mode,
+                                             EXPAND_NORMAL);
+             if (contains_call_div_mod (get_insns ()))
+               remainder = NULL_RTX;
+           }
+         if (remainder)
+           insns = get_insns ();
+         end_sequence ();
+       }
+    }
+
+  if (remainder)
+    emit_insn (insns);
 
   /* Check if optab_handler exists for divmod_optab for given mode.  */
-  if (optab_handler (tab, mode) != CODE_FOR_nothing)
+  else if (optab_handler (tab, mode) != CODE_FOR_nothing)
     {
       quotient = gen_reg_rtx (mode);
       remainder = gen_reg_rtx (mode);
@@ -2957,7 +3306,7 @@ expand_DIVMOD (internal_fn, gcall *call_stmt)
     }
 
   /* Generate call to divmod libfunc if it exists.  */
-  else if ((libfunc = optab_libfunc (tab, mode)) != NULL_RTX)
+  else if (rtx libfunc = optab_libfunc (tab, mode))
     targetm.expand_divmod_libfunc (libfunc, mode, op0, op1,
                                   &quotient, &remainder);
 
@@ -3213,11 +3562,12 @@ multi_vector_optab_supported_p (convert_optab optab, tree_pair types,
 #define direct_cond_unary_optab_supported_p direct_optab_supported_p
 #define direct_cond_binary_optab_supported_p direct_optab_supported_p
 #define direct_cond_ternary_optab_supported_p direct_optab_supported_p
-#define direct_mask_load_optab_supported_p direct_optab_supported_p
+#define direct_mask_load_optab_supported_p convert_optab_supported_p
 #define direct_load_lanes_optab_supported_p multi_vector_optab_supported_p
 #define direct_mask_load_lanes_optab_supported_p multi_vector_optab_supported_p
 #define direct_gather_load_optab_supported_p convert_optab_supported_p
-#define direct_mask_store_optab_supported_p direct_optab_supported_p
+#define direct_len_load_optab_supported_p direct_optab_supported_p
+#define direct_mask_store_optab_supported_p convert_optab_supported_p
 #define direct_store_lanes_optab_supported_p multi_vector_optab_supported_p
 #define direct_mask_store_lanes_optab_supported_p multi_vector_optab_supported_p
 #define direct_vec_cond_mask_optab_supported_p multi_vector_optab_supported_p
@@ -3225,11 +3575,13 @@ multi_vector_optab_supported_p (convert_optab optab, tree_pair types,
 #define direct_vec_condu_optab_supported_p multi_vector_optab_supported_p
 #define direct_vec_condeq_optab_supported_p multi_vector_optab_supported_p
 #define direct_scatter_store_optab_supported_p convert_optab_supported_p
+#define direct_len_store_optab_supported_p direct_optab_supported_p
 #define direct_while_optab_supported_p convert_optab_supported_p
 #define direct_fold_extract_optab_supported_p direct_optab_supported_p
 #define direct_fold_left_optab_supported_p direct_optab_supported_p
 #define direct_mask_fold_left_optab_supported_p direct_optab_supported_p
 #define direct_check_ptrs_optab_supported_p direct_optab_supported_p
+#define direct_vec_set_optab_supported_p direct_optab_supported_p
 
 /* Return the optab used by internal function FN.  */
 
@@ -3591,6 +3943,7 @@ internal_load_fn_p (internal_fn fn)
     case IFN_MASK_LOAD_LANES:
     case IFN_GATHER_LOAD:
     case IFN_MASK_GATHER_LOAD:
+    case IFN_LEN_LOAD:
       return true;
 
     default:
@@ -3610,6 +3963,7 @@ internal_store_fn_p (internal_fn fn)
     case IFN_MASK_STORE_LANES:
     case IFN_SCATTER_STORE:
     case IFN_MASK_SCATTER_STORE:
+    case IFN_LEN_STORE:
       return true;
 
     default:
@@ -3668,8 +4022,10 @@ internal_fn_stored_value_index (internal_fn fn)
   switch (fn)
     {
     case IFN_MASK_STORE:
+    case IFN_MASK_STORE_LANES:
     case IFN_SCATTER_STORE:
     case IFN_MASK_SCATTER_STORE:
+    case IFN_LEN_STORE:
       return 3;
 
     default: