| /* Mapping from optabs to underlying library functions |
| Copyright (C) 1987-2020 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "target.h" |
| #include "insn-codes.h" |
| #include "optabs-libfuncs.h" |
| #include "libfuncs.h" |
| #include "optabs-query.h" |
| #include "tree.h" |
| #include "stringpool.h" |
| #include "varasm.h" |
| #include "stor-layout.h" |
| #include "rtl.h" |
| |
| struct target_libfuncs default_target_libfuncs; |
| #if SWITCHABLE_TARGET |
| struct target_libfuncs *this_target_libfuncs = &default_target_libfuncs; |
| #endif |
| |
| #define libfunc_hash \ |
| (this_target_libfuncs->x_libfunc_hash) |
| |
| /* Prefixes for the current version of decimal floating point (BID vs. DPD) */ |
| #if ENABLE_DECIMAL_BID_FORMAT |
| #define DECIMAL_PREFIX "bid_" |
| #else |
| #define DECIMAL_PREFIX "dpd_" |
| #endif |
| |
| /* Used for libfunc_hash. */ |
| |
| hashval_t |
| libfunc_hasher::hash (libfunc_entry *e) |
| { |
| return ((e->mode1 + e->mode2 * NUM_MACHINE_MODES) ^ e->op); |
| } |
| |
| /* Used for libfunc_hash. */ |
| |
| bool |
| libfunc_hasher::equal (libfunc_entry *e1, libfunc_entry *e2) |
| { |
| return e1->op == e2->op && e1->mode1 == e2->mode1 && e1->mode2 == e2->mode2; |
| } |
| |
| /* Return libfunc corresponding operation defined by OPTAB converting |
| from MODE2 to MODE1. Trigger lazy initialization if needed, return NULL |
| if no libfunc is available. */ |
| rtx |
| convert_optab_libfunc (convert_optab optab, machine_mode mode1, |
| machine_mode mode2) |
| { |
| struct libfunc_entry e; |
| struct libfunc_entry **slot; |
| |
| /* ??? This ought to be an assert, but not all of the places |
| that we expand optabs know about the optabs that got moved |
| to being direct. */ |
| if (!(optab >= FIRST_CONV_OPTAB && optab <= LAST_CONVLIB_OPTAB)) |
| return NULL_RTX; |
| |
| e.op = optab; |
| e.mode1 = mode1; |
| e.mode2 = mode2; |
| slot = libfunc_hash->find_slot (&e, NO_INSERT); |
| if (!slot) |
| { |
| const struct convert_optab_libcall_d *d |
| = &convlib_def[optab - FIRST_CONV_OPTAB]; |
| |
| if (d->libcall_gen == NULL) |
| return NULL; |
| |
| d->libcall_gen (optab, d->libcall_basename, mode1, mode2); |
| slot = libfunc_hash->find_slot (&e, NO_INSERT); |
| if (!slot) |
| return NULL; |
| } |
| return (*slot)->libfunc; |
| } |
| |
| /* Return libfunc corresponding operation defined by OPTAB in MODE. |
| Trigger lazy initialization if needed, return NULL if no libfunc is |
| available. */ |
| rtx |
| optab_libfunc (optab optab, machine_mode mode) |
| { |
| struct libfunc_entry e; |
| struct libfunc_entry **slot; |
| |
| /* ??? This ought to be an assert, but not all of the places |
| that we expand optabs know about the optabs that got moved |
| to being direct. */ |
| if (!(optab >= FIRST_NORM_OPTAB && optab <= LAST_NORMLIB_OPTAB)) |
| return NULL_RTX; |
| |
| e.op = optab; |
| e.mode1 = mode; |
| e.mode2 = VOIDmode; |
| slot = libfunc_hash->find_slot (&e, NO_INSERT); |
| if (!slot) |
| { |
| const struct optab_libcall_d *d |
| = &normlib_def[optab - FIRST_NORM_OPTAB]; |
| |
| if (d->libcall_gen == NULL) |
| return NULL; |
| |
| d->libcall_gen (optab, d->libcall_basename, d->libcall_suffix, mode); |
| slot = libfunc_hash->find_slot (&e, NO_INSERT); |
| if (!slot) |
| return NULL; |
| } |
| return (*slot)->libfunc; |
| } |
| |
| /* Initialize the libfunc fields of an entire group of entries in some |
| optab. Each entry is set equal to a string consisting of a leading |
| pair of underscores followed by a generic operation name followed by |
| a mode name (downshifted to lowercase) followed by a single character |
| representing the number of operands for the given operation (which is |
| usually one of the characters '2', '3', or '4'). |
| |
| OPTABLE is the table in which libfunc fields are to be initialized. |
| OPNAME is the generic (string) name of the operation. |
| SUFFIX is the character which specifies the number of operands for |
| the given generic operation. |
| MODE is the mode to generate for. */ |
| |
| static void |
| gen_libfunc (optab optable, const char *opname, int suffix, |
| machine_mode mode) |
| { |
| unsigned opname_len = strlen (opname); |
| const char *mname = GET_MODE_NAME (mode); |
| unsigned mname_len = strlen (mname); |
| int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2; |
| int len = prefix_len + opname_len + mname_len + 1 + 1; |
| char *libfunc_name = XALLOCAVEC (char, len); |
| char *p; |
| const char *q; |
| |
| p = libfunc_name; |
| *p++ = '_'; |
| *p++ = '_'; |
| if (targetm.libfunc_gnu_prefix) |
| { |
| *p++ = 'g'; |
| *p++ = 'n'; |
| *p++ = 'u'; |
| *p++ = '_'; |
| } |
| for (q = opname; *q;) |
| *p++ = *q++; |
| for (q = mname; *q; q++) |
| *p++ = TOLOWER (*q); |
| *p++ = suffix; |
| *p = '\0'; |
| |
| set_optab_libfunc (optable, mode, |
| ggc_alloc_string (libfunc_name, p - libfunc_name)); |
| } |
| |
| /* Like gen_libfunc, but verify that integer operation is involved. */ |
| |
| void |
| gen_int_libfunc (optab optable, const char *opname, char suffix, |
| machine_mode mode) |
| { |
| int maxsize = 2 * BITS_PER_WORD; |
| int minsize = BITS_PER_WORD; |
| scalar_int_mode int_mode; |
| |
| if (!is_int_mode (mode, &int_mode)) |
| return; |
| if (maxsize < LONG_LONG_TYPE_SIZE) |
| maxsize = LONG_LONG_TYPE_SIZE; |
| if (minsize > INT_TYPE_SIZE |
| && (trapv_binoptab_p (optable) |
| || trapv_unoptab_p (optable))) |
| minsize = INT_TYPE_SIZE; |
| if (GET_MODE_BITSIZE (int_mode) < minsize |
| || GET_MODE_BITSIZE (int_mode) > maxsize) |
| return; |
| gen_libfunc (optable, opname, suffix, int_mode); |
| } |
| |
| /* Like gen_libfunc, but verify that FP and set decimal prefix if needed. */ |
| |
| void |
| gen_fp_libfunc (optab optable, const char *opname, char suffix, |
| machine_mode mode) |
| { |
| char *dec_opname; |
| |
| if (GET_MODE_CLASS (mode) == MODE_FLOAT) |
| gen_libfunc (optable, opname, suffix, mode); |
| if (DECIMAL_FLOAT_MODE_P (mode)) |
| { |
| dec_opname = XALLOCAVEC (char, sizeof (DECIMAL_PREFIX) + strlen (opname)); |
| /* For BID support, change the name to have either a bid_ or dpd_ prefix |
| depending on the low level floating format used. */ |
| memcpy (dec_opname, DECIMAL_PREFIX, sizeof (DECIMAL_PREFIX) - 1); |
| strcpy (dec_opname + sizeof (DECIMAL_PREFIX) - 1, opname); |
| gen_libfunc (optable, dec_opname, suffix, mode); |
| } |
| } |
| |
| /* Like gen_libfunc, but verify that fixed-point operation is involved. */ |
| |
| void |
| gen_fixed_libfunc (optab optable, const char *opname, char suffix, |
| machine_mode mode) |
| { |
| if (!ALL_FIXED_POINT_MODE_P (mode)) |
| return; |
| gen_libfunc (optable, opname, suffix, mode); |
| } |
| |
| /* Like gen_libfunc, but verify that signed fixed-point operation is |
| involved. */ |
| |
| void |
| gen_signed_fixed_libfunc (optab optable, const char *opname, char suffix, |
| machine_mode mode) |
| { |
| if (!SIGNED_FIXED_POINT_MODE_P (mode)) |
| return; |
| gen_libfunc (optable, opname, suffix, mode); |
| } |
| |
| /* Like gen_libfunc, but verify that unsigned fixed-point operation is |
| involved. */ |
| |
| void |
| gen_unsigned_fixed_libfunc (optab optable, const char *opname, char suffix, |
| machine_mode mode) |
| { |
| if (!UNSIGNED_FIXED_POINT_MODE_P (mode)) |
| return; |
| gen_libfunc (optable, opname, suffix, mode); |
| } |
| |
| /* Like gen_libfunc, but verify that FP or INT operation is involved. */ |
| |
| void |
| gen_int_fp_libfunc (optab optable, const char *name, char suffix, |
| 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); |
| } |
| |
| /* Like gen_libfunc, but verify that FP or INT operation is involved |
| and add 'v' suffix for integer operation. */ |
| |
| void |
| gen_intv_fp_libfunc (optab optable, const char *name, char suffix, |
| 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. */ |
| |
| void |
| gen_int_fp_fixed_libfunc (optab optable, const char *name, char suffix, |
| 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. */ |
| |
| void |
| gen_int_fp_signed_fixed_libfunc (optab optable, const char *name, char suffix, |
| 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. */ |
| |
| void |
| gen_int_fixed_libfunc (optab optable, const char *name, char suffix, |
| 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. */ |
| |
| void |
| gen_int_signed_fixed_libfunc (optab optable, const char *name, char suffix, |
| 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. */ |
| |
| void |
| gen_int_unsigned_fixed_libfunc (optab optable, const char *name, char suffix, |
| 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. */ |
| |
| void |
| gen_interclass_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| machine_mode fmode) |
| { |
| size_t opname_len = strlen (opname); |
| size_t mname_len = 0; |
| |
| const char *fname, *tname; |
| const char *q; |
| int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2; |
| 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, prefix_len + opname_len + mname_len + 1 + 1); |
| nondec_name[0] = '_'; |
| nondec_name[1] = '_'; |
| if (targetm.libfunc_gnu_prefix) |
| { |
| nondec_name[2] = 'g'; |
| nondec_name[3] = 'n'; |
| nondec_name[4] = 'u'; |
| nondec_name[5] = '_'; |
| } |
| |
| memcpy (&nondec_name[prefix_len], opname, opname_len); |
| nondec_suffix = nondec_name + opname_len + prefix_len; |
| |
| 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. */ |
| |
| void |
| gen_int_to_fp_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| 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. */ |
| |
| void |
| gen_ufloat_conv_libfunc (convert_optab tab, |
| const char *opname ATTRIBUTE_UNUSED, |
| machine_mode tmode, |
| 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. */ |
| |
| void |
| gen_int_to_fp_nondecimal_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| 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. */ |
| |
| void |
| gen_fp_to_int_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| 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. */ |
| |
| void |
| gen_intraclass_conv_libfunc (convert_optab tab, const char *opname, |
| machine_mode tmode, machine_mode fmode) |
| { |
| size_t opname_len = strlen (opname); |
| size_t mname_len = 0; |
| |
| const char *fname, *tname; |
| const char *q; |
| int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2; |
| 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] = '_'; |
| if (targetm.libfunc_gnu_prefix) |
| { |
| nondec_name[2] = 'g'; |
| nondec_name[3] = 'n'; |
| nondec_name[4] = 'u'; |
| nondec_name[5] = '_'; |
| } |
| memcpy (&nondec_name[prefix_len], opname, opname_len); |
| nondec_suffix = nondec_name + opname_len + prefix_len; |
| |
| 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. */ |
| |
| void |
| gen_trunc_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| machine_mode fmode) |
| { |
| scalar_float_mode float_tmode, float_fmode; |
| if (!is_a <scalar_float_mode> (fmode, &float_fmode) |
| || !is_a <scalar_float_mode> (tmode, &float_tmode) |
| || float_tmode == float_fmode) |
| return; |
| |
| if (GET_MODE_CLASS (float_tmode) != GET_MODE_CLASS (float_fmode)) |
| gen_interclass_conv_libfunc (tab, opname, float_tmode, float_fmode); |
| |
| if (GET_MODE_PRECISION (float_fmode) <= GET_MODE_PRECISION (float_tmode)) |
| return; |
| |
| if (GET_MODE_CLASS (float_tmode) == GET_MODE_CLASS (float_fmode)) |
| gen_intraclass_conv_libfunc (tab, opname, float_tmode, float_fmode); |
| } |
| |
| /* Pick proper libcall for extend_optab. We need to chose if we do |
| truncation or extension and interclass or intraclass. */ |
| |
| void |
| gen_extend_conv_libfunc (convert_optab tab, |
| const char *opname ATTRIBUTE_UNUSED, |
| machine_mode tmode, |
| machine_mode fmode) |
| { |
| scalar_float_mode float_tmode, float_fmode; |
| if (!is_a <scalar_float_mode> (fmode, &float_fmode) |
| || !is_a <scalar_float_mode> (tmode, &float_tmode) |
| || float_tmode == float_fmode) |
| return; |
| |
| if (GET_MODE_CLASS (float_tmode) != GET_MODE_CLASS (float_fmode)) |
| gen_interclass_conv_libfunc (tab, opname, float_tmode, float_fmode); |
| |
| if (GET_MODE_PRECISION (float_fmode) > GET_MODE_PRECISION (float_tmode)) |
| return; |
| |
| if (GET_MODE_CLASS (float_tmode) == GET_MODE_CLASS (float_fmode)) |
| gen_intraclass_conv_libfunc (tab, opname, tmode, fmode); |
| } |
| |
| /* Pick proper libcall for fract_optab. We need to chose if we do |
| interclass or intraclass. */ |
| |
| void |
| gen_fract_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| 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. */ |
| |
| void |
| gen_fractuns_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| 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. */ |
| |
| void |
| gen_satfract_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| 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. */ |
| |
| void |
| gen_satfractuns_conv_libfunc (convert_optab tab, |
| const char *opname, |
| machine_mode tmode, |
| 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); |
| } |
| |
| /* Hashtable callbacks for libfunc_decls. */ |
| |
| struct libfunc_decl_hasher : ggc_ptr_hash<tree_node> |
| { |
| static hashval_t |
| hash (tree entry) |
| { |
| return IDENTIFIER_HASH_VALUE (DECL_NAME (entry)); |
| } |
| |
| static bool |
| equal (tree decl, tree name) |
| { |
| return DECL_NAME (decl) == name; |
| } |
| }; |
| |
| /* A table of previously-created libfuncs, hashed by name. */ |
| static GTY (()) hash_table<libfunc_decl_hasher> *libfunc_decls; |
| |
| /* Build a decl for a libfunc named NAME with visibility VIS. */ |
| |
| tree |
| build_libfunc_function_visibility (const char *name, symbol_visibility vis) |
| { |
| /* ??? We don't have any type information; pretend this is "int foo ()". */ |
| tree decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, |
| get_identifier (name), |
| build_function_type (integer_type_node, NULL_TREE)); |
| DECL_EXTERNAL (decl) = 1; |
| TREE_PUBLIC (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| DECL_VISIBILITY (decl) = vis; |
| DECL_VISIBILITY_SPECIFIED (decl) = 1; |
| gcc_assert (DECL_ASSEMBLER_NAME (decl)); |
| |
| return decl; |
| } |
| |
| /* Build a decl for a libfunc named NAME. */ |
| |
| tree |
| build_libfunc_function (const char *name) |
| { |
| return build_libfunc_function_visibility (name, VISIBILITY_DEFAULT); |
| } |
| |
| /* Return a libfunc for NAME, creating one if we don't already have one. |
| The decl is given visibility VIS. The returned rtx is a SYMBOL_REF. */ |
| |
| rtx |
| init_one_libfunc_visibility (const char *name, symbol_visibility vis) |
| { |
| tree id, decl; |
| hashval_t hash; |
| |
| if (libfunc_decls == NULL) |
| libfunc_decls = hash_table<libfunc_decl_hasher>::create_ggc (37); |
| |
| /* See if we have already created a libfunc decl for this function. */ |
| id = get_identifier (name); |
| hash = IDENTIFIER_HASH_VALUE (id); |
| tree *slot = libfunc_decls->find_slot_with_hash (id, hash, INSERT); |
| decl = *slot; |
| if (decl == NULL) |
| { |
| /* Create a new decl, so that it can be passed to |
| targetm.encode_section_info. */ |
| decl = build_libfunc_function_visibility (name, vis); |
| *slot = decl; |
| } |
| return XEXP (DECL_RTL (decl), 0); |
| } |
| |
| rtx |
| init_one_libfunc (const char *name) |
| { |
| return init_one_libfunc_visibility (name, VISIBILITY_DEFAULT); |
| } |
| |
| /* Adjust the assembler name of libfunc NAME to ASMSPEC. */ |
| |
| rtx |
| set_user_assembler_libfunc (const char *name, const char *asmspec) |
| { |
| tree id, decl; |
| hashval_t hash; |
| |
| id = get_identifier (name); |
| hash = IDENTIFIER_HASH_VALUE (id); |
| tree *slot = libfunc_decls->find_slot_with_hash (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 op, machine_mode mode, const char *name) |
| { |
| rtx val; |
| struct libfunc_entry e; |
| struct libfunc_entry **slot; |
| |
| e.op = op; |
| e.mode1 = mode; |
| e.mode2 = VOIDmode; |
| |
| if (name) |
| val = init_one_libfunc (name); |
| else |
| val = 0; |
| slot = libfunc_hash->find_slot (&e, INSERT); |
| if (*slot == NULL) |
| *slot = ggc_alloc<libfunc_entry> (); |
| (*slot)->op = op; |
| (*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 optab, machine_mode tmode, |
| machine_mode fmode, const char *name) |
| { |
| rtx val; |
| struct libfunc_entry e; |
| struct libfunc_entry **slot; |
| |
| e.op = optab; |
| e.mode1 = tmode; |
| e.mode2 = fmode; |
| |
| if (name) |
| val = init_one_libfunc (name); |
| else |
| val = 0; |
| slot = libfunc_hash->find_slot (&e, INSERT); |
| if (*slot == NULL) |
| *slot = ggc_alloc<libfunc_entry> (); |
| (*slot)->op = optab; |
| (*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) |
| { |
| if (libfunc_hash) |
| libfunc_hash->empty (); |
| else |
| libfunc_hash = hash_table<libfunc_hasher>::create_ggc (10); |
| |
| /* Fill in the optabs with the insns we support. */ |
| init_all_optabs (this_fn_optabs); |
| |
| /* The ffs function operates on `int'. Fall back on it if we do not |
| have a libgcc2 function for that width. */ |
| if (INT_TYPE_SIZE < BITS_PER_WORD) |
| { |
| scalar_int_mode mode = int_mode_for_size (INT_TYPE_SIZE, 0).require (); |
| set_optab_libfunc (ffs_optab, mode, "ffs"); |
| } |
| |
| /* Explicitly initialize the bswap libfuncs since we need them to be |
| valid for things other than word_mode. */ |
| if (targetm.libfunc_gnu_prefix) |
| { |
| set_optab_libfunc (bswap_optab, SImode, "__gnu_bswapsi2"); |
| set_optab_libfunc (bswap_optab, DImode, "__gnu_bswapdi2"); |
| } |
| else |
| { |
| set_optab_libfunc (bswap_optab, SImode, "__bswapsi2"); |
| set_optab_libfunc (bswap_optab, DImode, "__bswapdi2"); |
| } |
| |
| /* Use cabs for double complex abs, since systems generally have cabs. |
| Don't define any libcall for float complex, so that cabs will be used. */ |
| if (complex_double_type_node) |
| set_optab_libfunc (abs_optab, TYPE_MODE (complex_double_type_node), |
| "cabs"); |
| |
| unwind_sjlj_register_libfunc = init_one_libfunc ("_Unwind_SjLj_Register"); |
| unwind_sjlj_unregister_libfunc |
| = init_one_libfunc ("_Unwind_SjLj_Unregister"); |
| |
| /* Allow the target to add more libcalls or rename some, etc. */ |
| targetm.init_libfuncs (); |
| } |
| |
| /* A helper function for init_sync_libfuncs. Using the basename BASE, |
| install libfuncs into TAB for BASE_N for 1 <= N <= MAX. */ |
| |
| static void |
| init_sync_libfuncs_1 (optab tab, const char *base, int max) |
| { |
| machine_mode mode; |
| char buf[64]; |
| size_t len = strlen (base); |
| int i; |
| |
| gcc_assert (max <= 8); |
| gcc_assert (len + 3 < sizeof (buf)); |
| |
| memcpy (buf, base, len); |
| buf[len] = '_'; |
| buf[len + 1] = '0'; |
| buf[len + 2] = '\0'; |
| |
| mode = QImode; |
| for (i = 1; i <= max; i *= 2) |
| { |
| if (i > 1) |
| mode = GET_MODE_2XWIDER_MODE (mode).require (); |
| buf[len + 1] = '0' + i; |
| set_optab_libfunc (tab, mode, buf); |
| } |
| } |
| |
| void |
| init_sync_libfuncs (int max) |
| { |
| if (!flag_sync_libcalls) |
| return; |
| |
| init_sync_libfuncs_1 (sync_compare_and_swap_optab, |
| "__sync_val_compare_and_swap", max); |
| init_sync_libfuncs_1 (sync_lock_test_and_set_optab, |
| "__sync_lock_test_and_set", max); |
| |
| init_sync_libfuncs_1 (sync_old_add_optab, "__sync_fetch_and_add", max); |
| init_sync_libfuncs_1 (sync_old_sub_optab, "__sync_fetch_and_sub", max); |
| init_sync_libfuncs_1 (sync_old_ior_optab, "__sync_fetch_and_or", max); |
| init_sync_libfuncs_1 (sync_old_and_optab, "__sync_fetch_and_and", max); |
| init_sync_libfuncs_1 (sync_old_xor_optab, "__sync_fetch_and_xor", max); |
| init_sync_libfuncs_1 (sync_old_nand_optab, "__sync_fetch_and_nand", max); |
| |
| init_sync_libfuncs_1 (sync_new_add_optab, "__sync_add_and_fetch", max); |
| init_sync_libfuncs_1 (sync_new_sub_optab, "__sync_sub_and_fetch", max); |
| init_sync_libfuncs_1 (sync_new_ior_optab, "__sync_or_and_fetch", max); |
| init_sync_libfuncs_1 (sync_new_and_optab, "__sync_and_and_fetch", max); |
| init_sync_libfuncs_1 (sync_new_xor_optab, "__sync_xor_and_fetch", max); |
| init_sync_libfuncs_1 (sync_new_nand_optab, "__sync_nand_and_fetch", max); |
| } |
| |
| #include "gt-optabs-libfuncs.h" |