blob: d08bdfec3aee41a8f7352c8c9f848e22d06a5d63 [file] [log] [blame]
/* Subroutines for the C front end on the PowerPC architecture.
Copyright (C) 2002-2021 Free Software Foundation, Inc.
Contributed by Zack Weinberg <zack@codesourcery.com>
and Paolo Bonzini <bonzini@gnu.org>
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/>. */
#define IN_TARGET_CODE 1
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "c-family/c-common.h"
#include "memmodel.h"
#include "tm_p.h"
#include "stringpool.h"
#include "stor-layout.h"
#include "c-family/c-pragma.h"
#include "langhooks.h"
#include "c/c-tree.h"
#include "rs6000-builtins.h"
static tree altivec_resolve_new_overloaded_builtin (location_t, tree, void *);
/* Handle the machine specific pragma longcall. Its syntax is
# pragma longcall ( TOGGLE )
where TOGGLE is either 0 or 1.
rs6000_default_long_calls is set to the value of TOGGLE, changing
whether or not new function declarations receive a longcall
attribute by default. */
void
rs6000_pragma_longcall (cpp_reader *pfile ATTRIBUTE_UNUSED)
{
#define SYNTAX_ERROR(gmsgid) do { \
warning (OPT_Wpragmas, gmsgid); \
warning (OPT_Wpragmas, "ignoring malformed %<#pragma longcall%>"); \
return; \
} while (0)
tree x, n;
/* If we get here, generic code has already scanned the directive
leader and the word "longcall". */
if (pragma_lex (&x) != CPP_OPEN_PAREN)
SYNTAX_ERROR ("missing open paren");
if (pragma_lex (&n) != CPP_NUMBER)
SYNTAX_ERROR ("missing number");
if (pragma_lex (&x) != CPP_CLOSE_PAREN)
SYNTAX_ERROR ("missing close paren");
if (n != integer_zero_node && n != integer_one_node)
SYNTAX_ERROR ("number must be 0 or 1");
if (pragma_lex (&x) != CPP_EOF)
warning (OPT_Wpragmas, "junk at end of %<#pragma longcall%>");
rs6000_default_long_calls = (n == integer_one_node);
}
/* Handle defining many CPP flags based on TARGET_xxx. As a general
policy, rather than trying to guess what flags a user might want a
#define for, it's better to define a flag for everything. */
#define builtin_define(TXT) cpp_define (pfile, TXT)
#define builtin_assert(TXT) cpp_assert (pfile, TXT)
/* Keep the AltiVec keywords handy for fast comparisons. */
static GTY(()) tree __vector_keyword;
static GTY(()) tree vector_keyword;
static GTY(()) tree __pixel_keyword;
static GTY(()) tree pixel_keyword;
static GTY(()) tree __bool_keyword;
static GTY(()) tree bool_keyword;
static GTY(()) tree _Bool_keyword;
static GTY(()) tree __int128_type;
static GTY(()) tree __uint128_type;
/* Preserved across calls. */
static tree expand_bool_pixel;
static cpp_hashnode *
altivec_categorize_keyword (const cpp_token *tok)
{
if (tok->type == CPP_NAME)
{
cpp_hashnode *ident = tok->val.node.node;
if (ident == C_CPP_HASHNODE (vector_keyword))
return C_CPP_HASHNODE (__vector_keyword);
if (ident == C_CPP_HASHNODE (pixel_keyword))
return C_CPP_HASHNODE (__pixel_keyword);
if (ident == C_CPP_HASHNODE (bool_keyword))
return C_CPP_HASHNODE (__bool_keyword);
if (ident == C_CPP_HASHNODE (_Bool_keyword))
return C_CPP_HASHNODE (__bool_keyword);
return ident;
}
return 0;
}
static void
init_vector_keywords (void)
{
/* Keywords without two leading underscores are context-sensitive, and hence
implemented as conditional macros, controlled by the
rs6000_macro_to_expand() function below. If we have ISA 2.07 64-bit
support, record the __int128_t and __uint128_t types. */
__vector_keyword = get_identifier ("__vector");
C_CPP_HASHNODE (__vector_keyword)->flags |= NODE_CONDITIONAL;
__pixel_keyword = get_identifier ("__pixel");
C_CPP_HASHNODE (__pixel_keyword)->flags |= NODE_CONDITIONAL;
__bool_keyword = get_identifier ("__bool");
C_CPP_HASHNODE (__bool_keyword)->flags |= NODE_CONDITIONAL;
vector_keyword = get_identifier ("vector");
C_CPP_HASHNODE (vector_keyword)->flags |= NODE_CONDITIONAL;
pixel_keyword = get_identifier ("pixel");
C_CPP_HASHNODE (pixel_keyword)->flags |= NODE_CONDITIONAL;
bool_keyword = get_identifier ("bool");
C_CPP_HASHNODE (bool_keyword)->flags |= NODE_CONDITIONAL;
_Bool_keyword = get_identifier ("_Bool");
C_CPP_HASHNODE (_Bool_keyword)->flags |= NODE_CONDITIONAL;
if (TARGET_VADDUQM)
{
__int128_type = get_identifier ("__int128_t");
__uint128_type = get_identifier ("__uint128_t");
}
}
/* Helper function to find out which RID_INT_N_* code is the one for
__int128, if any. Returns RID_MAX+1 if none apply, which is safe
(for our purposes, since we always expect to have __int128) to
compare against. */
static int
rid_int128(void)
{
int i;
for (i = 0; i < NUM_INT_N_ENTS; i ++)
if (int_n_enabled_p[i]
&& int_n_data[i].bitsize == 128)
return RID_INT_N_0 + i;
return RID_MAX + 1;
}
/* Called to decide whether a conditional macro should be expanded.
Since we have exactly one such macro (i.e, 'vector'), we do not
need to examine the 'tok' parameter. */
static cpp_hashnode *
rs6000_macro_to_expand (cpp_reader *pfile, const cpp_token *tok)
{
cpp_hashnode *expand_this = tok->val.node.node;
cpp_hashnode *ident;
/* If the current machine does not have altivec, don't look for the
keywords. */
if (!TARGET_ALTIVEC)
return NULL;
ident = altivec_categorize_keyword (tok);
if (ident != expand_this)
expand_this = NULL;
if (ident == C_CPP_HASHNODE (__vector_keyword))
{
int idx = 0;
do
tok = cpp_peek_token (pfile, idx++);
while (tok->type == CPP_PADDING);
ident = altivec_categorize_keyword (tok);
if (ident == C_CPP_HASHNODE (__pixel_keyword))
{
expand_this = C_CPP_HASHNODE (__vector_keyword);
expand_bool_pixel = __pixel_keyword;
}
else if (ident == C_CPP_HASHNODE (__bool_keyword))
{
expand_this = C_CPP_HASHNODE (__vector_keyword);
expand_bool_pixel = __bool_keyword;
}
/* The boost libraries have code with Iterator::vector vector in it. If
we allow the normal handling, this module will be called recursively,
and the vector will be skipped.; */
else if (ident && (ident != C_CPP_HASHNODE (__vector_keyword)))
{
enum rid rid_code = (enum rid)(ident->rid_code);
bool is_macro = cpp_macro_p (ident);
/* If there is a function-like macro, check if it is going to be
invoked with or without arguments. Without following ( treat
it like non-macro, otherwise the following cpp_get_token eats
what should be preserved. */
if (is_macro && cpp_fun_like_macro_p (ident))
{
int idx2 = idx;
do
tok = cpp_peek_token (pfile, idx2++);
while (tok->type == CPP_PADDING);
if (tok->type != CPP_OPEN_PAREN)
is_macro = false;
}
if (is_macro)
{
do
(void) cpp_get_token (pfile);
while (--idx > 0);
do
tok = cpp_peek_token (pfile, idx++);
while (tok->type == CPP_PADDING);
ident = altivec_categorize_keyword (tok);
if (ident == C_CPP_HASHNODE (__pixel_keyword))
{
expand_this = C_CPP_HASHNODE (__vector_keyword);
expand_bool_pixel = __pixel_keyword;
rid_code = RID_MAX;
}
else if (ident == C_CPP_HASHNODE (__bool_keyword))
{
expand_this = C_CPP_HASHNODE (__vector_keyword);
expand_bool_pixel = __bool_keyword;
rid_code = RID_MAX;
}
else if (ident)
rid_code = (enum rid)(ident->rid_code);
}
if (rid_code == RID_UNSIGNED || rid_code == RID_LONG
|| rid_code == RID_SHORT || rid_code == RID_SIGNED
|| rid_code == RID_INT || rid_code == RID_CHAR
|| rid_code == RID_FLOAT
|| (rid_code == RID_DOUBLE && TARGET_VSX)
|| (rid_code == rid_int128 () && TARGET_VADDUQM))
{
expand_this = C_CPP_HASHNODE (__vector_keyword);
/* If the next keyword is bool or pixel, it
will need to be expanded as well. */
do
tok = cpp_peek_token (pfile, idx++);
while (tok->type == CPP_PADDING);
ident = altivec_categorize_keyword (tok);
if (ident == C_CPP_HASHNODE (__pixel_keyword))
expand_bool_pixel = __pixel_keyword;
else if (ident == C_CPP_HASHNODE (__bool_keyword))
expand_bool_pixel = __bool_keyword;
else
{
/* Try two tokens down, too. */
do
tok = cpp_peek_token (pfile, idx++);
while (tok->type == CPP_PADDING);
ident = altivec_categorize_keyword (tok);
if (ident == C_CPP_HASHNODE (__pixel_keyword))
expand_bool_pixel = __pixel_keyword;
else if (ident == C_CPP_HASHNODE (__bool_keyword))
expand_bool_pixel = __bool_keyword;
}
}
/* Support vector __int128_t, but we don't need to worry about bool
or pixel on this type. */
else if (TARGET_VADDUQM
&& (ident == C_CPP_HASHNODE (__int128_type)
|| ident == C_CPP_HASHNODE (__uint128_type)))
expand_this = C_CPP_HASHNODE (__vector_keyword);
}
}
else if (expand_bool_pixel && ident == C_CPP_HASHNODE (__pixel_keyword))
{
expand_this = C_CPP_HASHNODE (__pixel_keyword);
expand_bool_pixel = 0;
}
else if (expand_bool_pixel && ident == C_CPP_HASHNODE (__bool_keyword))
{
expand_this = C_CPP_HASHNODE (__bool_keyword);
expand_bool_pixel = 0;
}
return expand_this;
}
/* Define or undefine a single macro. */
static void
rs6000_define_or_undefine_macro (bool define_p, const char *name)
{
if (TARGET_DEBUG_BUILTIN || TARGET_DEBUG_TARGET)
fprintf (stderr, "#%s %s\n", (define_p) ? "define" : "undef", name);
if (define_p)
cpp_define (parse_in, name);
else
cpp_undef (parse_in, name);
}
/* Define or undefine macros based on the current target. If the user does
#pragma GCC target, we need to adjust the macros dynamically. Note, some of
the options needed for builtins have been moved to separate variables, so
have both the target flags and the builtin flags as arguments. */
void
rs6000_target_modify_macros (bool define_p, HOST_WIDE_INT flags,
HOST_WIDE_INT bu_mask)
{
if (TARGET_DEBUG_BUILTIN || TARGET_DEBUG_TARGET)
fprintf (stderr,
"rs6000_target_modify_macros (%s, " HOST_WIDE_INT_PRINT_HEX
", " HOST_WIDE_INT_PRINT_HEX ")\n",
(define_p) ? "define" : "undef",
flags, bu_mask);
/* Each of the flags mentioned below controls whether certain
preprocessor macros will be automatically defined when
preprocessing source files for compilation by this compiler.
While most of these flags can be enabled or disabled
explicitly by specifying certain command-line options when
invoking the compiler, there are also many ways in which these
flags are enabled or disabled implicitly, based on compiler
defaults, configuration choices, and on the presence of certain
related command-line options. Many, but not all, of these
implicit behaviors can be found in file "rs6000.c", the
rs6000_option_override_internal() function.
In general, each of the flags may be automatically enabled in
any of the following conditions:
1. If no -mcpu target is specified on the command line and no
--with-cpu target is specified to the configure command line
and the TARGET_DEFAULT macro for this default cpu host
includes the flag, and the flag has not been explicitly disabled
by command-line options.
2. If the target specified with -mcpu=target on the command line, or
in the absence of a -mcpu=target command-line option, if the
target specified using --with-cpu=target on the configure
command line, is disqualified because the associated binary
tools (e.g. the assembler) lack support for the requested cpu,
and the TARGET_DEFAULT macro for this default cpu host
includes the flag, and the flag has not been explicitly disabled
by command-line options.
3. If either of the above two conditions apply except that the
TARGET_DEFAULT macro is defined to equal zero, and
TARGET_POWERPC64 and
a) BYTES_BIG_ENDIAN and the flag to be enabled is either
MASK_PPC_GFXOPT or MASK_POWERPC64 (flags for "powerpc64"
target), or
b) !BYTES_BIG_ENDIAN and the flag to be enabled is either
MASK_POWERPC64 or it is one of the flags included in
ISA_2_7_MASKS_SERVER (flags for "powerpc64le" target).
4. If a cpu has been requested with a -mcpu=target command-line option
and this cpu has not been disqualified due to shortcomings of the
binary tools, and the set of flags associated with the requested cpu
include the flag to be enabled. See rs6000-cpus.def for macro
definitions that represent various ABI standards
(e.g. ISA_2_1_MASKS, ISA_3_0_MASKS_SERVER) and for a list of
the specific flags that are associated with each of the cpu
choices that can be specified as the target of a -mcpu=target
compile option, or as the target of a --with-cpu=target
configure option. Target flags that are specified in either
of these two ways are considered "implicit" since the flags
are not mentioned specifically by name.
Additional documentation describing behavior specific to
particular flags is provided below, immediately preceding the
use of each relevant flag.
5. If there is no -mcpu=target command-line option, and the cpu
requested by a --with-cpu=target command-line option has not
been disqualified due to shortcomings of the binary tools, and
the set of flags associated with the specified target include
the flag to be enabled. See the notes immediately above for a
summary of the flags associated with particular cpu
definitions. */
/* rs6000_isa_flags based options. */
rs6000_define_or_undefine_macro (define_p, "_ARCH_PPC");
if ((flags & OPTION_MASK_PPC_GPOPT) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PPCSQ");
if ((flags & OPTION_MASK_PPC_GFXOPT) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PPCGR");
if ((flags & OPTION_MASK_POWERPC64) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PPC64");
if ((flags & OPTION_MASK_MFCRF) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PWR4");
if ((flags & OPTION_MASK_POPCNTB) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PWR5");
if ((flags & OPTION_MASK_FPRND) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PWR5X");
if ((flags & OPTION_MASK_CMPB) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PWR6");
if ((flags & OPTION_MASK_POPCNTD) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PWR7");
/* Note that the OPTION_MASK_DIRECT_MOVE flag is automatically
turned on in the following condition:
1. TARGET_P8_VECTOR is enabled and OPTION_MASK_DIRECT_MOVE is not
explicitly disabled.
Hereafter, the OPTION_MASK_DIRECT_MOVE flag is considered to
have been turned on explicitly.
Note that the OPTION_MASK_DIRECT_MOVE flag is automatically
turned off in any of the following conditions:
1. TARGET_HARD_FLOAT, TARGET_ALTIVEC, or TARGET_VSX is explicitly
disabled and OPTION_MASK_DIRECT_MOVE was not explicitly
enabled.
2. TARGET_VSX is off. */
if ((flags & OPTION_MASK_DIRECT_MOVE) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PWR8");
if ((flags & OPTION_MASK_MODULO) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PWR9");
if ((flags & OPTION_MASK_POWER10) != 0)
rs6000_define_or_undefine_macro (define_p, "_ARCH_PWR10");
if ((flags & OPTION_MASK_SOFT_FLOAT) != 0)
rs6000_define_or_undefine_macro (define_p, "_SOFT_FLOAT");
if ((flags & OPTION_MASK_RECIP_PRECISION) != 0)
rs6000_define_or_undefine_macro (define_p, "__RECIP_PRECISION__");
/* Note that the OPTION_MASK_ALTIVEC flag is automatically turned on
in any of the following conditions:
1. The operating system is Darwin and it is configured for 64
bit. (See darwin_rs6000_override_options.)
2. The operating system is Darwin and the operating system
version is 10.5 or higher and the user has not explicitly
disabled ALTIVEC by specifying -mcpu=G3 or -mno-altivec and
the compiler is not producing code for integration within the
kernel. (See darwin_rs6000_override_options.)
Note that the OPTION_MASK_ALTIVEC flag is automatically turned
off in any of the following conditions:
1. The operating system does not support saving of AltiVec
registers (OS_MISSING_ALTIVEC).
2. If an inner context (as introduced by
__attribute__((__target__())) or #pragma GCC target()
requests a target that normally enables the
OPTION_MASK_ALTIVEC flag but the outer-most "main target"
does not support the rs6000_altivec_abi, this flag is
turned off for the inner context unless OPTION_MASK_ALTIVEC
was explicitly enabled for the inner context. */
if ((flags & OPTION_MASK_ALTIVEC) != 0)
{
const char *vec_str = (define_p) ? "__VEC__=10206" : "__VEC__";
rs6000_define_or_undefine_macro (define_p, "__ALTIVEC__");
rs6000_define_or_undefine_macro (define_p, vec_str);
/* Define this when supporting context-sensitive keywords. */
if (!flag_iso)
rs6000_define_or_undefine_macro (define_p, "__APPLE_ALTIVEC__");
if (rs6000_aix_extabi)
rs6000_define_or_undefine_macro (define_p, "__EXTABI__");
}
/* Note that the OPTION_MASK_VSX flag is automatically turned on in
the following conditions:
1. TARGET_P8_VECTOR is explicitly turned on and the OPTION_MASK_VSX
was not explicitly turned off. Hereafter, the OPTION_MASK_VSX
flag is considered to have been explicitly turned on.
Note that the OPTION_MASK_VSX flag is automatically turned off in
the following conditions:
1. The operating system does not support saving of AltiVec
registers (OS_MISSING_ALTIVEC).
2. If the option TARGET_HARD_FLOAT is turned off. Hereafter, the
OPTION_MASK_VSX flag is considered to have been turned off
explicitly.
3. If TARGET_AVOID_XFORM is turned on explicitly at the outermost
compilation context, or if it is turned on by any means in an
inner compilation context. Hereafter, the OPTION_MASK_VSX
flag is considered to have been turned off explicitly.
4. If TARGET_ALTIVEC was explicitly disabled. Hereafter, the
OPTION_MASK_VSX flag is considered to have been turned off
explicitly.
5. If an inner context (as introduced by
__attribute__((__target__())) or #pragma GCC target()
requests a target that normally enables the
OPTION_MASK_VSX flag but the outer-most "main target"
does not support the rs6000_altivec_abi, this flag is
turned off for the inner context unless OPTION_MASK_VSX
was explicitly enabled for the inner context. */
if ((flags & OPTION_MASK_VSX) != 0)
rs6000_define_or_undefine_macro (define_p, "__VSX__");
if ((flags & OPTION_MASK_HTM) != 0)
{
rs6000_define_or_undefine_macro (define_p, "__HTM__");
/* Tell the user that our HTM insn patterns act as memory barriers. */
rs6000_define_or_undefine_macro (define_p, "__TM_FENCE__");
}
/* Note that the OPTION_MASK_P8_VECTOR flag is automatically turned
on in the following conditions:
1. TARGET_P9_VECTOR is explicitly turned on and
OPTION_MASK_P8_VECTOR is not explicitly turned off.
Hereafter, the OPTION_MASK_P8_VECTOR flag is considered to
have been turned off explicitly.
Note that the OPTION_MASK_P8_VECTOR flag is automatically turned
off in the following conditions:
1. If any of TARGET_HARD_FLOAT, TARGET_ALTIVEC, or TARGET_VSX
were turned off explicitly and OPTION_MASK_P8_VECTOR flag was
not turned on explicitly.
2. If TARGET_ALTIVEC is turned off. Hereafter, the
OPTION_MASK_P8_VECTOR flag is considered to have been turned off
explicitly.
3. If TARGET_VSX is turned off and OPTION_MASK_P8_VECTOR was not
explicitly enabled. If TARGET_VSX is explicitly enabled, the
OPTION_MASK_P8_VECTOR flag is hereafter also considered to
have been turned off explicitly. */
if ((flags & OPTION_MASK_P8_VECTOR) != 0)
rs6000_define_or_undefine_macro (define_p, "__POWER8_VECTOR__");
/* Note that the OPTION_MASK_P9_VECTOR flag is automatically turned
off in the following conditions:
1. If TARGET_P8_VECTOR is turned off and OPTION_MASK_P9_VECTOR is
not turned on explicitly. Hereafter, if OPTION_MASK_P8_VECTOR
was turned on explicitly, the OPTION_MASK_P9_VECTOR flag is
also considered to have been turned off explicitly.
Note that the OPTION_MASK_P9_VECTOR is automatically turned on
in the following conditions:
1. If TARGET_P9_MINMAX was turned on explicitly.
Hereafter, THE OPTION_MASK_P9_VECTOR flag is considered to
have been turned on explicitly. */
if ((flags & OPTION_MASK_P9_VECTOR) != 0)
rs6000_define_or_undefine_macro (define_p, "__POWER9_VECTOR__");
/* Note that the OPTION_MASK_QUAD_MEMORY flag is automatically
turned off in the following conditions:
1. If TARGET_POWERPC64 is turned off.
2. If WORDS_BIG_ENDIAN is false (non-atomic quad memory
load/store are disabled on little endian). */
if ((flags & OPTION_MASK_QUAD_MEMORY) != 0)
rs6000_define_or_undefine_macro (define_p, "__QUAD_MEMORY__");
/* Note that the OPTION_MASK_QUAD_MEMORY_ATOMIC flag is automatically
turned off in the following conditions:
1. If TARGET_POWERPC64 is turned off.
Note that the OPTION_MASK_QUAD_MEMORY_ATOMIC flag is
automatically turned on in the following conditions:
1. If TARGET_QUAD_MEMORY and this flag was not explicitly
disabled. */
if ((flags & OPTION_MASK_QUAD_MEMORY_ATOMIC) != 0)
rs6000_define_or_undefine_macro (define_p, "__QUAD_MEMORY_ATOMIC__");
/* Note that the OPTION_MASK_CRYPTO flag is automatically turned off
in the following conditions:
1. If any of TARGET_HARD_FLOAT or TARGET_ALTIVEC or TARGET_VSX
are turned off explicitly and OPTION_MASK_CRYPTO is not turned
on explicitly.
2. If TARGET_ALTIVEC is turned off. */
if ((flags & OPTION_MASK_CRYPTO) != 0)
rs6000_define_or_undefine_macro (define_p, "__CRYPTO__");
if ((flags & OPTION_MASK_FLOAT128_KEYWORD) != 0)
{
rs6000_define_or_undefine_macro (define_p, "__FLOAT128__");
if (define_p)
rs6000_define_or_undefine_macro (true, "__float128=__ieee128");
else
rs6000_define_or_undefine_macro (false, "__float128");
}
/* OPTION_MASK_FLOAT128_HARDWARE can be turned on if -mcpu=power9 is used or
via the target attribute/pragma. */
if ((flags & OPTION_MASK_FLOAT128_HW) != 0)
rs6000_define_or_undefine_macro (define_p, "__FLOAT128_HARDWARE__");
/* options from the builtin masks. */
/* Note that RS6000_BTM_CELL is enabled only if (rs6000_cpu ==
PROCESSOR_CELL) (e.g. -mcpu=cell). */
if ((bu_mask & RS6000_BTM_CELL) != 0)
rs6000_define_or_undefine_macro (define_p, "__PPU__");
/* Tell the user if we support the MMA instructions. */
if ((flags & OPTION_MASK_MMA) != 0)
rs6000_define_or_undefine_macro (define_p, "__MMA__");
/* Whether pc-relative code is being generated. */
if ((flags & OPTION_MASK_PCREL) != 0)
rs6000_define_or_undefine_macro (define_p, "__PCREL__");
/* Tell the user -mrop-protect is in play. */
if (rs6000_rop_protect)
rs6000_define_or_undefine_macro (define_p, "__ROP_PROTECT__");
}
void
rs6000_cpu_cpp_builtins (cpp_reader *pfile)
{
/* Define all of the common macros. */
rs6000_target_modify_macros (true, rs6000_isa_flags,
rs6000_builtin_mask_calculate ());
if (TARGET_FRE)
builtin_define ("__RECIP__");
if (TARGET_FRES)
builtin_define ("__RECIPF__");
if (TARGET_FRSQRTE)
builtin_define ("__RSQRTE__");
if (TARGET_FRSQRTES)
builtin_define ("__RSQRTEF__");
if (TARGET_FLOAT128_TYPE)
builtin_define ("__FLOAT128_TYPE__");
#ifdef TARGET_LIBC_PROVIDES_HWCAP_IN_TCB
builtin_define ("__BUILTIN_CPU_SUPPORTS__");
#endif
if (TARGET_EXTRA_BUILTINS && cpp_get_options (pfile)->lang != CLK_ASM)
{
/* Define the AltiVec syntactic elements. */
builtin_define ("__vector=__attribute__((altivec(vector__)))");
builtin_define ("__pixel=__attribute__((altivec(pixel__))) unsigned short");
builtin_define ("__bool=__attribute__((altivec(bool__))) unsigned");
if (!flag_iso)
{
builtin_define ("vector=vector");
builtin_define ("pixel=pixel");
builtin_define ("bool=bool");
builtin_define ("_Bool=_Bool");
init_vector_keywords ();
/* Enable context-sensitive macros. */
cpp_get_callbacks (pfile)->macro_to_expand = rs6000_macro_to_expand;
}
}
if (!TARGET_HARD_FLOAT)
builtin_define ("_SOFT_DOUBLE");
/* Used by lwarx/stwcx. errata work-around. */
if (rs6000_cpu == PROCESSOR_PPC405)
builtin_define ("__PPC405__");
/* Used by libstdc++. */
if (TARGET_NO_LWSYNC)
builtin_define ("__NO_LWSYNC__");
if (TARGET_EXTRA_BUILTINS)
{
/* For the VSX builtin functions identical to Altivec functions, just map
the altivec builtin into the vsx version (the altivec functions
generate VSX code if -mvsx). */
builtin_define ("__builtin_vsx_xxland=__builtin_vec_and");
builtin_define ("__builtin_vsx_xxlandc=__builtin_vec_andc");
builtin_define ("__builtin_vsx_xxlnor=__builtin_vec_nor");
builtin_define ("__builtin_vsx_xxlor=__builtin_vec_or");
builtin_define ("__builtin_vsx_xxlxor=__builtin_vec_xor");
builtin_define ("__builtin_vsx_xxsel=__builtin_vec_sel");
builtin_define ("__builtin_vsx_vperm=__builtin_vec_perm");
/* Also map the a and m versions of the multiply/add instructions to the
builtin for people blindly going off the instruction manual. */
builtin_define ("__builtin_vsx_xvmaddadp=__builtin_vsx_xvmadddp");
builtin_define ("__builtin_vsx_xvmaddmdp=__builtin_vsx_xvmadddp");
builtin_define ("__builtin_vsx_xvmaddasp=__builtin_vsx_xvmaddsp");
builtin_define ("__builtin_vsx_xvmaddmsp=__builtin_vsx_xvmaddsp");
builtin_define ("__builtin_vsx_xvmsubadp=__builtin_vsx_xvmsubdp");
builtin_define ("__builtin_vsx_xvmsubmdp=__builtin_vsx_xvmsubdp");
builtin_define ("__builtin_vsx_xvmsubasp=__builtin_vsx_xvmsubsp");
builtin_define ("__builtin_vsx_xvmsubmsp=__builtin_vsx_xvmsubsp");
builtin_define ("__builtin_vsx_xvnmaddadp=__builtin_vsx_xvnmadddp");
builtin_define ("__builtin_vsx_xvnmaddmdp=__builtin_vsx_xvnmadddp");
builtin_define ("__builtin_vsx_xvnmaddasp=__builtin_vsx_xvnmaddsp");
builtin_define ("__builtin_vsx_xvnmaddmsp=__builtin_vsx_xvnmaddsp");
builtin_define ("__builtin_vsx_xvnmsubadp=__builtin_vsx_xvnmsubdp");
builtin_define ("__builtin_vsx_xvnmsubmdp=__builtin_vsx_xvnmsubdp");
builtin_define ("__builtin_vsx_xvnmsubasp=__builtin_vsx_xvnmsubsp");
builtin_define ("__builtin_vsx_xvnmsubmsp=__builtin_vsx_xvnmsubsp");
}
/* Map the old _Float128 'q' builtins into the new 'f128' builtins. */
if (TARGET_FLOAT128_TYPE)
{
builtin_define ("__builtin_fabsq=__builtin_fabsf128");
builtin_define ("__builtin_copysignq=__builtin_copysignf128");
builtin_define ("__builtin_nanq=__builtin_nanf128");
builtin_define ("__builtin_nansq=__builtin_nansf128");
builtin_define ("__builtin_infq=__builtin_inff128");
builtin_define ("__builtin_huge_valq=__builtin_huge_valf128");
}
/* Tell users they can use __builtin_bswap{16,64}. */
builtin_define ("__HAVE_BSWAP__");
/* May be overridden by target configuration. */
RS6000_CPU_CPP_ENDIAN_BUILTINS();
if (TARGET_LONG_DOUBLE_128)
{
builtin_define ("__LONG_DOUBLE_128__");
builtin_define ("__LONGDOUBLE128");
if (TARGET_IEEEQUAD)
{
/* Older versions of GLIBC used __attribute__((__KC__)) to create the
IEEE 128-bit floating point complex type for C++ (which does not
support _Float128 _Complex). If the default for long double is
IEEE 128-bit mode, the library would need to use
__attribute__((__TC__)) instead. Defining __KF__ and __KC__
is a stop-gap to build with the older libraries, until we
get an updated library. */
builtin_define ("__LONG_DOUBLE_IEEE128__");
builtin_define ("__KF__=__TF__");
builtin_define ("__KC__=__TC__");
}
else
builtin_define ("__LONG_DOUBLE_IBM128__");
}
switch (TARGET_CMODEL)
{
/* Deliberately omit __CMODEL_SMALL__ since that was the default
before --mcmodel support was added. */
case CMODEL_MEDIUM:
builtin_define ("__CMODEL_MEDIUM__");
break;
case CMODEL_LARGE:
builtin_define ("__CMODEL_LARGE__");
break;
default:
break;
}
switch (rs6000_current_abi)
{
case ABI_V4:
builtin_define ("_CALL_SYSV");
break;
case ABI_AIX:
builtin_define ("_CALL_AIXDESC");
builtin_define ("_CALL_AIX");
builtin_define ("_CALL_ELF=1");
break;
case ABI_ELFv2:
builtin_define ("_CALL_ELF=2");
break;
case ABI_DARWIN:
builtin_define ("_CALL_DARWIN");
break;
default:
break;
}
/* Vector element order. */
if (BYTES_BIG_ENDIAN)
builtin_define ("__VEC_ELEMENT_REG_ORDER__=__ORDER_BIG_ENDIAN__");
else
builtin_define ("__VEC_ELEMENT_REG_ORDER__=__ORDER_LITTLE_ENDIAN__");
/* Let the compiled code know if 'f' class registers will not be available. */
if (TARGET_SOFT_FLOAT)
builtin_define ("__NO_FPRS__");
/* Whether aggregates passed by value are aligned to a 16 byte boundary
if their alignment is 16 bytes or larger. */
if ((TARGET_MACHO && rs6000_darwin64_abi)
|| DEFAULT_ABI == ABI_ELFv2
|| (DEFAULT_ABI == ABI_AIX && !rs6000_compat_align_parm))
builtin_define ("__STRUCT_PARM_ALIGN__=16");
}
/* Convert a type stored into a struct altivec_builtin_types as ID,
into a tree. The types are in rs6000_builtin_types: negative values
create a pointer type for the type associated to ~ID. Note it is
a logical NOT, rather than a negation, otherwise you cannot represent
a pointer type for ID 0. */
static inline tree
rs6000_builtin_type (int id)
{
tree t;
t = rs6000_builtin_types[id < 0 ? ~id : id];
return id < 0 ? build_pointer_type (t) : t;
}
/* Check whether the type of an argument, T, is compatible with a type ID
stored into a struct altivec_builtin_types. Integer types are considered
compatible; otherwise, the language hook lang_hooks.types_compatible_p makes
the decision. Also allow long double and _Float128 to be compatible if
-mabi=ieeelongdouble. */
static inline bool
is_float128_p (tree t)
{
return (t == float128_type_node
|| (TARGET_IEEEQUAD
&& TARGET_LONG_DOUBLE_128
&& t == long_double_type_node));
}
/* Return true iff ARGTYPE can be compatibly passed as PARMTYPE. */
static bool
rs6000_new_builtin_type_compatible (tree parmtype, tree argtype)
{
if (parmtype == error_mark_node)
return false;
if (INTEGRAL_TYPE_P (parmtype) && INTEGRAL_TYPE_P (argtype))
return true;
if (TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128
&& is_float128_p (parmtype) && is_float128_p (argtype))
return true;
if (POINTER_TYPE_P (parmtype) && POINTER_TYPE_P (argtype))
{
parmtype = TREE_TYPE (parmtype);
argtype = TREE_TYPE (argtype);
if (TYPE_READONLY (argtype))
parmtype = build_qualified_type (parmtype, TYPE_QUAL_CONST);
}
return lang_hooks.types_compatible_p (parmtype, argtype);
}
static inline bool
rs6000_builtin_type_compatible (tree t, int id)
{
tree builtin_type;
builtin_type = rs6000_builtin_type (id);
if (t == error_mark_node)
return false;
if (INTEGRAL_TYPE_P (t) && INTEGRAL_TYPE_P (builtin_type))
return true;
else if (TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128
&& is_float128_p (t) && is_float128_p (builtin_type))
return true;
else
return lang_hooks.types_compatible_p (t, builtin_type);
}
/* In addition to calling fold_convert for EXPR of type TYPE, also
call c_fully_fold to remove any C_MAYBE_CONST_EXPRs that could be
hiding there (PR47197). */
static tree
fully_fold_convert (tree type, tree expr)
{
tree result = fold_convert (type, expr);
bool maybe_const = true;
if (!c_dialect_cxx ())
result = c_fully_fold (result, false, &maybe_const);
return result;
}
/* Build a tree for a function call to an Altivec non-overloaded builtin.
The overloaded builtin that matched the types and args is described
by DESC. The N arguments are given in ARGS, respectively.
Actually the only thing it does is calling fold_convert on ARGS, with
a small exception for vec_{all,any}_{ge,le} predicates. */
static tree
altivec_build_resolved_builtin (tree *args, int n,
const struct altivec_builtin_types *desc)
{
tree impl_fndecl = rs6000_builtin_decls[desc->overloaded_code];
tree ret_type = rs6000_builtin_type (desc->ret_type);
tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (impl_fndecl));
tree arg_type[4];
tree call;
int i;
for (i = 0; i < n; i++)
arg_type[i] = TREE_VALUE (argtypes), argtypes = TREE_CHAIN (argtypes);
/* The AltiVec overloading implementation is overall gross, but this
is particularly disgusting. The vec_{all,any}_{ge,le} builtins
are completely different for floating-point vs. integer vector
types, because the former has vcmpgefp, but the latter should use
vcmpgtXX.
In practice, the second and third arguments are swapped, and the
condition (LT vs. EQ, which is recognizable by bit 1 of the first
argument) is reversed. Patch the arguments here before building
the resolved CALL_EXPR. */
if (n == 3
&& desc->code == ALTIVEC_BUILTIN_VEC_VCMPGE_P
&& desc->overloaded_code != ALTIVEC_BUILTIN_VCMPGEFP_P
&& desc->overloaded_code != VSX_BUILTIN_XVCMPGEDP_P)
{
std::swap (args[1], args[2]);
std::swap (arg_type[1], arg_type[2]);
args[0] = fold_build2 (BIT_XOR_EXPR, TREE_TYPE (args[0]), args[0],
build_int_cst (NULL_TREE, 2));
}
switch (n)
{
case 0:
call = build_call_expr (impl_fndecl, 0);
break;
case 1:
call = build_call_expr (impl_fndecl, 1,
fully_fold_convert (arg_type[0], args[0]));
break;
case 2:
call = build_call_expr (impl_fndecl, 2,
fully_fold_convert (arg_type[0], args[0]),
fully_fold_convert (arg_type[1], args[1]));
break;
case 3:
call = build_call_expr (impl_fndecl, 3,
fully_fold_convert (arg_type[0], args[0]),
fully_fold_convert (arg_type[1], args[1]),
fully_fold_convert (arg_type[2], args[2]));
break;
case 4:
call = build_call_expr (impl_fndecl, 4,
fully_fold_convert (arg_type[0], args[0]),
fully_fold_convert (arg_type[1], args[1]),
fully_fold_convert (arg_type[2], args[2]),
fully_fold_convert (arg_type[3], args[3]));
break;
default:
gcc_unreachable ();
}
return fold_convert (ret_type, call);
}
/* Implementation of the resolve_overloaded_builtin target hook, to
support Altivec's overloaded builtins. */
tree
altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
void *passed_arglist)
{
if (new_builtins_are_live)
return altivec_resolve_new_overloaded_builtin (loc, fndecl,
passed_arglist);
vec<tree, va_gc> *arglist = static_cast<vec<tree, va_gc> *> (passed_arglist);
unsigned int nargs = vec_safe_length (arglist);
enum rs6000_builtins fcode
= (enum rs6000_builtins) DECL_MD_FUNCTION_CODE (fndecl);
tree fnargs = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
tree types[4], args[4];
const struct altivec_builtin_types *desc;
unsigned int n;
if (!rs6000_overloaded_builtin_p (fcode))
return NULL_TREE;
if (TARGET_DEBUG_BUILTIN)
fprintf (stderr, "altivec_resolve_overloaded_builtin, code = %4d, %s\n",
(int)fcode, IDENTIFIER_POINTER (DECL_NAME (fndecl)));
/* vec_lvsl and vec_lvsr are deprecated for use with LE element order. */
if (fcode == ALTIVEC_BUILTIN_VEC_LVSL && !BYTES_BIG_ENDIAN)
warning (OPT_Wdeprecated,
"%<vec_lvsl%> is deprecated for little endian; use "
"assignment for unaligned loads and stores");
else if (fcode == ALTIVEC_BUILTIN_VEC_LVSR && !BYTES_BIG_ENDIAN)
warning (OPT_Wdeprecated,
"%<vec_lvsr%> is deprecated for little endian; use "
"assignment for unaligned loads and stores");
if (fcode == ALTIVEC_BUILTIN_VEC_MUL)
{
/* vec_mul needs to be special cased because there are no instructions
for it for the {un}signed char, {un}signed short, and {un}signed int
types. */
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", "vec_mul");
return error_mark_node;
}
tree arg0 = (*arglist)[0];
tree arg0_type = TREE_TYPE (arg0);
tree arg1 = (*arglist)[1];
tree arg1_type = TREE_TYPE (arg1);
/* Both arguments must be vectors and the types must be compatible. */
if (TREE_CODE (arg0_type) != VECTOR_TYPE)
goto bad;
if (!lang_hooks.types_compatible_p (arg0_type, arg1_type))
goto bad;
switch (TYPE_MODE (TREE_TYPE (arg0_type)))
{
case E_QImode:
case E_HImode:
case E_SImode:
case E_DImode:
case E_TImode:
{
/* For scalar types just use a multiply expression. */
return fold_build2_loc (loc, MULT_EXPR, TREE_TYPE (arg0), arg0,
fold_convert (TREE_TYPE (arg0), arg1));
}
case E_SFmode:
{
/* For floats use the xvmulsp instruction directly. */
tree call = rs6000_builtin_decls[VSX_BUILTIN_XVMULSP];
return build_call_expr (call, 2, arg0, arg1);
}
case E_DFmode:
{
/* For doubles use the xvmuldp instruction directly. */
tree call = rs6000_builtin_decls[VSX_BUILTIN_XVMULDP];
return build_call_expr (call, 2, arg0, arg1);
}
/* Other types are errors. */
default:
goto bad;
}
}
if (fcode == ALTIVEC_BUILTIN_VEC_CMPNE)
{
/* vec_cmpne needs to be special cased because there are no instructions
for it (prior to power 9). */
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", "vec_cmpne");
return error_mark_node;
}
tree arg0 = (*arglist)[0];
tree arg0_type = TREE_TYPE (arg0);
tree arg1 = (*arglist)[1];
tree arg1_type = TREE_TYPE (arg1);
/* Both arguments must be vectors and the types must be compatible. */
if (TREE_CODE (arg0_type) != VECTOR_TYPE)
goto bad;
if (!lang_hooks.types_compatible_p (arg0_type, arg1_type))
goto bad;
/* Power9 instructions provide the most efficient implementation of
ALTIVEC_BUILTIN_VEC_CMPNE if the mode is not DImode or TImode
or SFmode or DFmode. */
if (!TARGET_P9_VECTOR
|| (TYPE_MODE (TREE_TYPE (arg0_type)) == DImode)
|| (TYPE_MODE (TREE_TYPE (arg0_type)) == TImode)
|| (TYPE_MODE (TREE_TYPE (arg0_type)) == SFmode)
|| (TYPE_MODE (TREE_TYPE (arg0_type)) == DFmode))
{
switch (TYPE_MODE (TREE_TYPE (arg0_type)))
{
/* vec_cmpneq (va, vb) == vec_nor (vec_cmpeq (va, vb),
vec_cmpeq (va, vb)). */
/* Note: vec_nand also works but opt changes vec_nand's
to vec_nor's anyway. */
case E_QImode:
case E_HImode:
case E_SImode:
case E_DImode:
case E_TImode:
case E_SFmode:
case E_DFmode:
{
/* call = vec_cmpeq (va, vb)
result = vec_nor (call, call). */
vec<tree, va_gc> *params = make_tree_vector ();
vec_safe_push (params, arg0);
vec_safe_push (params, arg1);
tree call = altivec_resolve_overloaded_builtin
(loc, rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_CMPEQ],
params);
/* Use save_expr to ensure that operands used more than once
that may have side effects (like calls) are only evaluated
once. */
call = save_expr (call);
params = make_tree_vector ();
vec_safe_push (params, call);
vec_safe_push (params, call);
return altivec_resolve_overloaded_builtin
(loc, rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_NOR], params);
}
/* Other types are errors. */
default:
goto bad;
}
}
/* else, fall through and process the Power9 alternative below */
}
if (fcode == ALTIVEC_BUILTIN_VEC_ADDE
|| fcode == ALTIVEC_BUILTIN_VEC_SUBE)
{
/* vec_adde needs to be special cased because there is no instruction
for the {un}signed int version. */
if (nargs != 3)
{
const char *name = fcode == ALTIVEC_BUILTIN_VEC_ADDE ?
"vec_adde": "vec_sube";
error ("builtin %qs only accepts 3 arguments", name);
return error_mark_node;
}
tree arg0 = (*arglist)[0];
tree arg0_type = TREE_TYPE (arg0);
tree arg1 = (*arglist)[1];
tree arg1_type = TREE_TYPE (arg1);
tree arg2 = (*arglist)[2];
tree arg2_type = TREE_TYPE (arg2);
/* All 3 arguments must be vectors of (signed or unsigned) (int or
__int128) and the types must be compatible. */
if (TREE_CODE (arg0_type) != VECTOR_TYPE)
goto bad;
if (!lang_hooks.types_compatible_p (arg0_type, arg1_type)
|| !lang_hooks.types_compatible_p (arg1_type, arg2_type))
goto bad;
switch (TYPE_MODE (TREE_TYPE (arg0_type)))
{
/* For {un}signed ints,
vec_adde (va, vb, carryv) == vec_add (vec_add (va, vb),
vec_and (carryv, 1)).
vec_sube (va, vb, carryv) == vec_sub (vec_sub (va, vb),
vec_and (carryv, 1)). */
case E_SImode:
{
tree add_sub_builtin;
vec<tree, va_gc> *params = make_tree_vector ();
vec_safe_push (params, arg0);
vec_safe_push (params, arg1);
if (fcode == ALTIVEC_BUILTIN_VEC_ADDE)
add_sub_builtin = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_ADD];
else
add_sub_builtin = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_SUB];
tree call = altivec_resolve_overloaded_builtin (loc,
add_sub_builtin,
params);
tree const1 = build_int_cstu (TREE_TYPE (arg0_type), 1);
tree ones_vector = build_vector_from_val (arg0_type, const1);
tree and_expr = fold_build2_loc (loc, BIT_AND_EXPR, arg0_type,
arg2, ones_vector);
params = make_tree_vector ();
vec_safe_push (params, call);
vec_safe_push (params, and_expr);
return altivec_resolve_overloaded_builtin (loc, add_sub_builtin,
params);
}
/* For {un}signed __int128s use the vaddeuqm instruction
directly. */
case E_TImode:
{
tree bii;
if (fcode == ALTIVEC_BUILTIN_VEC_ADDE)
bii = rs6000_builtin_decls[P8V_BUILTIN_VEC_VADDEUQM];
else
bii = rs6000_builtin_decls[P8V_BUILTIN_VEC_VSUBEUQM];
return altivec_resolve_overloaded_builtin (loc, bii, arglist);
}
/* Types other than {un}signed int and {un}signed __int128
are errors. */
default:
goto bad;
}
}
if (fcode == ALTIVEC_BUILTIN_VEC_ADDEC
|| fcode == ALTIVEC_BUILTIN_VEC_SUBEC)
{
/* vec_addec and vec_subec needs to be special cased because there is
no instruction for the {un}signed int version. */
if (nargs != 3)
{
const char *name = fcode == ALTIVEC_BUILTIN_VEC_ADDEC ?
"vec_addec": "vec_subec";
error ("builtin %qs only accepts 3 arguments", name);
return error_mark_node;
}
tree arg0 = (*arglist)[0];
tree arg0_type = TREE_TYPE (arg0);
tree arg1 = (*arglist)[1];
tree arg1_type = TREE_TYPE (arg1);
tree arg2 = (*arglist)[2];
tree arg2_type = TREE_TYPE (arg2);
/* All 3 arguments must be vectors of (signed or unsigned) (int or
__int128) and the types must be compatible. */
if (TREE_CODE (arg0_type) != VECTOR_TYPE)
goto bad;
if (!lang_hooks.types_compatible_p (arg0_type, arg1_type)
|| !lang_hooks.types_compatible_p (arg1_type, arg2_type))
goto bad;
switch (TYPE_MODE (TREE_TYPE (arg0_type)))
{
/* For {un}signed ints,
vec_addec (va, vb, carryv) ==
vec_or (vec_addc (va, vb),
vec_addc (vec_add (va, vb),
vec_and (carryv, 0x1))). */
case E_SImode:
{
/* Use save_expr to ensure that operands used more than once
that may have side effects (like calls) are only evaluated
once. */
tree as_builtin;
tree as_c_builtin;
arg0 = save_expr (arg0);
arg1 = save_expr (arg1);
vec<tree, va_gc> *params = make_tree_vector ();
vec_safe_push (params, arg0);
vec_safe_push (params, arg1);
if (fcode == ALTIVEC_BUILTIN_VEC_ADDEC)
as_c_builtin = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_ADDC];
else
as_c_builtin = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_SUBC];
tree call1 = altivec_resolve_overloaded_builtin (loc, as_c_builtin,
params);
params = make_tree_vector ();
vec_safe_push (params, arg0);
vec_safe_push (params, arg1);
if (fcode == ALTIVEC_BUILTIN_VEC_ADDEC)
as_builtin = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_ADD];
else
as_builtin = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_SUB];
tree call2 = altivec_resolve_overloaded_builtin (loc, as_builtin,
params);
tree const1 = build_int_cstu (TREE_TYPE (arg0_type), 1);
tree ones_vector = build_vector_from_val (arg0_type, const1);
tree and_expr = fold_build2_loc (loc, BIT_AND_EXPR, arg0_type,
arg2, ones_vector);
params = make_tree_vector ();
vec_safe_push (params, call2);
vec_safe_push (params, and_expr);
call2 = altivec_resolve_overloaded_builtin (loc, as_c_builtin,
params);
params = make_tree_vector ();
vec_safe_push (params, call1);
vec_safe_push (params, call2);
tree or_builtin = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_OR];
return altivec_resolve_overloaded_builtin (loc, or_builtin,
params);
}
/* For {un}signed __int128s use the vaddecuq/vsubbecuq
instructions. */
case E_TImode:
{
tree bii;
if (fcode == ALTIVEC_BUILTIN_VEC_ADDEC)
bii = rs6000_builtin_decls[P8V_BUILTIN_VEC_VADDECUQ];
else
bii = rs6000_builtin_decls[P8V_BUILTIN_VEC_VSUBECUQ];
return altivec_resolve_overloaded_builtin (loc, bii, arglist);
}
/* Types other than {un}signed int and {un}signed __int128
are errors. */
default:
goto bad;
}
}
/* For now treat vec_splats and vec_promote as the same. */
if (fcode == ALTIVEC_BUILTIN_VEC_SPLATS
|| fcode == ALTIVEC_BUILTIN_VEC_PROMOTE)
{
tree type, arg;
int size;
int i;
bool unsigned_p;
vec<constructor_elt, va_gc> *vec;
const char *name = fcode == ALTIVEC_BUILTIN_VEC_SPLATS ? "vec_splats": "vec_promote";
if (fcode == ALTIVEC_BUILTIN_VEC_SPLATS && nargs != 1)
{
error ("builtin %qs only accepts 1 argument", name);
return error_mark_node;
}
if (fcode == ALTIVEC_BUILTIN_VEC_PROMOTE && nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", name);
return error_mark_node;
}
/* Ignore promote's element argument. */
if (fcode == ALTIVEC_BUILTIN_VEC_PROMOTE
&& !INTEGRAL_TYPE_P (TREE_TYPE ((*arglist)[1])))
goto bad;
arg = (*arglist)[0];
type = TREE_TYPE (arg);
if (!SCALAR_FLOAT_TYPE_P (type)
&& !INTEGRAL_TYPE_P (type))
goto bad;
unsigned_p = TYPE_UNSIGNED (type);
switch (TYPE_MODE (type))
{
case E_TImode:
type = (unsigned_p ? unsigned_V1TI_type_node : V1TI_type_node);
size = 1;
break;
case E_DImode:
type = (unsigned_p ? unsigned_V2DI_type_node : V2DI_type_node);
size = 2;
break;
case E_SImode:
type = (unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node);
size = 4;
break;
case E_HImode:
type = (unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node);
size = 8;
break;
case E_QImode:
type = (unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node);
size = 16;
break;
case E_SFmode: type = V4SF_type_node; size = 4; break;
case E_DFmode: type = V2DF_type_node; size = 2; break;
default:
goto bad;
}
arg = save_expr (fold_convert (TREE_TYPE (type), arg));
vec_alloc (vec, size);
for(i = 0; i < size; i++)
{
constructor_elt elt = {NULL_TREE, arg};
vec->quick_push (elt);
}
return build_constructor (type, vec);
}
/* For now use pointer tricks to do the extraction, unless we are on VSX
extracting a double from a constant offset. */
if (fcode == ALTIVEC_BUILTIN_VEC_EXTRACT)
{
tree arg1;
tree arg1_type;
tree arg2;
tree arg1_inner_type;
tree decl, stmt;
tree innerptrtype;
machine_mode mode;
/* No second argument. */
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", "vec_extract");
return error_mark_node;
}
arg2 = (*arglist)[1];
arg1 = (*arglist)[0];
arg1_type = TREE_TYPE (arg1);
if (TREE_CODE (arg1_type) != VECTOR_TYPE)
goto bad;
if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
goto bad;
/* See if we can optimize vec_extracts with the current VSX instruction
set. */
mode = TYPE_MODE (arg1_type);
if (VECTOR_MEM_VSX_P (mode))
{
tree call = NULL_TREE;
int nunits = GET_MODE_NUNITS (mode);
arg2 = fold_for_warn (arg2);
/* If the second argument is an integer constant, generate
the built-in code if we can. We need 64-bit and direct
move to extract the small integer vectors. */
if (TREE_CODE (arg2) == INTEGER_CST)
{
wide_int selector = wi::to_wide (arg2);
selector = wi::umod_trunc (selector, nunits);
arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
switch (mode)
{
default:
break;
case E_V1TImode:
call = rs6000_builtin_decls[VSX_BUILTIN_VEC_EXT_V1TI];
break;
case E_V2DFmode:
call = rs6000_builtin_decls[VSX_BUILTIN_VEC_EXT_V2DF];
break;
case E_V2DImode:
call = rs6000_builtin_decls[VSX_BUILTIN_VEC_EXT_V2DI];
break;
case E_V4SFmode:
call = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_EXT_V4SF];
break;
case E_V4SImode:
if (TARGET_DIRECT_MOVE_64BIT)
call = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_EXT_V4SI];
break;
case E_V8HImode:
if (TARGET_DIRECT_MOVE_64BIT)
call = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_EXT_V8HI];
break;
case E_V16QImode:
if (TARGET_DIRECT_MOVE_64BIT)
call = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_EXT_V16QI];
break;
}
}
/* If the second argument is variable, we can optimize it if we are
generating 64-bit code on a machine with direct move. */
else if (TREE_CODE (arg2) != INTEGER_CST && TARGET_DIRECT_MOVE_64BIT)
{
switch (mode)
{
default:
break;
case E_V2DFmode:
call = rs6000_builtin_decls[VSX_BUILTIN_VEC_EXT_V2DF];
break;
case E_V2DImode:
call = rs6000_builtin_decls[VSX_BUILTIN_VEC_EXT_V2DI];
break;
case E_V4SFmode:
call = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_EXT_V4SF];
break;
case E_V4SImode:
call = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_EXT_V4SI];
break;
case E_V8HImode:
call = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_EXT_V8HI];
break;
case E_V16QImode:
call = rs6000_builtin_decls[ALTIVEC_BUILTIN_VEC_EXT_V16QI];
break;
}
}
if (call)
{
tree result = build_call_expr (call, 2, arg1, arg2);
/* Coerce the result to vector element type. May be no-op. */
arg1_inner_type = TREE_TYPE (arg1_type);
result = fold_convert (arg1_inner_type, result);
return result;
}
}
/* Build *(((arg1_inner_type*)&(vector type){arg1})+arg2). */
arg1_inner_type = TREE_TYPE (arg1_type);
arg2 = build_binary_op (loc, BIT_AND_EXPR, arg2,
build_int_cst (TREE_TYPE (arg2),
TYPE_VECTOR_SUBPARTS (arg1_type)
- 1), 0);
decl = build_decl (loc, VAR_DECL, NULL_TREE, arg1_type);
DECL_EXTERNAL (decl) = 0;
TREE_PUBLIC (decl) = 0;
DECL_CONTEXT (decl) = current_function_decl;
TREE_USED (decl) = 1;
TREE_TYPE (decl) = arg1_type;
TREE_READONLY (decl) = TYPE_READONLY (arg1_type);
if (c_dialect_cxx ())
{
stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1,
NULL_TREE, NULL_TREE);
SET_EXPR_LOCATION (stmt, loc);
}
else
{
DECL_INITIAL (decl) = arg1;
stmt = build1 (DECL_EXPR, arg1_type, decl);
TREE_ADDRESSABLE (decl) = 1;
SET_EXPR_LOCATION (stmt, loc);
stmt = build1 (COMPOUND_LITERAL_EXPR, arg1_type, stmt);
}
innerptrtype = build_pointer_type (arg1_inner_type);
stmt = build_unary_op (loc, ADDR_EXPR, stmt, 0);
stmt = convert (innerptrtype, stmt);
stmt = build_binary_op (loc, PLUS_EXPR, stmt, arg2, 1);
stmt = build_indirect_ref (loc, stmt, RO_NULL);
/* PR83660: We mark this as having side effects so that
downstream in fold_build_cleanup_point_expr () it will get a
CLEANUP_POINT_EXPR. If it does not we can run into an ICE
later in gimplify_cleanup_point_expr (). Potentially this
causes missed optimization because the actually is no side
effect. */
if (c_dialect_cxx ())
TREE_SIDE_EFFECTS (stmt) = 1;
return stmt;
}
/* For now use pointer tricks to do the insertion, unless we are on VSX
inserting a double to a constant offset.. */
if (fcode == ALTIVEC_BUILTIN_VEC_INSERT)
{
tree arg0;
tree arg1;
tree arg2;
tree arg1_type;
tree decl, stmt;
machine_mode mode;
/* No second or third arguments. */
if (nargs != 3)
{
error ("builtin %qs only accepts 3 arguments", "vec_insert");
return error_mark_node;
}
arg0 = (*arglist)[0];
arg1 = (*arglist)[1];
arg1_type = TREE_TYPE (arg1);
arg2 = fold_for_warn ((*arglist)[2]);
if (TREE_CODE (arg1_type) != VECTOR_TYPE)
goto bad;
if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
goto bad;
/* If we can use the VSX xxpermdi instruction, use that for insert. */
mode = TYPE_MODE (arg1_type);
if ((mode == V2DFmode || mode == V2DImode) && VECTOR_UNIT_VSX_P (mode)
&& TREE_CODE (arg2) == INTEGER_CST)
{
wide_int selector = wi::to_wide (arg2);
selector = wi::umod_trunc (selector, 2);
tree call = NULL_TREE;
arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
if (mode == V2DFmode)
call = rs6000_builtin_decls[VSX_BUILTIN_VEC_SET_V2DF];
else if (mode == V2DImode)
call = rs6000_builtin_decls[VSX_BUILTIN_VEC_SET_V2DI];
/* Note, __builtin_vec_insert_<xxx> has vector and scalar types
reversed. */
if (call)
return build_call_expr (call, 3, arg1, arg0, arg2);
}
else if (mode == V1TImode && VECTOR_UNIT_VSX_P (mode)
&& TREE_CODE (arg2) == INTEGER_CST)
{
tree call = rs6000_builtin_decls[VSX_BUILTIN_VEC_SET_V1TI];
wide_int selector = wi::zero(32);
arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
/* Note, __builtin_vec_insert_<xxx> has vector and scalar types
reversed. */
return build_call_expr (call, 3, arg1, arg0, arg2);
}
/* Build *(((arg1_inner_type*)&(vector type){arg1})+arg2) = arg0 with
VIEW_CONVERT_EXPR. i.e.:
D.3192 = v1;
_1 = n & 3;
VIEW_CONVERT_EXPR<int[4]>(D.3192)[_1] = i;
v1 = D.3192;
D.3194 = v1; */
if (TYPE_VECTOR_SUBPARTS (arg1_type) == 1)
arg2 = build_int_cst (TREE_TYPE (arg2), 0);
else
arg2 = build_binary_op (loc, BIT_AND_EXPR, arg2,
build_int_cst (TREE_TYPE (arg2),
TYPE_VECTOR_SUBPARTS (arg1_type)
- 1), 0);
decl = build_decl (loc, VAR_DECL, NULL_TREE, arg1_type);
DECL_EXTERNAL (decl) = 0;
TREE_PUBLIC (decl) = 0;
DECL_CONTEXT (decl) = current_function_decl;
TREE_USED (decl) = 1;
TREE_TYPE (decl) = arg1_type;
TREE_READONLY (decl) = TYPE_READONLY (arg1_type);
TREE_ADDRESSABLE (decl) = 1;
if (c_dialect_cxx ())
{
stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1,
NULL_TREE, NULL_TREE);
SET_EXPR_LOCATION (stmt, loc);
}
else
{
DECL_INITIAL (decl) = arg1;
stmt = build1 (DECL_EXPR, arg1_type, decl);
SET_EXPR_LOCATION (stmt, loc);
stmt = build1 (COMPOUND_LITERAL_EXPR, arg1_type, stmt);
}
if (TARGET_VSX)
{
stmt = build_array_ref (loc, stmt, arg2);
stmt = fold_build2 (MODIFY_EXPR, TREE_TYPE (arg0), stmt,
convert (TREE_TYPE (stmt), arg0));
stmt = build2 (COMPOUND_EXPR, arg1_type, stmt, decl);
}
else
{
tree arg1_inner_type;
tree innerptrtype;
arg1_inner_type = TREE_TYPE (arg1_type);
innerptrtype = build_pointer_type (arg1_inner_type);
stmt = build_unary_op (loc, ADDR_EXPR, stmt, 0);
stmt = convert (innerptrtype, stmt);
stmt = build_binary_op (loc, PLUS_EXPR, stmt, arg2, 1);
stmt = build_indirect_ref (loc, stmt, RO_NULL);
stmt = build2 (MODIFY_EXPR, TREE_TYPE (stmt), stmt,
convert (TREE_TYPE (stmt), arg0));
stmt = build2 (COMPOUND_EXPR, arg1_type, stmt, decl);
}
return stmt;
}
for (n = 0;
!VOID_TYPE_P (TREE_VALUE (fnargs)) && n < nargs;
fnargs = TREE_CHAIN (fnargs), n++)
{
tree decl_type = TREE_VALUE (fnargs);
tree arg = (*arglist)[n];
tree type;
if (arg == error_mark_node)
return error_mark_node;
if (n >= 4)
abort ();
arg = default_conversion (arg);
/* The C++ front-end converts float * to const void * using
NOP_EXPR<const void *> (NOP_EXPR<void *> (x)). */
type = TREE_TYPE (arg);
if (POINTER_TYPE_P (type)
&& TREE_CODE (arg) == NOP_EXPR
&& lang_hooks.types_compatible_p (TREE_TYPE (arg),
const_ptr_type_node)
&& lang_hooks.types_compatible_p (TREE_TYPE (TREE_OPERAND (arg, 0)),
ptr_type_node))
{
arg = TREE_OPERAND (arg, 0);
type = TREE_TYPE (arg);
}
/* Remove the const from the pointers to simplify the overload
matching further down. */
if (POINTER_TYPE_P (decl_type)
&& POINTER_TYPE_P (type)
&& TYPE_QUALS (TREE_TYPE (type)) != 0)
{
if (TYPE_READONLY (TREE_TYPE (type))
&& !TYPE_READONLY (TREE_TYPE (decl_type)))
warning (0, "passing argument %d of %qE discards qualifiers from "
"pointer target type", n + 1, fndecl);
type = build_pointer_type (build_qualified_type (TREE_TYPE (type),
0));
arg = fold_convert (type, arg);
}
/* For P9V_BUILTIN_VEC_LXVL, convert any const * to its non constant
equivalent to simplify the overload matching below. */
if (fcode == P9V_BUILTIN_VEC_LXVL)
{
if (POINTER_TYPE_P (type)
&& TYPE_READONLY (TREE_TYPE (type)))
{
type = build_pointer_type (build_qualified_type (
TREE_TYPE (type),0));
arg = fold_convert (type, arg);
}
}
args[n] = arg;
types[n] = type;
}
/* If the number of arguments did not match the prototype, return NULL
and the generic code will issue the appropriate error message. */
if (!VOID_TYPE_P (TREE_VALUE (fnargs)) || n < nargs)
return NULL;
if (n == 0)
abort ();
if (fcode == ALTIVEC_BUILTIN_VEC_STEP)
{
if (TREE_CODE (types[0]) != VECTOR_TYPE)
goto bad;
return build_int_cst (NULL_TREE, TYPE_VECTOR_SUBPARTS (types[0]));
}
{
bool unsupported_builtin = false;
enum rs6000_builtins overloaded_code;
tree result = NULL;
for (desc = altivec_overloaded_builtins;
desc->code && desc->code != fcode; desc++)
continue;
/* Need to special case __builtin_cmp because the overloaded forms
of this function take (unsigned int, unsigned int) or (unsigned
long long int, unsigned long long int). Since C conventions
allow the respective argument types to be implicitly coerced into
each other, the default handling does not provide adequate
discrimination between the desired forms of the function. */
if (fcode == P6_OV_BUILTIN_CMPB)
{
machine_mode arg1_mode = TYPE_MODE (types[0]);
machine_mode arg2_mode = TYPE_MODE (types[1]);
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", "__builtin_cmpb");
return error_mark_node;
}
/* If any supplied arguments are wider than 32 bits, resolve to
64-bit variant of built-in function. */
if ((GET_MODE_PRECISION (arg1_mode) > 32)
|| (GET_MODE_PRECISION (arg2_mode) > 32))
{
/* Assure all argument and result types are compatible with
the built-in function represented by P6_BUILTIN_CMPB. */
overloaded_code = P6_BUILTIN_CMPB;
}
else
{
/* Assure all argument and result types are compatible with
the built-in function represented by P6_BUILTIN_CMPB_32. */
overloaded_code = P6_BUILTIN_CMPB_32;
}
while (desc->code && desc->code == fcode
&& desc->overloaded_code != overloaded_code)
desc++;
if (desc->code && (desc->code == fcode)
&& rs6000_builtin_type_compatible (types[0], desc->op1)
&& rs6000_builtin_type_compatible (types[1], desc->op2))
{
if (rs6000_builtin_decls[desc->overloaded_code] != NULL_TREE)
{
result = altivec_build_resolved_builtin (args, n, desc);
/* overloaded_code is set above */
if (!rs6000_builtin_is_supported_p (overloaded_code))
unsupported_builtin = true;
else
return result;
}
else
unsupported_builtin = true;
}
}
else if (fcode == P9V_BUILTIN_VEC_VSIEDP)
{
machine_mode arg1_mode = TYPE_MODE (types[0]);
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments",
"scalar_insert_exp");
return error_mark_node;
}
/* If supplied first argument is wider than 64 bits, resolve to
128-bit variant of built-in function. */
if (GET_MODE_PRECISION (arg1_mode) > 64)
{
/* If first argument is of float variety, choose variant
that expects __ieee128 argument. Otherwise, expect
__int128 argument. */
if (GET_MODE_CLASS (arg1_mode) == MODE_FLOAT)
overloaded_code = P9V_BUILTIN_VSIEQPF;
else
overloaded_code = P9V_BUILTIN_VSIEQP;
}
else
{
/* If first argument is of float variety, choose variant
that expects double argument. Otherwise, expect
long long int argument. */
if (GET_MODE_CLASS (arg1_mode) == MODE_FLOAT)
overloaded_code = P9V_BUILTIN_VSIEDPF;
else
overloaded_code = P9V_BUILTIN_VSIEDP;
}
while (desc->code && desc->code == fcode
&& desc->overloaded_code != overloaded_code)
desc++;
if (desc->code && (desc->code == fcode)
&& rs6000_builtin_type_compatible (types[0], desc->op1)
&& rs6000_builtin_type_compatible (types[1], desc->op2))
{
if (rs6000_builtin_decls[desc->overloaded_code] != NULL_TREE)
{
result = altivec_build_resolved_builtin (args, n, desc);
/* overloaded_code is set above. */
if (!rs6000_builtin_is_supported_p (overloaded_code))
unsupported_builtin = true;
else
return result;
}
else
unsupported_builtin = true;
}
}
else if ((fcode == P10_BUILTIN_VEC_XXEVAL)
|| (fcode == P10V_BUILTIN_VXXPERMX))
{
signed char op3_type;
/* Need to special case P10_BUILTIN_VEC_XXEVAL and
P10V_BUILTIN_VXXPERMX because they take 4 arguments and the
existing infrastructure only handles three. */
if (nargs != 4)
{
const char *name = fcode == P10_BUILTIN_VEC_XXEVAL ?
"__builtin_vec_xxeval":"__builtin_vec_xxpermx";
error ("builtin %qs requires 4 arguments", name);
return error_mark_node;
}
for ( ; desc->code == fcode; desc++)
{
if (fcode == P10_BUILTIN_VEC_XXEVAL)
op3_type = desc->op3;
else /* P10V_BUILTIN_VXXPERMX */
op3_type = RS6000_BTI_V16QI;
if (rs6000_builtin_type_compatible (types[0], desc->op1)
&& rs6000_builtin_type_compatible (types[1], desc->op2)
&& rs6000_builtin_type_compatible (types[2], desc->op3)
&& rs6000_builtin_type_compatible (types[2], op3_type)
&& rs6000_builtin_type_compatible (types[3],
RS6000_BTI_UINTSI))
{
if (rs6000_builtin_decls[desc->overloaded_code] == NULL_TREE)
unsupported_builtin = true;
else
{
result = altivec_build_resolved_builtin (args, n, desc);
if (rs6000_builtin_is_supported_p (desc->overloaded_code))
return result;
/* Allow loop to continue in case a different
definition is supported. */
overloaded_code = desc->overloaded_code;
unsupported_builtin = true;
}
}
}
}
else
{
/* For arguments after the last, we have RS6000_BTI_NOT_OPAQUE in
the opX fields. */
for (; desc->code == fcode; desc++)
{
if ((desc->op1 == RS6000_BTI_NOT_OPAQUE
|| rs6000_builtin_type_compatible (types[0], desc->op1))
&& (desc->op2 == RS6000_BTI_NOT_OPAQUE
|| rs6000_builtin_type_compatible (types[1], desc->op2))
&& (desc->op3 == RS6000_BTI_NOT_OPAQUE
|| rs6000_builtin_type_compatible (types[2], desc->op3)))
{
if (rs6000_builtin_decls[desc->overloaded_code] != NULL_TREE)
{
result = altivec_build_resolved_builtin (args, n, desc);
if (!rs6000_builtin_is_supported_p (desc->overloaded_code))
{
/* Allow loop to continue in case a different
definition is supported. */
overloaded_code = desc->overloaded_code;
unsupported_builtin = true;
}
else
return result;
}
else
unsupported_builtin = true;
}
}
}
if (unsupported_builtin)
{
const char *name = rs6000_overloaded_builtin_name (fcode);
if (result != NULL)
{
const char *internal_name
= rs6000_overloaded_builtin_name (overloaded_code);
/* An error message making reference to the name of the
non-overloaded function has already been issued. Add
clarification of the previous message. */
rich_location richloc (line_table, input_location);
inform (&richloc, "builtin %qs requires builtin %qs",
name, internal_name);
}
else
error ("%qs is not supported in this compiler configuration", name);
/* If an error-representing result tree was returned from
altivec_build_resolved_builtin above, use it. */
return (result != NULL) ? result : error_mark_node;
}
}
bad:
{
const char *name = rs6000_overloaded_builtin_name (fcode);
error ("invalid parameter combination for AltiVec intrinsic %qs", name);
return error_mark_node;
}
}
/* Build a tree for a function call to an Altivec non-overloaded builtin.
The overloaded builtin that matched the types and args is described
by DESC. The N arguments are given in ARGS, respectively.
Actually the only thing it does is calling fold_convert on ARGS, with
a small exception for vec_{all,any}_{ge,le} predicates. */
static tree
altivec_build_new_resolved_builtin (tree *args, int n, tree fntype,
tree ret_type,
rs6000_gen_builtins bif_id,
rs6000_gen_builtins ovld_id)
{
tree argtypes = TYPE_ARG_TYPES (fntype);
tree arg_type[MAX_OVLD_ARGS];
tree fndecl = rs6000_builtin_decls_x[bif_id];
for (int i = 0; i < n; i++)
{
arg_type[i] = TREE_VALUE (argtypes);
argtypes = TREE_CHAIN (argtypes);
}
/* The AltiVec overloading implementation is overall gross, but this
is particularly disgusting. The vec_{all,any}_{ge,le} builtins
are completely different for floating-point vs. integer vector
types, because the former has vcmpgefp, but the latter should use
vcmpgtXX.
In practice, the second and third arguments are swapped, and the
condition (LT vs. EQ, which is recognizable by bit 1 of the first
argument) is reversed. Patch the arguments here before building
the resolved CALL_EXPR. */
if (n == 3
&& ovld_id == RS6000_OVLD_VEC_CMPGE_P
&& bif_id != RS6000_BIF_VCMPGEFP_P
&& bif_id != RS6000_BIF_XVCMPGEDP_P)
{
std::swap (args[1], args[2]);
std::swap (arg_type[1], arg_type[2]);
args[0] = fold_build2 (BIT_XOR_EXPR, TREE_TYPE (args[0]), args[0],
build_int_cst (NULL_TREE, 2));
}
for (int j = 0; j < n; j++)
args[j] = fully_fold_convert (arg_type[j], args[j]);
/* If the number of arguments to an overloaded function increases,
we must expand this switch. */
gcc_assert (MAX_OVLD_ARGS <= 4);
tree call;
switch (n)
{
case 0:
call = build_call_expr (fndecl, 0);
break;
case 1:
call = build_call_expr (fndecl, 1, args[0]);
break;
case 2:
call = build_call_expr (fndecl, 2, args[0], args[1]);
break;
case 3:
call = build_call_expr (fndecl, 3, args[0], args[1], args[2]);
break;
case 4:
call = build_call_expr (fndecl, 4, args[0], args[1], args[2], args[3]);
break;
default:
gcc_unreachable ();
}
return fold_convert (ret_type, call);
}
/* Implementation of the resolve_overloaded_builtin target hook, to
support Altivec's overloaded builtins. FIXME: This code needs
to be brutally factored. */
static tree
altivec_resolve_new_overloaded_builtin (location_t loc, tree fndecl,
void *passed_arglist)
{
vec<tree, va_gc> *arglist = static_cast<vec<tree, va_gc> *> (passed_arglist);
unsigned int nargs = vec_safe_length (arglist);
enum rs6000_gen_builtins fcode
= (enum rs6000_gen_builtins) DECL_MD_FUNCTION_CODE (fndecl);
tree fnargs = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
tree types[MAX_OVLD_ARGS];
tree args[MAX_OVLD_ARGS];
/* Return immediately if this isn't an overload. */
if (fcode <= RS6000_OVLD_NONE)
return NULL_TREE;
unsigned int adj_fcode = fcode - RS6000_OVLD_NONE;
if (TARGET_DEBUG_BUILTIN)
fprintf (stderr, "altivec_resolve_overloaded_builtin, code = %4d, %s\n",
(int) fcode, IDENTIFIER_POINTER (DECL_NAME (fndecl)));
/* vec_lvsl and vec_lvsr are deprecated for use with LE element order. */
if (fcode == RS6000_OVLD_VEC_LVSL && !BYTES_BIG_ENDIAN)
warning (OPT_Wdeprecated,
"%<vec_lvsl%> is deprecated for little endian; use "
"assignment for unaligned loads and stores");
else if (fcode == RS6000_OVLD_VEC_LVSR && !BYTES_BIG_ENDIAN)
warning (OPT_Wdeprecated,
"%<vec_lvsr%> is deprecated for little endian; use "
"assignment for unaligned loads and stores");
if (fcode == RS6000_OVLD_VEC_MUL)
{
/* vec_mul needs to be special cased because there are no instructions
for it for the {un}signed char, {un}signed short, and {un}signed int
types. */
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", "vec_mul");
return error_mark_node;
}
tree arg0 = (*arglist)[0];
tree arg0_type = TREE_TYPE (arg0);
tree arg1 = (*arglist)[1];
tree arg1_type = TREE_TYPE (arg1);
/* Both arguments must be vectors and the types must be compatible. */
if (TREE_CODE (arg0_type) != VECTOR_TYPE)
goto bad;
if (!lang_hooks.types_compatible_p (arg0_type, arg1_type))
goto bad;
switch (TYPE_MODE (TREE_TYPE (arg0_type)))
{
case E_QImode:
case E_HImode:
case E_SImode:
case E_DImode:
case E_TImode:
{
/* For scalar types just use a multiply expression. */
return fold_build2_loc (loc, MULT_EXPR, TREE_TYPE (arg0), arg0,
fold_convert (TREE_TYPE (arg0), arg1));
}
case E_SFmode:
{
/* For floats use the xvmulsp instruction directly. */
tree call = rs6000_builtin_decls_x[RS6000_BIF_XVMULSP];
return build_call_expr (call, 2, arg0, arg1);
}
case E_DFmode:
{
/* For doubles use the xvmuldp instruction directly. */
tree call = rs6000_builtin_decls_x[RS6000_BIF_XVMULDP];
return build_call_expr (call, 2, arg0, arg1);
}
/* Other types are errors. */
default:
goto bad;
}
}
if (fcode == RS6000_OVLD_VEC_CMPNE)
{
/* vec_cmpne needs to be special cased because there are no instructions
for it (prior to power 9). */
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", "vec_cmpne");
return error_mark_node;
}
tree arg0 = (*arglist)[0];
tree arg0_type = TREE_TYPE (arg0);
tree arg1 = (*arglist)[1];
tree arg1_type = TREE_TYPE (arg1);
/* Both arguments must be vectors and the types must be compatible. */
if (TREE_CODE (arg0_type) != VECTOR_TYPE)
goto bad;
if (!lang_hooks.types_compatible_p (arg0_type, arg1_type))
goto bad;
/* Power9 instructions provide the most efficient implementation of
ALTIVEC_BUILTIN_VEC_CMPNE if the mode is not DImode or TImode
or SFmode or DFmode. */
if (!TARGET_P9_VECTOR
|| (TYPE_MODE (TREE_TYPE (arg0_type)) == DImode)
|| (TYPE_MODE (TREE_TYPE (arg0_type)) == TImode)
|| (TYPE_MODE (TREE_TYPE (arg0_type)) == SFmode)
|| (TYPE_MODE (TREE_TYPE (arg0_type)) == DFmode))
{
switch (TYPE_MODE (TREE_TYPE (arg0_type)))
{
/* vec_cmpneq (va, vb) == vec_nor (vec_cmpeq (va, vb),
vec_cmpeq (va, vb)). */
/* Note: vec_nand also works but opt changes vec_nand's
to vec_nor's anyway. */
case E_QImode:
case E_HImode:
case E_SImode:
case E_DImode:
case E_TImode:
case E_SFmode:
case E_DFmode:
{
/* call = vec_cmpeq (va, vb)
result = vec_nor (call, call). */
vec<tree, va_gc> *params = make_tree_vector ();
vec_safe_push (params, arg0);
vec_safe_push (params, arg1);
tree call = altivec_resolve_new_overloaded_builtin
(loc, rs6000_builtin_decls_x[RS6000_OVLD_VEC_CMPEQ],
params);
/* Use save_expr to ensure that operands used more than once
that may have side effects (like calls) are only evaluated
once. */
call = save_expr (call);
params = make_tree_vector ();
vec_safe_push (params, call);
vec_safe_push (params, call);
return altivec_resolve_new_overloaded_builtin
(loc, rs6000_builtin_decls_x[RS6000_OVLD_VEC_NOR], params);
}
/* Other types are errors. */
default:
goto bad;
}
}
/* else, fall through and process the Power9 alternative below */
}
if (fcode == RS6000_OVLD_VEC_ADDE || fcode == RS6000_OVLD_VEC_SUBE)
{
/* vec_adde needs to be special cased because there is no instruction
for the {un}signed int version. */
if (nargs != 3)
{
const char *name;
name = fcode == RS6000_OVLD_VEC_ADDE ? "vec_adde" : "vec_sube";
error ("builtin %qs only accepts 3 arguments", name);
return error_mark_node;
}
tree arg0 = (*arglist)[0];
tree arg0_type = TREE_TYPE (arg0);
tree arg1 = (*arglist)[1];
tree arg1_type = TREE_TYPE (arg1);
tree arg2 = (*arglist)[2];
tree arg2_type = TREE_TYPE (arg2);
/* All 3 arguments must be vectors of (signed or unsigned) (int or
__int128) and the types must be compatible. */
if (TREE_CODE (arg0_type) != VECTOR_TYPE)
goto bad;
if (!lang_hooks.types_compatible_p (arg0_type, arg1_type)
|| !lang_hooks.types_compatible_p (arg1_type, arg2_type))
goto bad;
switch (TYPE_MODE (TREE_TYPE (arg0_type)))
{
/* For {un}signed ints,
vec_adde (va, vb, carryv) == vec_add (vec_add (va, vb),
vec_and (carryv, 1)).
vec_sube (va, vb, carryv) == vec_sub (vec_sub (va, vb),
vec_and (carryv, 1)). */
case E_SImode:
{
tree add_sub_builtin;
vec<tree, va_gc> *params = make_tree_vector ();
vec_safe_push (params, arg0);
vec_safe_push (params, arg1);
if (fcode == RS6000_OVLD_VEC_ADDE)
add_sub_builtin = rs6000_builtin_decls_x[RS6000_OVLD_VEC_ADD];
else
add_sub_builtin = rs6000_builtin_decls_x[RS6000_OVLD_VEC_SUB];
tree call
= altivec_resolve_new_overloaded_builtin (loc,
add_sub_builtin,
params);
tree const1 = build_int_cstu (TREE_TYPE (arg0_type), 1);
tree ones_vector = build_vector_from_val (arg0_type, const1);
tree and_expr = fold_build2_loc (loc, BIT_AND_EXPR, arg0_type,
arg2, ones_vector);
params = make_tree_vector ();
vec_safe_push (params, call);
vec_safe_push (params, and_expr);
return altivec_resolve_new_overloaded_builtin (loc,
add_sub_builtin,
params);
}
/* For {un}signed __int128s use the vaddeuqm/vsubeuqm instruction
directly. */
case E_TImode:
break;
/* Types other than {un}signed int and {un}signed __int128
are errors. */
default:
goto bad;
}
}
if (fcode == RS6000_OVLD_VEC_ADDEC || fcode == RS6000_OVLD_VEC_SUBEC)
{
/* vec_addec and vec_subec needs to be special cased because there is
no instruction for the {un}signed int version. */
if (nargs != 3)
{
const char *name;
name = fcode == RS6000_OVLD_VEC_ADDEC ? "vec_addec" : "vec_subec";
error ("builtin %qs only accepts 3 arguments", name);
return error_mark_node;
}
tree arg0 = (*arglist)[0];
tree arg0_type = TREE_TYPE (arg0);
tree arg1 = (*arglist)[1];
tree arg1_type = TREE_TYPE (arg1);
tree arg2 = (*arglist)[2];
tree arg2_type = TREE_TYPE (arg2);
/* All 3 arguments must be vectors of (signed or unsigned) (int or
__int128) and the types must be compatible. */
if (TREE_CODE (arg0_type) != VECTOR_TYPE)
goto bad;
if (!lang_hooks.types_compatible_p (arg0_type, arg1_type)
|| !lang_hooks.types_compatible_p (arg1_type, arg2_type))
goto bad;
switch (TYPE_MODE (TREE_TYPE (arg0_type)))
{
/* For {un}signed ints,
vec_addec (va, vb, carryv) ==
vec_or (vec_addc (va, vb),
vec_addc (vec_add (va, vb),
vec_and (carryv, 0x1))). */
case E_SImode:
{
/* Use save_expr to ensure that operands used more than once
that may have side effects (like calls) are only evaluated
once. */
tree as_builtin;
tree as_c_builtin;
arg0 = save_expr (arg0);
arg1 = save_expr (arg1);
vec<tree, va_gc> *params = make_tree_vector ();
vec_safe_push (params, arg0);
vec_safe_push (params, arg1);
if (fcode == RS6000_OVLD_VEC_ADDEC)
as_c_builtin = rs6000_builtin_decls_x[RS6000_OVLD_VEC_ADDC];
else
as_c_builtin = rs6000_builtin_decls_x[RS6000_OVLD_VEC_SUBC];
tree call1 = altivec_resolve_new_overloaded_builtin (loc,
as_c_builtin,
params);
params = make_tree_vector ();
vec_safe_push (params, arg0);
vec_safe_push (params, arg1);
if (fcode == RS6000_OVLD_VEC_ADDEC)
as_builtin = rs6000_builtin_decls_x[RS6000_OVLD_VEC_ADD];
else
as_builtin = rs6000_builtin_decls_x[RS6000_OVLD_VEC_SUB];
tree call2 = altivec_resolve_new_overloaded_builtin (loc,
as_builtin,
params);
tree const1 = build_int_cstu (TREE_TYPE (arg0_type), 1);
tree ones_vector = build_vector_from_val (arg0_type, const1);
tree and_expr = fold_build2_loc (loc, BIT_AND_EXPR, arg0_type,
arg2, ones_vector);
params = make_tree_vector ();
vec_safe_push (params, call2);
vec_safe_push (params, and_expr);
call2 = altivec_resolve_new_overloaded_builtin (loc, as_c_builtin,
params);
params = make_tree_vector ();
vec_safe_push (params, call1);
vec_safe_push (params, call2);
tree or_builtin = rs6000_builtin_decls_x[RS6000_OVLD_VEC_OR];
return altivec_resolve_new_overloaded_builtin (loc, or_builtin,
params);
}
/* For {un}signed __int128s use the vaddecuq/vsubbecuq
instructions. This occurs through normal processing. */
case E_TImode:
break;
/* Types other than {un}signed int and {un}signed __int128
are errors. */
default:
goto bad;
}
}
/* For now treat vec_splats and vec_promote as the same. */
if (fcode == RS6000_OVLD_VEC_SPLATS || fcode == RS6000_OVLD_VEC_PROMOTE)
{
tree type, arg;
int size;
int i;
bool unsigned_p;
vec<constructor_elt, va_gc> *vec;
const char *name;
name = fcode == RS6000_OVLD_VEC_SPLATS ? "vec_splats" : "vec_promote";
if (fcode == RS6000_OVLD_VEC_SPLATS && nargs != 1)
{
error ("builtin %qs only accepts 1 argument", name);
return error_mark_node;
}
if (fcode == RS6000_OVLD_VEC_PROMOTE && nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", name);
return error_mark_node;
}
/* Ignore promote's element argument. */
if (fcode == RS6000_OVLD_VEC_PROMOTE
&& !INTEGRAL_TYPE_P (TREE_TYPE ((*arglist)[1])))
goto bad;
arg = (*arglist)[0];
type = TREE_TYPE (arg);
if (!SCALAR_FLOAT_TYPE_P (type)
&& !INTEGRAL_TYPE_P (type))
goto bad;
unsigned_p = TYPE_UNSIGNED (type);
switch (TYPE_MODE (type))
{
case E_TImode:
type = unsigned_p ? unsigned_V1TI_type_node : V1TI_type_node;
size = 1;
break;
case E_DImode:
type = unsigned_p ? unsigned_V2DI_type_node : V2DI_type_node;
size = 2;
break;
case E_SImode:
type = unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node;
size = 4;
break;
case E_HImode:
type = unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node;
size = 8;
break;
case E_QImode:
type = unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node;
size = 16;
break;
case E_SFmode:
type = V4SF_type_node;
size = 4;
break;
case E_DFmode:
type = V2DF_type_node;
size = 2;
break;
default:
goto bad;
}
arg = save_expr (fold_convert (TREE_TYPE (type), arg));
vec_alloc (vec, size);
for (i = 0; i < size; i++)
{
constructor_elt elt = {NULL_TREE, arg};
vec->quick_push (elt);
}
return build_constructor (type, vec);
}
/* For now use pointer tricks to do the extraction, unless we are on VSX
extracting a double from a constant offset. */
if (fcode == RS6000_OVLD_VEC_EXTRACT)
{
tree arg1;
tree arg1_type;
tree arg2;
tree arg1_inner_type;
tree decl, stmt;
tree innerptrtype;
machine_mode mode;
/* No second argument. */
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", "vec_extract");
return error_mark_node;
}
arg2 = (*arglist)[1];
arg1 = (*arglist)[0];
arg1_type = TREE_TYPE (arg1);
if (TREE_CODE (arg1_type) != VECTOR_TYPE)
goto bad;
if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
goto bad;
/* See if we can optimize vec_extracts with the current VSX instruction
set. */
mode = TYPE_MODE (arg1_type);
if (VECTOR_MEM_VSX_P (mode))
{
tree call = NULL_TREE;
int nunits = GET_MODE_NUNITS (mode);
arg2 = fold_for_warn (arg2);
/* If the second argument is an integer constant, generate
the built-in code if we can. We need 64-bit and direct
move to extract the small integer vectors. */
if (TREE_CODE (arg2) == INTEGER_CST)
{
wide_int selector = wi::to_wide (arg2);
selector = wi::umod_trunc (selector, nunits);
arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
switch (mode)
{
default:
break;
case E_V1TImode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V1TI];
break;
case E_V2DFmode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V2DF];
break;
case E_V2DImode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V2DI];
break;
case E_V4SFmode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V4SF];
break;
case E_V4SImode:
if (TARGET_DIRECT_MOVE_64BIT)
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V4SI];
break;
case E_V8HImode:
if (TARGET_DIRECT_MOVE_64BIT)
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V8HI];
break;
case E_V16QImode:
if (TARGET_DIRECT_MOVE_64BIT)
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V16QI];
break;
}
}
/* If the second argument is variable, we can optimize it if we are
generating 64-bit code on a machine with direct move. */
else if (TREE_CODE (arg2) != INTEGER_CST && TARGET_DIRECT_MOVE_64BIT)
{
switch (mode)
{
default:
break;
case E_V2DFmode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V2DF];
break;
case E_V2DImode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V2DI];
break;
case E_V4SFmode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V4SF];
break;
case E_V4SImode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V4SI];
break;
case E_V8HImode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V8HI];
break;
case E_V16QImode:
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_EXT_V16QI];
break;
}
}
if (call)
{
tree result = build_call_expr (call, 2, arg1, arg2);
/* Coerce the result to vector element type. May be no-op. */
arg1_inner_type = TREE_TYPE (arg1_type);
result = fold_convert (arg1_inner_type, result);
return result;
}
}
/* Build *(((arg1_inner_type*)&(vector type){arg1})+arg2). */
arg1_inner_type = TREE_TYPE (arg1_type);
tree subp = build_int_cst (TREE_TYPE (arg2),
TYPE_VECTOR_SUBPARTS (arg1_type) - 1);
arg2 = build_binary_op (loc, BIT_AND_EXPR, arg2, subp, 0);
decl = build_decl (loc, VAR_DECL, NULL_TREE, arg1_type);
DECL_EXTERNAL (decl) = 0;
TREE_PUBLIC (decl) = 0;
DECL_CONTEXT (decl) = current_function_decl;
TREE_USED (decl) = 1;
TREE_TYPE (decl) = arg1_type;
TREE_READONLY (decl) = TYPE_READONLY (arg1_type);
if (c_dialect_cxx ())
{
stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1,
NULL_TREE, NULL_TREE);
SET_EXPR_LOCATION (stmt, loc);
}
else
{
DECL_INITIAL (decl) = arg1;
stmt = build1 (DECL_EXPR, arg1_type, decl);
TREE_ADDRESSABLE (decl) = 1;
SET_EXPR_LOCATION (stmt, loc);
stmt = build1 (COMPOUND_LITERAL_EXPR, arg1_type, stmt);
}
innerptrtype = build_pointer_type (arg1_inner_type);
stmt = build_unary_op (loc, ADDR_EXPR, stmt, 0);
stmt = convert (innerptrtype, stmt);
stmt = build_binary_op (loc, PLUS_EXPR, stmt, arg2, 1);
stmt = build_indirect_ref (loc, stmt, RO_NULL);
/* PR83660: We mark this as having side effects so that
downstream in fold_build_cleanup_point_expr () it will get a
CLEANUP_POINT_EXPR. If it does not we can run into an ICE
later in gimplify_cleanup_point_expr (). Potentially this
causes missed optimization because there actually is no side
effect. */
if (c_dialect_cxx ())
TREE_SIDE_EFFECTS (stmt) = 1;
return stmt;
}
/* For now use pointer tricks to do the insertion, unless we are on VSX
inserting a double to a constant offset. */
if (fcode == RS6000_OVLD_VEC_INSERT)
{
tree arg0;
tree arg1;
tree arg2;
tree arg1_type;
tree decl, stmt;
machine_mode mode;
/* No second or third arguments. */
if (nargs != 3)
{
error ("builtin %qs only accepts 3 arguments", "vec_insert");
return error_mark_node;
}
arg0 = (*arglist)[0];
arg1 = (*arglist)[1];
arg1_type = TREE_TYPE (arg1);
arg2 = fold_for_warn ((*arglist)[2]);
if (TREE_CODE (arg1_type) != VECTOR_TYPE)
goto bad;
if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
goto bad;
/* If we can use the VSX xxpermdi instruction, use that for insert. */
mode = TYPE_MODE (arg1_type);
if ((mode == V2DFmode || mode == V2DImode) && VECTOR_UNIT_VSX_P (mode)
&& TREE_CODE (arg2) == INTEGER_CST)
{
wide_int selector = wi::to_wide (arg2);
selector = wi::umod_trunc (selector, 2);
tree call = NULL_TREE;
arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
if (mode == V2DFmode)
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_SET_V2DF];
else if (mode == V2DImode)
call = rs6000_builtin_decls_x[RS6000_BIF_VEC_SET_V2DI];
/* Note, __builtin_vec_insert_<xxx> has vector and scalar types
reversed. */
if (call)
return build_call_expr (call, 3, arg1, arg0, arg2);
}
else if (mode == V1TImode && VECTOR_UNIT_VSX_P (mode)
&& TREE_CODE (arg2) == INTEGER_CST)
{
tree call = rs6000_builtin_decls_x[RS6000_BIF_VEC_SET_V1TI];
wide_int selector = wi::zero(32);
arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
/* Note, __builtin_vec_insert_<xxx> has vector and scalar types
reversed. */
return build_call_expr (call, 3, arg1, arg0, arg2);
}
/* Build *(((arg1_inner_type*)&(vector type){arg1})+arg2) = arg0 with
VIEW_CONVERT_EXPR. i.e.:
D.3192 = v1;
_1 = n & 3;
VIEW_CONVERT_EXPR<int[4]>(D.3192)[_1] = i;
v1 = D.3192;
D.3194 = v1; */
if (TYPE_VECTOR_SUBPARTS (arg1_type) == 1)
arg2 = build_int_cst (TREE_TYPE (arg2), 0);
else
arg2 = build_binary_op (loc, BIT_AND_EXPR, arg2,
build_int_cst (TREE_TYPE (arg2),
TYPE_VECTOR_SUBPARTS (arg1_type)
- 1), 0);
decl = build_decl (loc, VAR_DECL, NULL_TREE, arg1_type);
DECL_EXTERNAL (decl) = 0;
TREE_PUBLIC (decl) = 0;
DECL_CONTEXT (decl) = current_function_decl;
TREE_USED (decl) = 1;
TREE_TYPE (decl) = arg1_type;
TREE_READONLY (decl) = TYPE_READONLY (arg1_type);
TREE_ADDRESSABLE (decl) = 1;
if (c_dialect_cxx ())
{
stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1,
NULL_TREE, NULL_TREE);
SET_EXPR_LOCATION (stmt, loc);
}
else
{
DECL_INITIAL (decl) = arg1;
stmt = build1 (DECL_EXPR, arg1_type, decl);
SET_EXPR_LOCATION (stmt, loc);
stmt = build1 (COMPOUND_LITERAL_EXPR, arg1_type, stmt);
}
if (TARGET_VSX)
{
stmt = build_array_ref (loc, stmt, arg2);
stmt = fold_build2 (MODIFY_EXPR, TREE_TYPE (arg0), stmt,
convert (TREE_TYPE (stmt), arg0));
stmt = build2 (COMPOUND_EXPR, arg1_type, stmt, decl);
}
else
{
tree arg1_inner_type;
tree innerptrtype;
arg1_inner_type = TREE_TYPE (arg1_type);
innerptrtype = build_pointer_type (arg1_inner_type);
stmt = build_unary_op (loc, ADDR_EXPR, stmt, 0);
stmt = convert (innerptrtype, stmt);
stmt = build_binary_op (loc, PLUS_EXPR, stmt, arg2, 1);
stmt = build_indirect_ref (loc, stmt, RO_NULL);
stmt = build2 (MODIFY_EXPR, TREE_TYPE (stmt), stmt,
convert (TREE_TYPE (stmt), arg0));
stmt = build2 (COMPOUND_EXPR, arg1_type, stmt, decl);
}
return stmt;
}
unsigned int n;
for (n = 0;
!VOID_TYPE_P (TREE_VALUE (fnargs)) && n < nargs;
fnargs = TREE_CHAIN (fnargs), n++)
{
tree decl_type = TREE_VALUE (fnargs);
tree arg = (*arglist)[n];
tree type;
if (arg == error_mark_node)
return error_mark_node;
if (n >= MAX_OVLD_ARGS)
abort ();
arg = default_conversion (arg);
/* The C++ front-end converts float * to const void * using
NOP_EXPR<const void *> (NOP_EXPR<void *> (x)). */
type = TREE_TYPE (arg);
if (POINTER_TYPE_P (type)
&& TREE_CODE (arg) == NOP_EXPR
&& lang_hooks.types_compatible_p (TREE_TYPE (arg),
const_ptr_type_node)
&& lang_hooks.types_compatible_p (TREE_TYPE (TREE_OPERAND (arg, 0)),
ptr_type_node))
{
arg = TREE_OPERAND (arg, 0);
type = TREE_TYPE (arg);
}
/* Remove the const from the pointers to simplify the overload
matching further down. */
if (POINTER_TYPE_P (decl_type)
&& POINTER_TYPE_P (type)
&& TYPE_QUALS (TREE_TYPE (type)) != 0)
{
if (TYPE_READONLY (TREE_TYPE (type))
&& !TYPE_READONLY (TREE_TYPE (decl_type)))
warning (0, "passing argument %d of %qE discards const qualifier "
"from pointer target type", n + 1, fndecl);
type = build_qualified_type (TREE_TYPE (type), 0);
type = build_pointer_type (type);
arg = fold_convert (type, arg);
}
/* For RS6000_OVLD_VEC_LXVL, convert any const * to its non constant
equivalent to simplify the overload matching below. */
if (fcode == RS6000_OVLD_VEC_LXVL)
{
if (POINTER_TYPE_P (type)
&& TYPE_READONLY (TREE_TYPE (type)))
{
type = build_qualified_type (TREE_TYPE (type), 0);
type = build_pointer_type (type);
arg = fold_convert (type, arg);
}
}
args[n] = arg;
types[n] = type;
}
/* If the number of arguments did not match the prototype, return NULL
and the generic code will issue the appropriate error message. */
if (!VOID_TYPE_P (TREE_VALUE (fnargs)) || n < nargs)
return NULL;
if (fcode == RS6000_OVLD_VEC_STEP)
{
if (TREE_CODE (types[0]) != VECTOR_TYPE)
goto bad;
return build_int_cst (NULL_TREE, TYPE_VECTOR_SUBPARTS (types[0]));
}
{
bool unsupported_builtin = false;
enum rs6000_gen_builtins overloaded_code;
bool supported = false;
ovlddata *instance = rs6000_overload_info[adj_fcode].first_instance;
gcc_assert (instance != NULL);
/* Need to special case __builtin_cmpb because the overloaded forms
of this function take (unsigned int, unsigned int) or (unsigned
long long int, unsigned long long int). Since C conventions
allow the respective argument types to be implicitly coerced into
each other, the default handling does not provide adequate
discrimination between the desired forms of the function. */
if (fcode == RS6000_OVLD_SCAL_CMPB)
{
machine_mode arg1_mode = TYPE_MODE (types[0]);
machine_mode arg2_mode = TYPE_MODE (types[1]);
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments", "__builtin_cmpb");
return error_mark_node;
}
/* If any supplied arguments are wider than 32 bits, resolve to
64-bit variant of built-in function. */
if (GET_MODE_PRECISION (arg1_mode) > 32
|| GET_MODE_PRECISION (arg2_mode) > 32)
/* Assure all argument and result types are compatible with
the built-in function represented by RS6000_BIF_CMPB. */
overloaded_code = RS6000_BIF_CMPB;
else
/* Assure all argument and result types are compatible with
the built-in function represented by RS6000_BIF_CMPB_32. */
overloaded_code = RS6000_BIF_CMPB_32;
while (instance && instance->bifid != overloaded_code)
instance = instance->next;
gcc_assert (instance != NULL);
tree fntype = rs6000_builtin_info_x[instance->bifid].fntype;
tree parmtype0 = TREE_VALUE (TYPE_ARG_TYPES (fntype));
tree parmtype1 = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (fntype)));
if (rs6000_new_builtin_type_compatible (types[0], parmtype0)
&& rs6000_new_builtin_type_compatible (types[1], parmtype1))
{
if (rs6000_builtin_decl (instance->bifid, false) != error_mark_node
&& rs6000_new_builtin_is_supported (instance->bifid))
{
tree ret_type = TREE_TYPE (instance->fntype);
return altivec_build_new_resolved_builtin (args, n, fntype,
ret_type,
instance->bifid,
fcode);
}
else
unsupported_builtin = true;
}
}
else if (fcode == RS6000_OVLD_VEC_VSIE)
{
machine_mode arg1_mode = TYPE_MODE (types[0]);
if (nargs != 2)
{
error ("builtin %qs only accepts 2 arguments",
"scalar_insert_exp");
return error_mark_node;
}
/* If supplied first argument is wider than 64 bits, resolve to
128-bit variant of built-in function. */
if (GET_MODE_PRECISION (arg1_mode) > 64)
{
/* If first argument is of float variety, choose variant
that expects __ieee128 argument. Otherwise, expect
__int128 argument. */
if (GET_MODE_CLASS (arg1_mode) == MODE_FLOAT)
overloaded_code = RS6000_BIF_VSIEQPF;
else
overloaded_code = RS6000_BIF_VSIEQP;
}
else
{
/* If first argument is of float variety, choose variant
that expects double argument. Otherwise, expect
long long int argument. */
if (GET_MODE_CLASS (arg1_mode) == MODE_FLOAT)
overloaded_code = RS6000_BIF_VSIEDPF;
else
overloaded_code = RS6000_BIF_VSIEDP;
}
while (instance && instance->bifid != overloaded_code)
instance = instance->next;
gcc_assert (instance != NULL);
tree fntype = rs6000_builtin_info_x[instance->bifid].fntype;
tree parmtype0 = TREE_VALUE (TYPE_ARG_TYPES (fntype));
tree parmtype1 = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (fntype)));
if (rs6000_new_builtin_type_compatible (types[0], parmtype0)
&& rs6000_new_builtin_type_compatible (types[1], parmtype1))
{
if (rs6000_builtin_decl (instance->bifid, false) != error_mark_node
&& rs6000_new_builtin_is_supported (instance->bifid))
{
tree ret_type = TREE_TYPE (instance->fntype);
return altivec_build_new_resolved_builtin (args, n, fntype,
ret_type,
instance->bifid,
fcode);
}
else
unsupported_builtin = true;
}
}
else
{
/* Functions with no arguments can have only one overloaded
instance. */
gcc_assert (n > 0 || !instance->next);
for (; instance != NULL; instance = instance->next)
{
bool mismatch = false;
tree nextparm = TYPE_ARG_TYPES (instance->fntype);
for (unsigned int arg_i = 0;
arg_i < nargs && nextparm != NULL;
arg_i++)
{
tree parmtype = TREE_VALUE (nextparm);
if (!rs6000_new_builtin_type_compatible (types[arg_i],
parmtype))
{
mismatch = true;
break;
}
nextparm = TREE_CHAIN (nextparm);
}
if (mismatch)
continue;
supported = rs6000_new_builtin_is_supported (instance->bifid);
if (rs6000_builtin_decl (instance->bifid, false) != error_mark_node
&& supported)
{
tree fntype = rs6000_builtin_info_x[instance->bifid].fntype;
tree ret_type = TREE_TYPE (instance->fntype);
return altivec_build_new_resolved_builtin (args, n, fntype,
ret_type,
instance->bifid,
fcode);
}
else
{
unsupported_builtin = true;
break;
}
}
}
if (unsupported_builtin)
{
const char *name = rs6000_overload_info[adj_fcode].ovld_name;
if (!supported)
{
const char *internal_name
= rs6000_builtin_info_x[instance->bifid].bifname;
/* An error message making reference to the name of the
non-overloaded function has already been issued. Add
clarification of the previous message. */
rich_location richloc (line_table, input_location);
inform (&richloc, "builtin %qs requires builtin %qs",
name, internal_name);
}
else
error ("%qs is not supported in this compiler configuration", name);
return error_mark_node;
}
}
bad:
{
const char *name = rs6000_overload_info[adj_fcode].ovld_name;
error ("invalid parameter combination for AltiVec intrinsic %qs", name);
return error_mark_node;
}
}