1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
|
/* Subroutines used for expanding RISC-V builtins.
Copyright (C) 2011-2018 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
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 "tm.h"
#include "rtl.h"
#include "tree.h"
#include "gimple-expr.h"
#include "memmodel.h"
#include "expmed.h"
#include "profile-count.h"
#include "optabs.h"
#include "recog.h"
#include "diagnostic-core.h"
#include "stor-layout.h"
#include "expr.h"
#include "langhooks.h"
/* Macros to create an enumeration identifier for a function prototype. */
#define RISCV_FTYPE_NAME1(A, B) RISCV_##A##_FTYPE_##B
/* Classifies the prototype of a built-in function. */
enum riscv_function_type {
#define DEF_RISCV_FTYPE(NARGS, LIST) RISCV_FTYPE_NAME##NARGS LIST,
#include "config/riscv/riscv-ftypes.def"
#undef DEF_RISCV_FTYPE
RISCV_MAX_FTYPE_MAX
};
/* Specifies how a built-in function should be converted into rtl. */
enum riscv_builtin_type {
/* The function corresponds directly to an .md pattern. */
RISCV_BUILTIN_DIRECT,
/* Likewise, but with return type VOID. */
RISCV_BUILTIN_DIRECT_NO_TARGET
};
/* Declare an availability predicate for built-in functions. */
#define AVAIL(NAME, COND) \
static unsigned int \
riscv_builtin_avail_##NAME (void) \
{ \
return (COND); \
}
/* This structure describes a single built-in function. */
struct riscv_builtin_description {
/* The code of the main .md file instruction. See riscv_builtin_type
for more information. */
enum insn_code icode;
/* The name of the built-in function. */
const char *name;
/* Specifies how the function should be expanded. */
enum riscv_builtin_type builtin_type;
/* The function's prototype. */
enum riscv_function_type prototype;
/* Whether the function is available. */
unsigned int (*avail) (void);
};
AVAIL (hard_float, TARGET_HARD_FLOAT)
/* Construct a riscv_builtin_description from the given arguments.
INSN is the name of the associated instruction pattern, without the
leading CODE_FOR_riscv_.
NAME is the name of the function itself, without the leading
"__builtin_riscv_".
BUILTIN_TYPE and FUNCTION_TYPE are riscv_builtin_description fields.
AVAIL is the name of the availability predicate, without the leading
riscv_builtin_avail_. */
#define RISCV_BUILTIN(INSN, NAME, BUILTIN_TYPE, FUNCTION_TYPE, AVAIL) \
{ CODE_FOR_riscv_ ## INSN, "__builtin_riscv_" NAME, \
BUILTIN_TYPE, FUNCTION_TYPE, riscv_builtin_avail_ ## AVAIL }
/* Define __builtin_riscv_<INSN>, which is a RISCV_BUILTIN_DIRECT function
mapped to instruction CODE_FOR_riscv_<INSN>, FUNCTION_TYPE and AVAIL
are as for RISCV_BUILTIN. */
#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
RISCV_BUILTIN (INSN, #INSN, RISCV_BUILTIN_DIRECT, FUNCTION_TYPE, AVAIL)
/* Define __builtin_riscv_<INSN>, which is a RISCV_BUILTIN_DIRECT_NO_TARGET
function mapped to instruction CODE_FOR_riscv_<INSN>, FUNCTION_TYPE
and AVAIL are as for RISCV_BUILTIN. */
#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
RISCV_BUILTIN (INSN, #INSN, RISCV_BUILTIN_DIRECT_NO_TARGET, \
FUNCTION_TYPE, AVAIL)
/* Argument types. */
#define RISCV_ATYPE_VOID void_type_node
#define RISCV_ATYPE_USI unsigned_intSI_type_node
/* RISCV_FTYPE_ATYPESN takes N RISCV_FTYPES-like type codes and lists
their associated RISCV_ATYPEs. */
#define RISCV_FTYPE_ATYPES1(A, B) \
RISCV_ATYPE_##A, RISCV_ATYPE_##B
static const struct riscv_builtin_description riscv_builtins[] = {
DIRECT_BUILTIN (frflags, RISCV_USI_FTYPE_VOID, hard_float),
DIRECT_NO_TARGET_BUILTIN (fsflags, RISCV_VOID_FTYPE_USI, hard_float)
};
/* Index I is the function declaration for riscv_builtins[I], or null if the
function isn't defined on this target. */
static GTY(()) tree riscv_builtin_decls[ARRAY_SIZE (riscv_builtins)];
/* Get the index I of the function declaration for riscv_builtin_decls[I]
using the instruction code or return null if not defined for the target. */
static GTY(()) int riscv_builtin_decl_index[NUM_INSN_CODES];
#define GET_BUILTIN_DECL(CODE) \
riscv_builtin_decls[riscv_builtin_decl_index[(CODE)]]
/* Return the function type associated with function prototype TYPE. */
static tree
riscv_build_function_type (enum riscv_function_type type)
{
static tree types[(int) RISCV_MAX_FTYPE_MAX];
if (types[(int) type] == NULL_TREE)
switch (type)
{
#define DEF_RISCV_FTYPE(NUM, ARGS) \
case RISCV_FTYPE_NAME##NUM ARGS: \
types[(int) type] \
= build_function_type_list (RISCV_FTYPE_ATYPES##NUM ARGS, \
NULL_TREE); \
break;
#include "config/riscv/riscv-ftypes.def"
#undef DEF_RISCV_FTYPE
default:
gcc_unreachable ();
}
return types[(int) type];
}
/* Implement TARGET_INIT_BUILTINS. */
void
riscv_init_builtins (void)
{
for (size_t i = 0; i < ARRAY_SIZE (riscv_builtins); i++)
{
const struct riscv_builtin_description *d = &riscv_builtins[i];
if (d->avail ())
{
tree type = riscv_build_function_type (d->prototype);
riscv_builtin_decls[i]
= add_builtin_function (d->name, type, i, BUILT_IN_MD, NULL, NULL);
riscv_builtin_decl_index[d->icode] = i;
}
}
}
/* Implement TARGET_BUILTIN_DECL. */
tree
riscv_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED)
{
if (code >= ARRAY_SIZE (riscv_builtins))
return error_mark_node;
return riscv_builtin_decls[code];
}
/* Take argument ARGNO from EXP's argument list and convert it into
an expand operand. Store the operand in *OP. */
static void
riscv_prepare_builtin_arg (struct expand_operand *op, tree exp, unsigned argno)
{
tree arg = CALL_EXPR_ARG (exp, argno);
create_input_operand (op, expand_normal (arg), TYPE_MODE (TREE_TYPE (arg)));
}
/* Expand instruction ICODE as part of a built-in function sequence.
Use the first NOPS elements of OPS as the instruction's operands.
HAS_TARGET_P is true if operand 0 is a target; it is false if the
instruction has no target.
Return the target rtx if HAS_TARGET_P, otherwise return const0_rtx. */
static rtx
riscv_expand_builtin_insn (enum insn_code icode, unsigned int n_ops,
struct expand_operand *ops, bool has_target_p)
{
if (!maybe_expand_insn (icode, n_ops, ops))
{
error ("invalid argument to built-in function");
return has_target_p ? gen_reg_rtx (ops[0].mode) : const0_rtx;
}
return has_target_p ? ops[0].value : const0_rtx;
}
/* Expand a RISCV_BUILTIN_DIRECT or RISCV_BUILTIN_DIRECT_NO_TARGET function;
HAS_TARGET_P says which. EXP is the CALL_EXPR that calls the function
and ICODE is the code of the associated .md pattern. TARGET, if nonnull,
suggests a good place to put the result. */
static rtx
riscv_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
bool has_target_p)
{
struct expand_operand ops[MAX_RECOG_OPERANDS];
/* Map any target to operand 0. */
int opno = 0;
if (has_target_p)
create_output_operand (&ops[opno++], target, TYPE_MODE (TREE_TYPE (exp)));
/* Map the arguments to the other operands. */
gcc_assert (opno + call_expr_nargs (exp)
== insn_data[icode].n_generator_args);
for (int argno = 0; argno < call_expr_nargs (exp); argno++)
riscv_prepare_builtin_arg (&ops[opno++], exp, argno);
return riscv_expand_builtin_insn (icode, opno, ops, has_target_p);
}
/* Implement TARGET_EXPAND_BUILTIN. */
rtx
riscv_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
machine_mode mode ATTRIBUTE_UNUSED,
int ignore ATTRIBUTE_UNUSED)
{
tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
const struct riscv_builtin_description *d = &riscv_builtins[fcode];
switch (d->builtin_type)
{
case RISCV_BUILTIN_DIRECT:
return riscv_expand_builtin_direct (d->icode, target, exp, true);
case RISCV_BUILTIN_DIRECT_NO_TARGET:
return riscv_expand_builtin_direct (d->icode, target, exp, false);
}
gcc_unreachable ();
}
/* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV. */
void
riscv_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
{
if (!TARGET_HARD_FLOAT)
return;
tree frflags = GET_BUILTIN_DECL (CODE_FOR_riscv_frflags);
tree fsflags = GET_BUILTIN_DECL (CODE_FOR_riscv_fsflags);
tree old_flags = create_tmp_var_raw (RISCV_ATYPE_USI);
*hold = build2 (MODIFY_EXPR, RISCV_ATYPE_USI, old_flags,
build_call_expr (frflags, 0));
*clear = build_call_expr (fsflags, 1, old_flags);
*update = NULL_TREE;
}
|