+static void
+gen_intv_fp_libfunc (optab optable, const char *name, char suffix,
+ enum machine_mode mode)
+{
+ if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT)
+ gen_fp_libfunc (optable, name, suffix, mode);
+ if (GET_MODE_CLASS (mode) == MODE_INT)
+ {
+ int len = strlen (name);
+ char *v_name = XALLOCAVEC (char, len + 2);
+ strcpy (v_name, name);
+ v_name[len] = 'v';
+ v_name[len + 1] = 0;
+ gen_int_libfunc (optable, v_name, suffix, mode);
+ }
+}
+
+/* Like gen_libfunc, but verify that FP or INT or FIXED operation is
+ involved. */
+
+static void
+gen_int_fp_fixed_libfunc (optab optable, const char *name, char suffix,
+ enum machine_mode mode)
+{
+ if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT)
+ gen_fp_libfunc (optable, name, suffix, mode);
+ if (INTEGRAL_MODE_P (mode))
+ gen_int_libfunc (optable, name, suffix, mode);
+ if (ALL_FIXED_POINT_MODE_P (mode))
+ gen_fixed_libfunc (optable, name, suffix, mode);
+}
+
+/* Like gen_libfunc, but verify that FP or INT or signed FIXED operation is
+ involved. */
+
+static void
+gen_int_fp_signed_fixed_libfunc (optab optable, const char *name, char suffix,
+ enum machine_mode mode)
+{
+ if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT)
+ gen_fp_libfunc (optable, name, suffix, mode);
+ if (INTEGRAL_MODE_P (mode))
+ gen_int_libfunc (optable, name, suffix, mode);
+ if (SIGNED_FIXED_POINT_MODE_P (mode))
+ gen_signed_fixed_libfunc (optable, name, suffix, mode);
+}
+
+/* Like gen_libfunc, but verify that INT or FIXED operation is
+ involved. */
+
+static void
+gen_int_fixed_libfunc (optab optable, const char *name, char suffix,
+ enum machine_mode mode)
+{
+ if (INTEGRAL_MODE_P (mode))
+ gen_int_libfunc (optable, name, suffix, mode);
+ if (ALL_FIXED_POINT_MODE_P (mode))
+ gen_fixed_libfunc (optable, name, suffix, mode);
+}
+
+/* Like gen_libfunc, but verify that INT or signed FIXED operation is
+ involved. */
+
+static void
+gen_int_signed_fixed_libfunc (optab optable, const char *name, char suffix,
+ enum machine_mode mode)
+{
+ if (INTEGRAL_MODE_P (mode))
+ gen_int_libfunc (optable, name, suffix, mode);
+ if (SIGNED_FIXED_POINT_MODE_P (mode))
+ gen_signed_fixed_libfunc (optable, name, suffix, mode);
+}
+
+/* Like gen_libfunc, but verify that INT or unsigned FIXED operation is
+ involved. */
+
+static void
+gen_int_unsigned_fixed_libfunc (optab optable, const char *name, char suffix,
+ enum machine_mode mode)
+{
+ if (INTEGRAL_MODE_P (mode))
+ gen_int_libfunc (optable, name, suffix, mode);
+ if (UNSIGNED_FIXED_POINT_MODE_P (mode))
+ gen_unsigned_fixed_libfunc (optable, name, suffix, mode);
+}
+
+/* Initialize the libfunc fields of an entire group of entries of an
+ inter-mode-class conversion optab. The string formation rules are
+ similar to the ones for init_libfuncs, above, but instead of having
+ a mode name and an operand count these functions have two mode names
+ and no operand count. */
+
+static void
+gen_interclass_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ size_t opname_len = strlen (opname);
+ size_t mname_len = 0;
+
+ const char *fname, *tname;
+ const char *q;
+ char *libfunc_name, *suffix;
+ char *nondec_name, *dec_name, *nondec_suffix, *dec_suffix;
+ char *p;
+
+ /* If this is a decimal conversion, add the current BID vs. DPD prefix that
+ depends on which underlying decimal floating point format is used. */
+ const size_t dec_len = sizeof (DECIMAL_PREFIX) - 1;
+
+ mname_len = strlen (GET_MODE_NAME (tmode)) + strlen (GET_MODE_NAME (fmode));
+
+ nondec_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1);
+ nondec_name[0] = '_';
+ nondec_name[1] = '_';
+ memcpy (&nondec_name[2], opname, opname_len);
+ nondec_suffix = nondec_name + opname_len + 2;
+
+ dec_name = XALLOCAVEC (char, 2 + dec_len + opname_len + mname_len + 1 + 1);
+ dec_name[0] = '_';
+ dec_name[1] = '_';
+ memcpy (&dec_name[2], DECIMAL_PREFIX, dec_len);
+ memcpy (&dec_name[2+dec_len], opname, opname_len);
+ dec_suffix = dec_name + dec_len + opname_len + 2;
+
+ fname = GET_MODE_NAME (fmode);
+ tname = GET_MODE_NAME (tmode);
+
+ if (DECIMAL_FLOAT_MODE_P(fmode) || DECIMAL_FLOAT_MODE_P(tmode))
+ {
+ libfunc_name = dec_name;
+ suffix = dec_suffix;
+ }
+ else
+ {
+ libfunc_name = nondec_name;
+ suffix = nondec_suffix;
+ }
+
+ p = suffix;
+ for (q = fname; *q; p++, q++)
+ *p = TOLOWER (*q);
+ for (q = tname; *q; p++, q++)
+ *p = TOLOWER (*q);
+
+ *p = '\0';
+
+ set_conv_libfunc (tab, tmode, fmode,
+ ggc_alloc_string (libfunc_name, p - libfunc_name));
+}
+
+/* Same as gen_interclass_conv_libfunc but verify that we are producing
+ int->fp conversion. */
+
+static void
+gen_int_to_fp_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (GET_MODE_CLASS (fmode) != MODE_INT)
+ return;
+ if (GET_MODE_CLASS (tmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (tmode))
+ return;
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* ufloat_optab is special by using floatun for FP and floatuns decimal fp
+ naming scheme. */
+
+static void
+gen_ufloat_conv_libfunc (convert_optab tab,
+ const char *opname ATTRIBUTE_UNUSED,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (DECIMAL_FLOAT_MODE_P (tmode))
+ gen_int_to_fp_conv_libfunc (tab, "floatuns", tmode, fmode);
+ else
+ gen_int_to_fp_conv_libfunc (tab, "floatun", tmode, fmode);
+}
+
+/* Same as gen_interclass_conv_libfunc but verify that we are producing
+ fp->int conversion. */
+
+static void
+gen_int_to_fp_nondecimal_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (GET_MODE_CLASS (fmode) != MODE_INT)
+ return;
+ if (GET_MODE_CLASS (tmode) != MODE_FLOAT)
+ return;
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* Same as gen_interclass_conv_libfunc but verify that we are producing
+ fp->int conversion with no decimal floating point involved. */
+
+static void
+gen_fp_to_int_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (GET_MODE_CLASS (fmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (fmode))
+ return;
+ if (GET_MODE_CLASS (tmode) != MODE_INT)
+ return;
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* Initialize the libfunc fields of an of an intra-mode-class conversion optab.
+ The string formation rules are
+ similar to the ones for init_libfunc, above. */
+
+static void
+gen_intraclass_conv_libfunc (convert_optab tab, const char *opname,
+ enum machine_mode tmode, enum machine_mode fmode)
+{
+ size_t opname_len = strlen (opname);
+ size_t mname_len = 0;
+
+ const char *fname, *tname;
+ const char *q;
+ char *nondec_name, *dec_name, *nondec_suffix, *dec_suffix;
+ char *libfunc_name, *suffix;
+ char *p;
+
+ /* If this is a decimal conversion, add the current BID vs. DPD prefix that
+ depends on which underlying decimal floating point format is used. */
+ const size_t dec_len = sizeof (DECIMAL_PREFIX) - 1;
+
+ mname_len = strlen (GET_MODE_NAME (tmode)) + strlen (GET_MODE_NAME (fmode));
+
+ nondec_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1);
+ nondec_name[0] = '_';
+ nondec_name[1] = '_';
+ memcpy (&nondec_name[2], opname, opname_len);
+ nondec_suffix = nondec_name + opname_len + 2;
+
+ dec_name = XALLOCAVEC (char, 2 + dec_len + opname_len + mname_len + 1 + 1);
+ dec_name[0] = '_';
+ dec_name[1] = '_';
+ memcpy (&dec_name[2], DECIMAL_PREFIX, dec_len);
+ memcpy (&dec_name[2 + dec_len], opname, opname_len);
+ dec_suffix = dec_name + dec_len + opname_len + 2;
+
+ fname = GET_MODE_NAME (fmode);
+ tname = GET_MODE_NAME (tmode);
+
+ if (DECIMAL_FLOAT_MODE_P(fmode) || DECIMAL_FLOAT_MODE_P(tmode))
+ {
+ libfunc_name = dec_name;
+ suffix = dec_suffix;
+ }
+ else
+ {
+ libfunc_name = nondec_name;
+ suffix = nondec_suffix;
+ }
+
+ p = suffix;
+ for (q = fname; *q; p++, q++)
+ *p = TOLOWER (*q);
+ for (q = tname; *q; p++, q++)
+ *p = TOLOWER (*q);
+
+ *p++ = '2';
+ *p = '\0';
+
+ set_conv_libfunc (tab, tmode, fmode,
+ ggc_alloc_string (libfunc_name, p - libfunc_name));
+}
+
+/* Pick proper libcall for trunc_optab. We need to chose if we do
+ truncation or extension and interclass or intraclass. */
+
+static void
+gen_trunc_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (GET_MODE_CLASS (tmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (tmode))
+ return;
+ if (GET_MODE_CLASS (fmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (fmode))
+ return;
+ if (tmode == fmode)
+ return;
+
+ if ((GET_MODE_CLASS (tmode) == MODE_FLOAT && DECIMAL_FLOAT_MODE_P (fmode))
+ || (GET_MODE_CLASS (fmode) == MODE_FLOAT && DECIMAL_FLOAT_MODE_P (tmode)))
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+
+ if (GET_MODE_PRECISION (fmode) <= GET_MODE_PRECISION (tmode))
+ return;
+
+ if ((GET_MODE_CLASS (tmode) == MODE_FLOAT
+ && GET_MODE_CLASS (fmode) == MODE_FLOAT)
+ || (DECIMAL_FLOAT_MODE_P (fmode) && DECIMAL_FLOAT_MODE_P (tmode)))
+ gen_intraclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* Pick proper libcall for extend_optab. We need to chose if we do
+ truncation or extension and interclass or intraclass. */
+
+static void
+gen_extend_conv_libfunc (convert_optab tab,
+ const char *opname ATTRIBUTE_UNUSED,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (GET_MODE_CLASS (tmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (tmode))
+ return;
+ if (GET_MODE_CLASS (fmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (fmode))
+ return;
+ if (tmode == fmode)
+ return;
+
+ if ((GET_MODE_CLASS (tmode) == MODE_FLOAT && DECIMAL_FLOAT_MODE_P (fmode))
+ || (GET_MODE_CLASS (fmode) == MODE_FLOAT && DECIMAL_FLOAT_MODE_P (tmode)))
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+
+ if (GET_MODE_PRECISION (fmode) > GET_MODE_PRECISION (tmode))
+ return;
+
+ if ((GET_MODE_CLASS (tmode) == MODE_FLOAT
+ && GET_MODE_CLASS (fmode) == MODE_FLOAT)
+ || (DECIMAL_FLOAT_MODE_P (fmode) && DECIMAL_FLOAT_MODE_P (tmode)))
+ gen_intraclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* Pick proper libcall for fract_optab. We need to chose if we do
+ interclass or intraclass. */
+
+static void
+gen_fract_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (tmode == fmode)
+ return;
+ if (!(ALL_FIXED_POINT_MODE_P (tmode) || ALL_FIXED_POINT_MODE_P (fmode)))
+ return;
+
+ if (GET_MODE_CLASS (tmode) == GET_MODE_CLASS (fmode))
+ gen_intraclass_conv_libfunc (tab, opname, tmode, fmode);
+ else
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* Pick proper libcall for fractuns_optab. */
+
+static void
+gen_fractuns_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (tmode == fmode)
+ return;
+ /* One mode must be a fixed-point mode, and the other must be an integer
+ mode. */
+ if (!((ALL_FIXED_POINT_MODE_P (tmode) && GET_MODE_CLASS (fmode) == MODE_INT)
+ || (ALL_FIXED_POINT_MODE_P (fmode)
+ && GET_MODE_CLASS (tmode) == MODE_INT)))
+ return;
+
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* Pick proper libcall for satfract_optab. We need to chose if we do
+ interclass or intraclass. */
+
+static void
+gen_satfract_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (tmode == fmode)
+ return;
+ /* TMODE must be a fixed-point mode. */
+ if (!ALL_FIXED_POINT_MODE_P (tmode))
+ return;
+
+ if (GET_MODE_CLASS (tmode) == GET_MODE_CLASS (fmode))
+ gen_intraclass_conv_libfunc (tab, opname, tmode, fmode);
+ else
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* Pick proper libcall for satfractuns_optab. */
+
+static void
+gen_satfractuns_conv_libfunc (convert_optab tab,
+ const char *opname,
+ enum machine_mode tmode,
+ enum machine_mode fmode)
+{
+ if (tmode == fmode)
+ return;
+ /* TMODE must be a fixed-point mode, and FMODE must be an integer mode. */
+ if (!(ALL_FIXED_POINT_MODE_P (tmode) && GET_MODE_CLASS (fmode) == MODE_INT))
+ return;
+
+ gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
+}
+
+/* A table of previously-created libfuncs, hashed by name. */
+static GTY ((param_is (union tree_node))) htab_t libfunc_decls;
+
+/* Hashtable callbacks for libfunc_decls. */
+
+static hashval_t
+libfunc_decl_hash (const void *entry)
+{
+ return htab_hash_string (IDENTIFIER_POINTER (DECL_NAME ((const_tree) entry)));
+}
+
+static int
+libfunc_decl_eq (const void *entry1, const void *entry2)
+{
+ return DECL_NAME ((const_tree) entry1) == (const_tree) entry2;
+}
+
+rtx
+init_one_libfunc (const char *name)
+{
+ tree id, decl;
+ void **slot;
+ hashval_t hash;
+
+ if (libfunc_decls == NULL)
+ libfunc_decls = htab_create_ggc (37, libfunc_decl_hash,
+ libfunc_decl_eq, NULL);
+
+ /* See if we have already created a libfunc decl for this function. */
+ id = get_identifier (name);
+ hash = htab_hash_string (name);
+ slot = htab_find_slot_with_hash (libfunc_decls, id, hash, INSERT);
+ decl = (tree) *slot;
+ if (decl == NULL)
+ {
+ /* Create a new decl, so that it can be passed to
+ targetm.encode_section_info. */
+ /* ??? We don't have any type information except for this is
+ a function. Pretend this is "int foo()". */
+ decl = build_decl (FUNCTION_DECL, get_identifier (name),
+ build_function_type (integer_type_node, NULL_TREE));
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_EXTERNAL (decl) = 1;
+ TREE_PUBLIC (decl) = 1;
+
+ /* Zap the nonsensical SYMBOL_REF_DECL for this. What we're left with
+ are the flags assigned by targetm.encode_section_info. */
+ SET_SYMBOL_REF_DECL (XEXP (DECL_RTL (decl), 0), NULL);
+
+ *slot = decl;
+ }
+ return XEXP (DECL_RTL (decl), 0);
+}
+
+/* Adjust the assembler name of libfunc NAME to ASMSPEC. */
+
+rtx
+set_user_assembler_libfunc (const char *name, const char *asmspec)
+{
+ tree id, decl;
+ void **slot;
+ hashval_t hash;
+
+ id = get_identifier (name);
+ hash = htab_hash_string (name);
+ slot = htab_find_slot_with_hash (libfunc_decls, id, hash, NO_INSERT);
+ gcc_assert (slot);
+ decl = (tree) *slot;
+ set_user_assembler_name (decl, asmspec);
+ return XEXP (DECL_RTL (decl), 0);
+}
+
+/* Call this to reset the function entry for one optab (OPTABLE) in mode
+ MODE to NAME, which should be either 0 or a string constant. */
+void
+set_optab_libfunc (optab optable, enum machine_mode mode, const char *name)
+{
+ rtx val;
+ struct libfunc_entry e;
+ struct libfunc_entry **slot;
+ e.optab = (size_t) (optable - &optab_table[0]);
+ e.mode1 = mode;
+ e.mode2 = VOIDmode;
+
+ if (name)
+ val = init_one_libfunc (name);
+ else
+ val = 0;
+ slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, INSERT);
+ if (*slot == NULL)
+ *slot = GGC_NEW (struct libfunc_entry);
+ (*slot)->optab = (size_t) (optable - &optab_table[0]);
+ (*slot)->mode1 = mode;
+ (*slot)->mode2 = VOIDmode;
+ (*slot)->libfunc = val;
+}
+
+/* Call this to reset the function entry for one conversion optab
+ (OPTABLE) from mode FMODE to mode TMODE to NAME, which should be
+ either 0 or a string constant. */
+void
+set_conv_libfunc (convert_optab optable, enum machine_mode tmode,
+ enum machine_mode fmode, const char *name)
+{
+ rtx val;
+ struct libfunc_entry e;
+ struct libfunc_entry **slot;
+ e.optab = (size_t) (optable - &convert_optab_table[0]);
+ e.mode1 = tmode;
+ e.mode2 = fmode;
+
+ if (name)
+ val = init_one_libfunc (name);
+ else
+ val = 0;
+ slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, INSERT);
+ if (*slot == NULL)
+ *slot = GGC_NEW (struct libfunc_entry);
+ (*slot)->optab = (size_t) (optable - &convert_optab_table[0]);
+ (*slot)->mode1 = tmode;
+ (*slot)->mode2 = fmode;
+ (*slot)->libfunc = val;
+}
+
+/* Call this to initialize the contents of the optabs
+ appropriately for the current target machine. */
+
+void
+init_optabs (void)
+{
+ unsigned int i;
+ enum machine_mode int_mode;
+ static bool reinit;
+
+ libfunc_hash = htab_create_ggc (10, hash_libfunc, eq_libfunc, NULL);
+ /* Start by initializing all tables to contain CODE_FOR_nothing. */