xref: /netbsd-src/external/gpl3/gcc/dist/gcc/config/loongarch/loongarch-builtins.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
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