1 /* Subroutines used for expanding LoongArch builtins.
2 Copyright (C) 2021-2022 Free Software Foundation, Inc.
3 Contributed by Loongson Ltd.
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
20
21 #define IN_TARGET_CODE 1
22
23 #include "config.h"
24 #include "system.h"
25 #include "coretypes.h"
26 #include "backend.h"
27 #include "target.h"
28 #include "rtl.h"
29 #include "tree.h"
30 #include "memmodel.h"
31 #include "gimple.h"
32 #include "tm_p.h"
33 #include "optabs.h"
34 #include "recog.h"
35 #include "diagnostic.h"
36 #include "fold-const.h"
37 #include "expr.h"
38 #include "langhooks.h"
39
40 /* Macros to create an enumeration identifier for a function prototype. */
41 #define LARCH_FTYPE_NAME1(A, B) LARCH_##A##_FTYPE_##B
42 #define LARCH_FTYPE_NAME2(A, B, C) LARCH_##A##_FTYPE_##B##_##C
43 #define LARCH_FTYPE_NAME3(A, B, C, D) LARCH_##A##_FTYPE_##B##_##C##_##D
44
45 /* Classifies the prototype of a built-in function. */
46 enum loongarch_function_type
47 {
48 #define DEF_LARCH_FTYPE(NARGS, LIST) LARCH_FTYPE_NAME##NARGS LIST,
49 #include "config/loongarch/loongarch-ftypes.def"
50 #undef DEF_LARCH_FTYPE
51 LARCH_MAX_FTYPE_MAX
52 };
53
54 /* Specifies how a built-in function should be converted into rtl. */
55 enum loongarch_builtin_type
56 {
57 /* The function corresponds directly to an .md pattern. The return
58 value is mapped to operand 0 and the arguments are mapped to
59 operands 1 and above. */
60 LARCH_BUILTIN_DIRECT,
61
62 /* The function corresponds directly to an .md pattern. There is no return
63 value and the arguments are mapped to operands 0 and above. */
64 LARCH_BUILTIN_DIRECT_NO_TARGET,
65
66 };
67
68 /* Declare an availability predicate for built-in functions that require
69 * COND to be true. NAME is the main part of the predicate's name. */
70 #define AVAIL_ALL(NAME, COND) \
71 static unsigned int \
72 loongarch_builtin_avail_##NAME (void) \
73 { \
74 return (COND) ? 1 : 0; \
75 }
76
77 static unsigned int
loongarch_builtin_avail_default(void)78 loongarch_builtin_avail_default (void)
79 {
80 return 1;
81 }
82 /* This structure describes a single built-in function. */
83 struct loongarch_builtin_description
84 {
85 /* The code of the main .md file instruction. See loongarch_builtin_type
86 for more information. */
87 enum insn_code icode;
88
89 /* The name of the built-in function. */
90 const char *name;
91
92 /* Specifies how the function should be expanded. */
93 enum loongarch_builtin_type builtin_type;
94
95 /* The function's prototype. */
96 enum loongarch_function_type function_type;
97
98 /* Whether the function is available. */
99 unsigned int (*avail) (void);
100 };
101
102 AVAIL_ALL (hard_float, TARGET_HARD_FLOAT_ABI)
103
104 /* Construct a loongarch_builtin_description from the given arguments.
105
106 INSN is the name of the associated instruction pattern, without the
107 leading CODE_FOR_loongarch_.
108
109 CODE is the floating-point condition code associated with the
110 function. It can be 'f' if the field is not applicable.
111
112 NAME is the name of the function itself, without the leading
113 "__builtin_loongarch_".
114
115 BUILTIN_TYPE and FUNCTION_TYPE are loongarch_builtin_description fields.
116
117 AVAIL is the name of the availability predicate, without the leading
118 loongarch_builtin_avail_. */
119 #define LARCH_BUILTIN(INSN, NAME, BUILTIN_TYPE, FUNCTION_TYPE, AVAIL) \
120 { \
121 CODE_FOR_loongarch_##INSN, "__builtin_loongarch_" NAME, \
122 BUILTIN_TYPE, FUNCTION_TYPE, \
123 loongarch_builtin_avail_##AVAIL \
124 }
125
126 /* Define __builtin_loongarch_<INSN>, which is a LARCH_BUILTIN_DIRECT function
127 mapped to instruction CODE_FOR_loongarch_<INSN>, FUNCTION_TYPE and AVAIL
128 are as for LARCH_BUILTIN. */
129 #define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
130 LARCH_BUILTIN (INSN, #INSN, LARCH_BUILTIN_DIRECT, FUNCTION_TYPE, AVAIL)
131
132 /* Define __builtin_loongarch_<INSN>, which is a LARCH_BUILTIN_DIRECT_NO_TARGET
133 function mapped to instruction CODE_FOR_loongarch_<INSN>, FUNCTION_TYPE
134 and AVAIL are as for LARCH_BUILTIN. */
135 #define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
136 LARCH_BUILTIN (INSN, #INSN, LARCH_BUILTIN_DIRECT_NO_TARGET, \
137 FUNCTION_TYPE, AVAIL)
138
139 static const struct loongarch_builtin_description loongarch_builtins[] = {
140 #define LARCH_MOVFCSR2GR 0
141 DIRECT_BUILTIN (movfcsr2gr, LARCH_USI_FTYPE_UQI, hard_float),
142 #define LARCH_MOVGR2FCSR 1
143 DIRECT_NO_TARGET_BUILTIN (movgr2fcsr, LARCH_VOID_FTYPE_UQI_USI, hard_float),
144
145 DIRECT_NO_TARGET_BUILTIN (cacop_w, LARCH_VOID_FTYPE_USI_USI_SI, default),
146 DIRECT_NO_TARGET_BUILTIN (cacop_d, LARCH_VOID_FTYPE_USI_UDI_SI, default),
147 DIRECT_NO_TARGET_BUILTIN (dbar, LARCH_VOID_FTYPE_USI, default),
148 DIRECT_NO_TARGET_BUILTIN (ibar, LARCH_VOID_FTYPE_USI, default),
149
150 DIRECT_BUILTIN (lddir_d, LARCH_DI_FTYPE_DI_UQI, default),
151 DIRECT_BUILTIN (lddir_w, LARCH_SI_FTYPE_SI_UQI, default),
152 DIRECT_NO_TARGET_BUILTIN (ldpte_d, LARCH_VOID_FTYPE_DI_UQI, default),
153 DIRECT_NO_TARGET_BUILTIN (ldpte_w, LARCH_VOID_FTYPE_SI_UQI, default),
154
155 /* CRC Instrinsic */
156
157 DIRECT_BUILTIN (crc_w_b_w, LARCH_SI_FTYPE_QI_SI, default),
158 DIRECT_BUILTIN (crc_w_h_w, LARCH_SI_FTYPE_HI_SI, default),
159 DIRECT_BUILTIN (crc_w_w_w, LARCH_SI_FTYPE_SI_SI, default),
160 DIRECT_BUILTIN (crc_w_d_w, LARCH_SI_FTYPE_DI_SI, default),
161 DIRECT_BUILTIN (crcc_w_b_w, LARCH_SI_FTYPE_QI_SI, default),
162 DIRECT_BUILTIN (crcc_w_h_w, LARCH_SI_FTYPE_HI_SI, default),
163 DIRECT_BUILTIN (crcc_w_w_w, LARCH_SI_FTYPE_SI_SI, default),
164 DIRECT_BUILTIN (crcc_w_d_w, LARCH_SI_FTYPE_DI_SI, default),
165
166 DIRECT_BUILTIN (csrrd_w, LARCH_USI_FTYPE_USI, default),
167 DIRECT_BUILTIN (csrrd_d, LARCH_UDI_FTYPE_USI, default),
168 DIRECT_BUILTIN (csrwr_w, LARCH_USI_FTYPE_USI_USI, default),
169 DIRECT_BUILTIN (csrwr_d, LARCH_UDI_FTYPE_UDI_USI, default),
170 DIRECT_BUILTIN (csrxchg_w, LARCH_USI_FTYPE_USI_USI_USI, default),
171 DIRECT_BUILTIN (csrxchg_d, LARCH_UDI_FTYPE_UDI_UDI_USI, default),
172 DIRECT_BUILTIN (iocsrrd_b, LARCH_UQI_FTYPE_USI, default),
173 DIRECT_BUILTIN (iocsrrd_h, LARCH_UHI_FTYPE_USI, default),
174 DIRECT_BUILTIN (iocsrrd_w, LARCH_USI_FTYPE_USI, default),
175 DIRECT_BUILTIN (iocsrrd_d, LARCH_UDI_FTYPE_USI, default),
176 DIRECT_NO_TARGET_BUILTIN (iocsrwr_b, LARCH_VOID_FTYPE_UQI_USI, default),
177 DIRECT_NO_TARGET_BUILTIN (iocsrwr_h, LARCH_VOID_FTYPE_UHI_USI, default),
178 DIRECT_NO_TARGET_BUILTIN (iocsrwr_w, LARCH_VOID_FTYPE_USI_USI, default),
179 DIRECT_NO_TARGET_BUILTIN (iocsrwr_d, LARCH_VOID_FTYPE_UDI_USI, default),
180
181 DIRECT_BUILTIN (cpucfg, LARCH_USI_FTYPE_USI, default),
182 DIRECT_NO_TARGET_BUILTIN (asrtle_d, LARCH_VOID_FTYPE_DI_DI, default),
183 DIRECT_NO_TARGET_BUILTIN (asrtgt_d, LARCH_VOID_FTYPE_DI_DI, default),
184 DIRECT_NO_TARGET_BUILTIN (syscall, LARCH_VOID_FTYPE_USI, default),
185 DIRECT_NO_TARGET_BUILTIN (break, LARCH_VOID_FTYPE_USI, default),
186 };
187
188 /* Index I is the function declaration for loongarch_builtins[I], or null if
189 the function isn't defined on this target. */
190 static GTY (()) tree loongarch_builtin_decls[ARRAY_SIZE (loongarch_builtins)];
191 /* Get the index I of the function declaration for loongarch_builtin_decls[I]
192 using the instruction code or return null if not defined for the target. */
193 static GTY (()) int loongarch_get_builtin_decl_index[NUM_INSN_CODES];
194
195 /* Source-level argument types. */
196 #define LARCH_ATYPE_VOID void_type_node
197 #define LARCH_ATYPE_INT integer_type_node
198 #define LARCH_ATYPE_POINTER ptr_type_node
199
200 /* Standard mode-based argument types. */
201 #define LARCH_ATYPE_QI intQI_type_node
202 #define LARCH_ATYPE_UQI unsigned_intQI_type_node
203 #define LARCH_ATYPE_HI intHI_type_node
204 #define LARCH_ATYPE_UHI unsigned_intHI_type_node
205 #define LARCH_ATYPE_SI intSI_type_node
206 #define LARCH_ATYPE_USI unsigned_intSI_type_node
207 #define LARCH_ATYPE_DI intDI_type_node
208 #define LARCH_ATYPE_UDI unsigned_intDI_type_node
209 #define LARCH_ATYPE_SF float_type_node
210 #define LARCH_ATYPE_DF double_type_node
211
212 /* LARCH_FTYPE_ATYPESN takes N LARCH_FTYPES-like type codes and lists
213 their associated LARCH_ATYPEs. */
214 #define LARCH_FTYPE_ATYPES1(A, B) LARCH_ATYPE_##A, LARCH_ATYPE_##B
215
216 #define LARCH_FTYPE_ATYPES2(A, B, C) \
217 LARCH_ATYPE_##A, LARCH_ATYPE_##B, LARCH_ATYPE_##C
218
219 #define LARCH_FTYPE_ATYPES3(A, B, C, D) \
220 LARCH_ATYPE_##A, LARCH_ATYPE_##B, LARCH_ATYPE_##C, LARCH_ATYPE_##D
221
222 #define LARCH_FTYPE_ATYPES4(A, B, C, D, E) \
223 LARCH_ATYPE_##A, LARCH_ATYPE_##B, LARCH_ATYPE_##C, LARCH_ATYPE_##D, \
224 LARCH_ATYPE_##E
225
226 /* Return the function type associated with function prototype TYPE. */
227
228 static tree
loongarch_build_function_type(enum loongarch_function_type type)229 loongarch_build_function_type (enum loongarch_function_type type)
230 {
231 static tree types[(int) LARCH_MAX_FTYPE_MAX];
232
233 if (types[(int) type] == NULL_TREE)
234 switch (type)
235 {
236 #define DEF_LARCH_FTYPE(NUM, ARGS) \
237 case LARCH_FTYPE_NAME##NUM ARGS: \
238 types[(int) type] \
239 = build_function_type_list (LARCH_FTYPE_ATYPES##NUM ARGS, NULL_TREE); \
240 break;
241 #include "config/loongarch/loongarch-ftypes.def"
242 #undef DEF_LARCH_FTYPE
243 default:
244 gcc_unreachable ();
245 }
246
247 return types[(int) type];
248 }
249
250 /* Implement TARGET_INIT_BUILTINS. */
251
252 void
loongarch_init_builtins(void)253 loongarch_init_builtins (void)
254 {
255 const struct loongarch_builtin_description *d;
256 unsigned int i;
257 tree type;
258
259 /* Iterate through all of the bdesc arrays, initializing all of the
260 builtin functions. */
261 for (i = 0; i < ARRAY_SIZE (loongarch_builtins); i++)
262 {
263 d = &loongarch_builtins[i];
264 if (d->avail ())
265 {
266 type = loongarch_build_function_type (d->function_type);
267 loongarch_builtin_decls[i]
268 = add_builtin_function (d->name, type, i, BUILT_IN_MD, NULL,
269 NULL);
270 loongarch_get_builtin_decl_index[d->icode] = i;
271 }
272 }
273 }
274
275 /* Implement TARGET_BUILTIN_DECL. */
276
277 tree
loongarch_builtin_decl(unsigned int code,bool initialize_p ATTRIBUTE_UNUSED)278 loongarch_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED)
279 {
280 if (code >= ARRAY_SIZE (loongarch_builtins))
281 return error_mark_node;
282 return loongarch_builtin_decls[code];
283 }
284
285 /* Take argument ARGNO from EXP's argument list and convert it into
286 an expand operand. Store the operand in *OP. */
287
288 static void
loongarch_prepare_builtin_arg(struct expand_operand * op,tree exp,unsigned int argno)289 loongarch_prepare_builtin_arg (struct expand_operand *op, tree exp,
290 unsigned int argno)
291 {
292 tree arg;
293 rtx value;
294
295 arg = CALL_EXPR_ARG (exp, argno);
296 value = expand_normal (arg);
297 create_input_operand (op, value, TYPE_MODE (TREE_TYPE (arg)));
298 }
299
300 /* Expand instruction ICODE as part of a built-in function sequence.
301 Use the first NOPS elements of OPS as the instruction's operands.
302 HAS_TARGET_P is true if operand 0 is a target; it is false if the
303 instruction has no target.
304
305 Return the target rtx if HAS_TARGET_P, otherwise return const0_rtx. */
306
307 static rtx
loongarch_expand_builtin_insn(enum insn_code icode,unsigned int nops,struct expand_operand * ops,bool has_target_p)308 loongarch_expand_builtin_insn (enum insn_code icode, unsigned int nops,
309 struct expand_operand *ops, bool has_target_p)
310 {
311 if (!maybe_expand_insn (icode, nops, ops))
312 {
313 error ("invalid argument to built-in function");
314 return has_target_p ? gen_reg_rtx (ops[0].mode) : const0_rtx;
315 }
316 return has_target_p ? ops[0].value : const0_rtx;
317 }
318
319 /* Expand a LARCH_BUILTIN_DIRECT or LARCH_BUILTIN_DIRECT_NO_TARGET function;
320 HAS_TARGET_P says which. EXP is the CALL_EXPR that calls the function
321 and ICODE is the code of the associated .md pattern. TARGET, if nonnull,
322 suggests a good place to put the result. */
323
324 static rtx
loongarch_expand_builtin_direct(enum insn_code icode,rtx target,tree exp,bool has_target_p)325 loongarch_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
326 bool has_target_p)
327 {
328 struct expand_operand ops[MAX_RECOG_OPERANDS];
329 int opno, argno;
330
331 /* Map any target to operand 0. */
332 opno = 0;
333 if (has_target_p)
334 create_output_operand (&ops[opno++], target, TYPE_MODE (TREE_TYPE (exp)));
335
336 /* Map the arguments to the other operands. */
337 gcc_assert (opno + call_expr_nargs (exp)
338 == insn_data[icode].n_generator_args);
339 for (argno = 0; argno < call_expr_nargs (exp); argno++)
340 loongarch_prepare_builtin_arg (&ops[opno++], exp, argno);
341
342 return loongarch_expand_builtin_insn (icode, opno, ops, has_target_p);
343 }
344
345 /* Implement TARGET_EXPAND_BUILTIN. */
346
347 rtx
loongarch_expand_builtin(tree exp,rtx target,rtx subtarget ATTRIBUTE_UNUSED,machine_mode mode ATTRIBUTE_UNUSED,int ignore ATTRIBUTE_UNUSED)348 loongarch_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
349 machine_mode mode ATTRIBUTE_UNUSED,
350 int ignore ATTRIBUTE_UNUSED)
351 {
352 tree fndecl;
353 unsigned int fcode, avail;
354 const struct loongarch_builtin_description *d;
355
356 fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
357 fcode = DECL_MD_FUNCTION_CODE (fndecl);
358 gcc_assert (fcode < ARRAY_SIZE (loongarch_builtins));
359 d = &loongarch_builtins[fcode];
360 avail = d->avail ();
361 gcc_assert (avail != 0);
362 switch (d->builtin_type)
363 {
364 case LARCH_BUILTIN_DIRECT:
365 return loongarch_expand_builtin_direct (d->icode, target, exp, true);
366
367 case LARCH_BUILTIN_DIRECT_NO_TARGET:
368 return loongarch_expand_builtin_direct (d->icode, target, exp, false);
369 }
370 gcc_unreachable ();
371 }
372
373 /* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV. */
374
375 void
loongarch_atomic_assign_expand_fenv(tree * hold,tree * clear,tree * update)376 loongarch_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
377 {
378 if (!TARGET_HARD_FLOAT_ABI)
379 return;
380 tree exceptions_var = create_tmp_var_raw (LARCH_ATYPE_USI);
381 tree fcsr_orig_var = create_tmp_var_raw (LARCH_ATYPE_USI);
382 tree fcsr_mod_var = create_tmp_var_raw (LARCH_ATYPE_USI);
383 tree const0 = build_int_cst (LARCH_ATYPE_UQI, 0);
384 tree get_fcsr = loongarch_builtin_decls[LARCH_MOVFCSR2GR];
385 tree set_fcsr = loongarch_builtin_decls[LARCH_MOVGR2FCSR];
386 tree get_fcsr_hold_call = build_call_expr (get_fcsr, 1, const0);
387 tree hold_assign_orig = build4 (TARGET_EXPR, LARCH_ATYPE_USI,
388 fcsr_orig_var, get_fcsr_hold_call,
389 NULL, NULL);
390 tree hold_mod_val = build2 (BIT_AND_EXPR, LARCH_ATYPE_USI, fcsr_orig_var,
391 build_int_cst (LARCH_ATYPE_USI, 0xffe0ffe0));
392 tree hold_assign_mod = build4 (TARGET_EXPR, LARCH_ATYPE_USI,
393 fcsr_mod_var, hold_mod_val, NULL, NULL);
394 tree set_fcsr_hold_call = build_call_expr (set_fcsr, 2, const0,
395 fcsr_mod_var);
396 tree hold_all = build2 (COMPOUND_EXPR, LARCH_ATYPE_USI, hold_assign_orig,
397 hold_assign_mod);
398 *hold = build2 (COMPOUND_EXPR, void_type_node, hold_all, set_fcsr_hold_call);
399
400 *clear = build_call_expr (set_fcsr, 2, const0, fcsr_mod_var);
401
402 tree get_fcsr_update_call = build_call_expr (get_fcsr, 1, const0);
403 *update = build4 (TARGET_EXPR, LARCH_ATYPE_USI, exceptions_var,
404 get_fcsr_update_call, NULL, NULL);
405 tree set_fcsr_update_call = build_call_expr (set_fcsr, 2, const0,
406 fcsr_orig_var);
407 *update = build2 (COMPOUND_EXPR, void_type_node, *update,
408 set_fcsr_update_call);
409 tree atomic_feraiseexcept
410 = builtin_decl_implicit (BUILT_IN_ATOMIC_FERAISEEXCEPT);
411 tree int_exceptions_var = fold_convert (integer_type_node, exceptions_var);
412 tree atomic_feraiseexcept_call = build_call_expr (atomic_feraiseexcept, 1,
413 int_exceptions_var);
414 *update = build2 (COMPOUND_EXPR, void_type_node, *update,
415 atomic_feraiseexcept_call);
416 }
417
418 /* Implement TARGET_BUILTIN_VA_LIST. */
419
420 tree
loongarch_build_builtin_va_list(void)421 loongarch_build_builtin_va_list (void)
422 {
423 return ptr_type_node;
424 }
425