]>
Commit | Line | Data |
---|---|---|
62ec3b53 | 1 | /* Subroutines used for LoongArch code generation. |
2 | Copyright (C) 2021-2022 Free Software Foundation, Inc. | |
3 | Contributed by Loongson Ltd. | |
4 | Based on MIPS and RISC-V target for GNU compiler. | |
5 | ||
6 | This file is part of GCC. | |
7 | ||
8 | GCC is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 3, or (at your option) | |
11 | any later version. | |
12 | ||
13 | GCC is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with GCC; see the file COPYING3. If not see | |
20 | <http://www.gnu.org/licenses/>. */ | |
21 | ||
22 | #define IN_TARGET_CODE 1 | |
23 | ||
24 | #include "config.h" | |
25 | #include "system.h" | |
26 | #include "coretypes.h" | |
27 | #include "backend.h" | |
28 | #include "target.h" | |
29 | #include "rtl.h" | |
30 | #include "tree.h" | |
31 | #include "memmodel.h" | |
32 | #include "gimple.h" | |
33 | #include "cfghooks.h" | |
34 | #include "df.h" | |
35 | #include "tm_p.h" | |
36 | #include "stringpool.h" | |
37 | #include "attribs.h" | |
38 | #include "optabs.h" | |
39 | #include "regs.h" | |
40 | #include "emit-rtl.h" | |
41 | #include "recog.h" | |
42 | #include "cgraph.h" | |
43 | #include "diagnostic.h" | |
44 | #include "insn-attr.h" | |
45 | #include "output.h" | |
46 | #include "alias.h" | |
47 | #include "fold-const.h" | |
48 | #include "varasm.h" | |
49 | #include "stor-layout.h" | |
50 | #include "calls.h" | |
51 | #include "explow.h" | |
52 | #include "expr.h" | |
53 | #include "libfuncs.h" | |
54 | #include "reload.h" | |
55 | #include "common/common-target.h" | |
56 | #include "langhooks.h" | |
57 | #include "cfgrtl.h" | |
58 | #include "cfganal.h" | |
59 | #include "sched-int.h" | |
60 | #include "gimplify.h" | |
61 | #include "target-globals.h" | |
62 | #include "tree-pass.h" | |
63 | #include "context.h" | |
64 | #include "builtins.h" | |
65 | #include "rtl-iter.h" | |
66 | ||
67 | /* This file should be included last. */ | |
68 | #include "target-def.h" | |
69 | ||
70 | /* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF. */ | |
71 | #define UNSPEC_ADDRESS_P(X) \ | |
72 | (GET_CODE (X) == UNSPEC \ | |
73 | && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST \ | |
74 | && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES) | |
75 | ||
76 | /* Extract the symbol or label from UNSPEC wrapper X. */ | |
77 | #define UNSPEC_ADDRESS(X) XVECEXP (X, 0, 0) | |
78 | ||
79 | /* Extract the symbol type from UNSPEC wrapper X. */ | |
80 | #define UNSPEC_ADDRESS_TYPE(X) \ | |
81 | ((enum loongarch_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST)) | |
82 | ||
83 | /* True if INSN is a loongarch.md pattern or asm statement. */ | |
84 | /* ??? This test exists through the compiler, perhaps it should be | |
85 | moved to rtl.h. */ | |
86 | #define USEFUL_INSN_P(INSN) \ | |
87 | (NONDEBUG_INSN_P (INSN) \ | |
88 | && GET_CODE (PATTERN (INSN)) != USE \ | |
89 | && GET_CODE (PATTERN (INSN)) != CLOBBER) | |
90 | ||
91 | /* True if bit BIT is set in VALUE. */ | |
92 | #define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0) | |
93 | ||
94 | /* Classifies an address. | |
95 | ||
96 | ADDRESS_REG | |
97 | A natural register + offset address. The register satisfies | |
98 | loongarch_valid_base_register_p and the offset is a const_arith_operand. | |
99 | ||
100 | ADDRESS_REG_REG | |
101 | A base register indexed by (optionally scaled) register. | |
102 | ||
103 | ADDRESS_CONST_INT | |
104 | A signed 16-bit constant address. | |
105 | ||
106 | ADDRESS_SYMBOLIC: | |
107 | A constant symbolic address. */ | |
108 | enum loongarch_address_type | |
109 | { | |
110 | ADDRESS_REG, | |
111 | ADDRESS_REG_REG, | |
112 | ADDRESS_CONST_INT, | |
113 | ADDRESS_SYMBOLIC | |
114 | }; | |
115 | ||
116 | ||
117 | /* Information about an address described by loongarch_address_type. | |
118 | ||
119 | ADDRESS_CONST_INT | |
120 | No fields are used. | |
121 | ||
122 | ADDRESS_REG | |
123 | REG is the base register and OFFSET is the constant offset. | |
124 | ||
125 | ADDRESS_REG_REG | |
126 | A base register indexed by (optionally scaled) register. | |
127 | ||
128 | ADDRESS_SYMBOLIC | |
129 | SYMBOL_TYPE is the type of symbol that the address references. */ | |
130 | struct loongarch_address_info | |
131 | { | |
132 | enum loongarch_address_type type; | |
133 | rtx reg; | |
134 | rtx offset; | |
135 | enum loongarch_symbol_type symbol_type; | |
136 | }; | |
137 | ||
138 | /* Method of loading instant numbers: | |
139 | ||
140 | METHOD_NORMAL: | |
141 | Load 0-31 bit of the immediate number. | |
142 | ||
143 | METHOD_LU32I: | |
144 | Load 32-51 bit of the immediate number. | |
145 | ||
146 | METHOD_LU52I: | |
147 | Load 52-63 bit of the immediate number. | |
148 | ||
149 | METHOD_INSV: | |
150 | immediate like 0xfff00000fffffxxx | |
151 | */ | |
152 | enum loongarch_load_imm_method | |
153 | { | |
154 | METHOD_NORMAL, | |
155 | METHOD_LU32I, | |
156 | METHOD_LU52I, | |
157 | METHOD_INSV | |
158 | }; | |
159 | ||
160 | struct loongarch_integer_op | |
161 | { | |
162 | enum rtx_code code; | |
163 | unsigned HOST_WIDE_INT value; | |
164 | enum loongarch_load_imm_method method; | |
165 | }; | |
166 | ||
167 | /* The largest number of operations needed to load an integer constant. | |
168 | The worst accepted case for 64-bit constants is LU12I.W,LU32I.D,LU52I.D,ORI | |
169 | or LU12I.W,LU32I.D,LU52I.D,ADDI.D DECL_ASSEMBLER_NAME. */ | |
170 | #define LARCH_MAX_INTEGER_OPS 4 | |
171 | ||
172 | /* Arrays that map GCC register numbers to debugger register numbers. */ | |
173 | int loongarch_dwarf_regno[FIRST_PSEUDO_REGISTER]; | |
174 | ||
175 | /* Index [M][R] is true if register R is allowed to hold a value of mode M. */ | |
176 | static bool loongarch_hard_regno_mode_ok_p[MAX_MACHINE_MODE] | |
177 | [FIRST_PSEUDO_REGISTER]; | |
178 | ||
179 | /* Index C is true if character C is a valid PRINT_OPERAND punctation | |
180 | character. */ | |
181 | static bool loongarch_print_operand_punct[256]; | |
182 | ||
183 | /* Cached value of can_issue_more. This is cached in loongarch_variable_issue | |
184 | hook and returned from loongarch_sched_reorder2. */ | |
185 | static int cached_can_issue_more; | |
186 | ||
187 | /* Index R is the smallest register class that contains register R. */ | |
188 | const enum reg_class loongarch_regno_to_class[FIRST_PSEUDO_REGISTER] = { | |
189 | GR_REGS, GR_REGS, GR_REGS, GR_REGS, | |
190 | JIRL_REGS, JIRL_REGS, JIRL_REGS, JIRL_REGS, | |
191 | JIRL_REGS, JIRL_REGS, JIRL_REGS, JIRL_REGS, | |
020b7d98 | 192 | SIBCALL_REGS, JIRL_REGS, SIBCALL_REGS, SIBCALL_REGS, |
62ec3b53 | 193 | SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, SIBCALL_REGS, |
194 | SIBCALL_REGS, GR_REGS, GR_REGS, JIRL_REGS, | |
195 | JIRL_REGS, JIRL_REGS, JIRL_REGS, JIRL_REGS, | |
196 | JIRL_REGS, JIRL_REGS, JIRL_REGS, JIRL_REGS, | |
197 | ||
198 | FP_REGS, FP_REGS, FP_REGS, FP_REGS, | |
199 | FP_REGS, FP_REGS, FP_REGS, FP_REGS, | |
200 | FP_REGS, FP_REGS, FP_REGS, FP_REGS, | |
201 | FP_REGS, FP_REGS, FP_REGS, FP_REGS, | |
202 | FP_REGS, FP_REGS, FP_REGS, FP_REGS, | |
203 | FP_REGS, FP_REGS, FP_REGS, FP_REGS, | |
204 | FP_REGS, FP_REGS, FP_REGS, FP_REGS, | |
205 | FP_REGS, FP_REGS, FP_REGS, FP_REGS, | |
206 | FCC_REGS, FCC_REGS, FCC_REGS, FCC_REGS, | |
207 | FCC_REGS, FCC_REGS, FCC_REGS, FCC_REGS, | |
208 | FRAME_REGS, FRAME_REGS | |
209 | }; | |
210 | ||
211 | /* Which cost information to use. */ | |
212 | static const struct loongarch_rtx_cost_data *loongarch_cost; | |
213 | ||
214 | /* Information about a single argument. */ | |
215 | struct loongarch_arg_info | |
216 | { | |
217 | /* True if the argument is at least partially passed on the stack. */ | |
218 | bool stack_p; | |
219 | ||
220 | /* The number of integer registers allocated to this argument. */ | |
221 | unsigned int num_gprs; | |
222 | ||
223 | /* The offset of the first register used, provided num_gprs is nonzero. | |
224 | If passed entirely on the stack, the value is MAX_ARGS_IN_REGISTERS. */ | |
225 | unsigned int gpr_offset; | |
226 | ||
227 | /* The number of floating-point registers allocated to this argument. */ | |
228 | unsigned int num_fprs; | |
229 | ||
230 | /* The offset of the first register used, provided num_fprs is nonzero. */ | |
231 | unsigned int fpr_offset; | |
232 | }; | |
233 | ||
234 | /* Invoke MACRO (COND) for each fcmp.cond.{s/d} condition. */ | |
235 | #define LARCH_FP_CONDITIONS(MACRO) \ | |
236 | MACRO (f), \ | |
237 | MACRO (un), \ | |
238 | MACRO (eq), \ | |
239 | MACRO (ueq), \ | |
240 | MACRO (olt), \ | |
241 | MACRO (ult), \ | |
242 | MACRO (ole), \ | |
243 | MACRO (ule), \ | |
244 | MACRO (sf), \ | |
245 | MACRO (ngle), \ | |
246 | MACRO (seq), \ | |
247 | MACRO (ngl), \ | |
248 | MACRO (lt), \ | |
249 | MACRO (nge), \ | |
250 | MACRO (le), \ | |
251 | MACRO (ngt) | |
252 | ||
253 | /* Enumerates the codes above as LARCH_FP_COND_<X>. */ | |
254 | #define DECLARE_LARCH_COND(X) LARCH_FP_COND_##X | |
255 | enum loongarch_fp_condition | |
256 | { | |
257 | LARCH_FP_CONDITIONS (DECLARE_LARCH_COND) | |
258 | }; | |
259 | #undef DECLARE_LARCH_COND | |
260 | ||
261 | /* Index X provides the string representation of LARCH_FP_COND_<X>. */ | |
262 | #define STRINGIFY(X) #X | |
263 | const char *const | |
264 | loongarch_fp_conditions[16]= {LARCH_FP_CONDITIONS (STRINGIFY)}; | |
265 | #undef STRINGIFY | |
266 | ||
267 | /* Implement TARGET_FUNCTION_ARG_BOUNDARY. Every parameter gets at | |
268 | least PARM_BOUNDARY bits of alignment, but will be given anything up | |
269 | to PREFERRED_STACK_BOUNDARY bits if the type requires it. */ | |
270 | ||
271 | static unsigned int | |
272 | loongarch_function_arg_boundary (machine_mode mode, const_tree type) | |
273 | { | |
274 | unsigned int alignment; | |
275 | ||
276 | /* Use natural alignment if the type is not aggregate data. */ | |
277 | if (type && !AGGREGATE_TYPE_P (type)) | |
278 | alignment = TYPE_ALIGN (TYPE_MAIN_VARIANT (type)); | |
279 | else | |
280 | alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode); | |
281 | ||
282 | return MIN (PREFERRED_STACK_BOUNDARY, MAX (PARM_BOUNDARY, alignment)); | |
283 | } | |
284 | ||
285 | /* If MODE represents an argument that can be passed or returned in | |
286 | floating-point registers, return the number of registers, else 0. */ | |
287 | ||
288 | static unsigned | |
289 | loongarch_pass_mode_in_fpr_p (machine_mode mode) | |
290 | { | |
291 | if (GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FP_ARG) | |
292 | { | |
293 | if (GET_MODE_CLASS (mode) == MODE_FLOAT) | |
294 | return 1; | |
295 | ||
296 | if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) | |
297 | return 2; | |
298 | } | |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
303 | typedef struct | |
304 | { | |
305 | const_tree type; | |
306 | HOST_WIDE_INT offset; | |
307 | } loongarch_aggregate_field; | |
308 | ||
309 | /* Identify subfields of aggregates that are candidates for passing in | |
310 | floating-point registers. */ | |
311 | ||
312 | static int | |
313 | loongarch_flatten_aggregate_field (const_tree type, | |
314 | loongarch_aggregate_field fields[2], int n, | |
315 | HOST_WIDE_INT offset) | |
316 | { | |
317 | switch (TREE_CODE (type)) | |
318 | { | |
319 | case RECORD_TYPE: | |
320 | /* Can't handle incomplete types nor sizes that are not fixed. */ | |
321 | if (!COMPLETE_TYPE_P (type) | |
322 | || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST | |
323 | || !tree_fits_uhwi_p (TYPE_SIZE (type))) | |
324 | return -1; | |
325 | ||
326 | for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f)) | |
327 | if (TREE_CODE (f) == FIELD_DECL) | |
328 | { | |
329 | if (!TYPE_P (TREE_TYPE (f))) | |
330 | return -1; | |
331 | ||
d1ee29fd XR |
332 | if (DECL_SIZE (f) && integer_zerop (DECL_SIZE (f))) |
333 | continue; | |
334 | ||
62ec3b53 | 335 | HOST_WIDE_INT pos = offset + int_byte_position (f); |
336 | n = loongarch_flatten_aggregate_field (TREE_TYPE (f), fields, n, | |
337 | pos); | |
338 | if (n < 0) | |
339 | return -1; | |
340 | } | |
341 | return n; | |
342 | ||
343 | case ARRAY_TYPE: | |
344 | { | |
345 | HOST_WIDE_INT n_elts; | |
346 | loongarch_aggregate_field subfields[2]; | |
347 | tree index = TYPE_DOMAIN (type); | |
348 | tree elt_size = TYPE_SIZE_UNIT (TREE_TYPE (type)); | |
349 | int n_subfields = loongarch_flatten_aggregate_field (TREE_TYPE (type), | |
350 | subfields, 0, | |
351 | offset); | |
352 | ||
353 | /* Can't handle incomplete types nor sizes that are not fixed. */ | |
354 | if (n_subfields <= 0 | |
355 | || !COMPLETE_TYPE_P (type) | |
356 | || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST | |
357 | || !index | |
358 | || !TYPE_MAX_VALUE (index) | |
359 | || !tree_fits_uhwi_p (TYPE_MAX_VALUE (index)) | |
360 | || !TYPE_MIN_VALUE (index) | |
361 | || !tree_fits_uhwi_p (TYPE_MIN_VALUE (index)) | |
362 | || !tree_fits_uhwi_p (elt_size)) | |
363 | return -1; | |
364 | ||
365 | n_elts = 1 + tree_to_uhwi (TYPE_MAX_VALUE (index)) | |
366 | - tree_to_uhwi (TYPE_MIN_VALUE (index)); | |
367 | gcc_assert (n_elts >= 0); | |
368 | ||
369 | for (HOST_WIDE_INT i = 0; i < n_elts; i++) | |
370 | for (int j = 0; j < n_subfields; j++) | |
371 | { | |
372 | if (n >= 2) | |
373 | return -1; | |
374 | ||
375 | fields[n] = subfields[j]; | |
376 | fields[n++].offset += i * tree_to_uhwi (elt_size); | |
377 | } | |
378 | ||
379 | return n; | |
380 | } | |
381 | ||
382 | case COMPLEX_TYPE: | |
383 | { | |
384 | /* Complex type need consume 2 field, so n must be 0. */ | |
385 | if (n != 0) | |
386 | return -1; | |
387 | ||
388 | HOST_WIDE_INT elt_size = GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (type))); | |
389 | ||
390 | if (elt_size <= UNITS_PER_FP_ARG) | |
391 | { | |
392 | fields[0].type = TREE_TYPE (type); | |
393 | fields[0].offset = offset; | |
394 | fields[1].type = TREE_TYPE (type); | |
395 | fields[1].offset = offset + elt_size; | |
396 | ||
397 | return 2; | |
398 | } | |
399 | ||
400 | return -1; | |
401 | } | |
402 | ||
403 | default: | |
404 | if (n < 2 | |
405 | && ((SCALAR_FLOAT_TYPE_P (type) | |
406 | && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FP_ARG) | |
407 | || (INTEGRAL_TYPE_P (type) | |
408 | && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_WORD))) | |
409 | { | |
410 | fields[n].type = type; | |
411 | fields[n].offset = offset; | |
412 | return n + 1; | |
413 | } | |
414 | else | |
415 | return -1; | |
416 | } | |
417 | } | |
418 | ||
419 | /* Identify candidate aggregates for passing in floating-point registers. | |
420 | Candidates have at most two fields after flattening. */ | |
421 | ||
422 | static int | |
423 | loongarch_flatten_aggregate_argument (const_tree type, | |
424 | loongarch_aggregate_field fields[2]) | |
425 | { | |
426 | if (!type || TREE_CODE (type) != RECORD_TYPE) | |
427 | return -1; | |
428 | ||
429 | return loongarch_flatten_aggregate_field (type, fields, 0, 0); | |
430 | } | |
431 | ||
432 | /* See whether TYPE is a record whose fields should be returned in one or | |
433 | two floating-point registers. If so, populate FIELDS accordingly. */ | |
434 | ||
435 | static unsigned | |
436 | loongarch_pass_aggregate_num_fpr (const_tree type, | |
437 | loongarch_aggregate_field fields[2]) | |
438 | { | |
439 | int n = loongarch_flatten_aggregate_argument (type, fields); | |
440 | ||
441 | for (int i = 0; i < n; i++) | |
442 | if (!SCALAR_FLOAT_TYPE_P (fields[i].type)) | |
443 | return 0; | |
444 | ||
445 | return n > 0 ? n : 0; | |
446 | } | |
447 | ||
448 | /* See whether TYPE is a record whose fields should be returned in one | |
449 | floating-point register and one integer register. If so, populate | |
450 | FIELDS accordingly. */ | |
451 | ||
452 | static bool | |
453 | loongarch_pass_aggregate_in_fpr_and_gpr_p (const_tree type, | |
454 | loongarch_aggregate_field fields[2]) | |
455 | { | |
456 | unsigned num_int = 0, num_float = 0; | |
457 | int n = loongarch_flatten_aggregate_argument (type, fields); | |
458 | ||
459 | for (int i = 0; i < n; i++) | |
460 | { | |
461 | num_float += SCALAR_FLOAT_TYPE_P (fields[i].type); | |
462 | num_int += INTEGRAL_TYPE_P (fields[i].type); | |
463 | } | |
464 | ||
465 | return num_int == 1 && num_float == 1; | |
466 | } | |
467 | ||
468 | /* Return the representation of an argument passed or returned in an FPR | |
469 | when the value has mode VALUE_MODE and the type has TYPE_MODE. The | |
470 | two modes may be different for structures like: | |
471 | ||
472 | struct __attribute__((packed)) foo { float f; } | |
473 | ||
474 | where the SFmode value "f" is passed in REGNO but the struct itself | |
475 | has mode BLKmode. */ | |
476 | ||
477 | static rtx | |
478 | loongarch_pass_fpr_single (machine_mode type_mode, unsigned regno, | |
6afb21b8 | 479 | machine_mode value_mode, |
480 | HOST_WIDE_INT offset) | |
62ec3b53 | 481 | { |
482 | rtx x = gen_rtx_REG (value_mode, regno); | |
483 | ||
484 | if (type_mode != value_mode) | |
485 | { | |
6afb21b8 | 486 | x = gen_rtx_EXPR_LIST (VOIDmode, x, GEN_INT (offset)); |
62ec3b53 | 487 | x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x)); |
488 | } | |
489 | return x; | |
490 | } | |
491 | ||
492 | /* Pass or return a composite value in the FPR pair REGNO and REGNO + 1. | |
493 | MODE is the mode of the composite. MODE1 and OFFSET1 are the mode and | |
494 | byte offset for the first value, likewise MODE2 and OFFSET2 for the | |
495 | second value. */ | |
496 | ||
497 | static rtx | |
498 | loongarch_pass_fpr_pair (machine_mode mode, unsigned regno1, | |
499 | machine_mode mode1, HOST_WIDE_INT offset1, | |
500 | unsigned regno2, machine_mode mode2, | |
501 | HOST_WIDE_INT offset2) | |
502 | { | |
503 | return gen_rtx_PARALLEL ( | |
504 | mode, gen_rtvec (2, | |
505 | gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode1, regno1), | |
506 | GEN_INT (offset1)), | |
507 | gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode2, regno2), | |
508 | GEN_INT (offset2)))); | |
509 | } | |
510 | ||
511 | /* Fill INFO with information about a single argument, and return an | |
512 | RTL pattern to pass or return the argument. CUM is the cumulative | |
513 | state for earlier arguments. MODE is the mode of this argument and | |
514 | TYPE is its type (if known). NAMED is true if this is a named | |
515 | (fixed) argument rather than a variable one. RETURN_P is true if | |
516 | returning the argument, or false if passing the argument. */ | |
517 | ||
518 | static rtx | |
519 | loongarch_get_arg_info (struct loongarch_arg_info *info, | |
520 | const CUMULATIVE_ARGS *cum, machine_mode mode, | |
521 | const_tree type, bool named, bool return_p) | |
522 | { | |
523 | unsigned num_bytes, num_words; | |
524 | unsigned fpr_base = return_p ? FP_RETURN : FP_ARG_FIRST; | |
525 | unsigned gpr_base = return_p ? GP_RETURN : GP_ARG_FIRST; | |
526 | unsigned alignment = loongarch_function_arg_boundary (mode, type); | |
527 | ||
528 | memset (info, 0, sizeof (*info)); | |
529 | info->gpr_offset = cum->num_gprs; | |
530 | info->fpr_offset = cum->num_fprs; | |
531 | ||
532 | if (named) | |
533 | { | |
534 | loongarch_aggregate_field fields[2]; | |
535 | unsigned fregno = fpr_base + info->fpr_offset; | |
536 | unsigned gregno = gpr_base + info->gpr_offset; | |
537 | ||
538 | /* Pass one- or two-element floating-point aggregates in FPRs. */ | |
539 | if ((info->num_fprs | |
540 | = loongarch_pass_aggregate_num_fpr (type, fields)) | |
541 | && info->fpr_offset + info->num_fprs <= MAX_ARGS_IN_REGISTERS) | |
542 | switch (info->num_fprs) | |
543 | { | |
544 | case 1: | |
545 | return loongarch_pass_fpr_single (mode, fregno, | |
6afb21b8 | 546 | TYPE_MODE (fields[0].type), |
547 | fields[0].offset); | |
62ec3b53 | 548 | |
549 | case 2: | |
550 | return loongarch_pass_fpr_pair (mode, fregno, | |
551 | TYPE_MODE (fields[0].type), | |
552 | fields[0].offset, | |
553 | fregno + 1, | |
554 | TYPE_MODE (fields[1].type), | |
555 | fields[1].offset); | |
556 | ||
557 | default: | |
558 | gcc_unreachable (); | |
559 | } | |
560 | ||
561 | /* Pass real and complex floating-point numbers in FPRs. */ | |
562 | if ((info->num_fprs = loongarch_pass_mode_in_fpr_p (mode)) | |
563 | && info->fpr_offset + info->num_fprs <= MAX_ARGS_IN_REGISTERS) | |
564 | switch (GET_MODE_CLASS (mode)) | |
565 | { | |
566 | case MODE_FLOAT: | |
567 | return gen_rtx_REG (mode, fregno); | |
568 | ||
569 | case MODE_COMPLEX_FLOAT: | |
570 | return loongarch_pass_fpr_pair (mode, fregno, | |
571 | GET_MODE_INNER (mode), 0, | |
572 | fregno + 1, GET_MODE_INNER (mode), | |
573 | GET_MODE_UNIT_SIZE (mode)); | |
574 | ||
575 | default: | |
576 | gcc_unreachable (); | |
577 | } | |
578 | ||
579 | /* Pass structs with one float and one integer in an FPR and a GPR. */ | |
580 | if (loongarch_pass_aggregate_in_fpr_and_gpr_p (type, fields) | |
581 | && info->gpr_offset < MAX_ARGS_IN_REGISTERS | |
582 | && info->fpr_offset < MAX_ARGS_IN_REGISTERS) | |
583 | { | |
584 | info->num_gprs = 1; | |
585 | info->num_fprs = 1; | |
586 | ||
587 | if (!SCALAR_FLOAT_TYPE_P (fields[0].type)) | |
588 | std::swap (fregno, gregno); | |
589 | ||
590 | return loongarch_pass_fpr_pair (mode, fregno, | |
591 | TYPE_MODE (fields[0].type), | |
592 | fields[0].offset, gregno, | |
593 | TYPE_MODE (fields[1].type), | |
594 | fields[1].offset); | |
595 | } | |
596 | } | |
597 | ||
598 | /* Work out the size of the argument. */ | |
599 | num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); | |
600 | num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; | |
601 | ||
602 | /* Doubleword-aligned varargs start on an even register boundary. */ | |
603 | if (!named && num_bytes != 0 && alignment > BITS_PER_WORD) | |
604 | info->gpr_offset += info->gpr_offset & 1; | |
605 | ||
606 | /* Partition the argument between registers and stack. */ | |
607 | info->num_fprs = 0; | |
608 | info->num_gprs = MIN (num_words, MAX_ARGS_IN_REGISTERS - info->gpr_offset); | |
609 | info->stack_p = (num_words - info->num_gprs) != 0; | |
610 | ||
611 | if (info->num_gprs || return_p) | |
612 | return gen_rtx_REG (mode, gpr_base + info->gpr_offset); | |
613 | ||
614 | return NULL_RTX; | |
615 | } | |
616 | ||
617 | /* Implement TARGET_FUNCTION_ARG. */ | |
618 | ||
619 | static rtx | |
620 | loongarch_function_arg (cumulative_args_t cum_v, const function_arg_info &arg) | |
621 | { | |
622 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); | |
623 | struct loongarch_arg_info info; | |
624 | ||
625 | if (arg.end_marker_p ()) | |
626 | return NULL; | |
627 | ||
628 | return loongarch_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, | |
629 | false); | |
630 | } | |
631 | ||
632 | /* Implement TARGET_FUNCTION_ARG_ADVANCE. */ | |
633 | ||
634 | static void | |
635 | loongarch_function_arg_advance (cumulative_args_t cum_v, | |
636 | const function_arg_info &arg) | |
637 | { | |
638 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); | |
639 | struct loongarch_arg_info info; | |
640 | ||
641 | loongarch_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, false); | |
642 | ||
643 | /* Advance the register count. This has the effect of setting | |
644 | num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned | |
645 | argument required us to skip the final GPR and pass the whole | |
646 | argument on the stack. */ | |
647 | cum->num_fprs = info.fpr_offset + info.num_fprs; | |
648 | cum->num_gprs = info.gpr_offset + info.num_gprs; | |
649 | } | |
650 | ||
651 | /* Implement TARGET_ARG_PARTIAL_BYTES. */ | |
652 | ||
653 | static int | |
654 | loongarch_arg_partial_bytes (cumulative_args_t cum, | |
655 | const function_arg_info &generic_arg) | |
656 | { | |
657 | struct loongarch_arg_info arg; | |
658 | ||
659 | loongarch_get_arg_info (&arg, get_cumulative_args (cum), generic_arg.mode, | |
660 | generic_arg.type, generic_arg.named, false); | |
661 | return arg.stack_p ? arg.num_gprs * UNITS_PER_WORD : 0; | |
662 | } | |
663 | ||
664 | /* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls, | |
665 | VALTYPE is the return type and MODE is VOIDmode. For libcalls, | |
666 | VALTYPE is null and MODE is the mode of the return value. */ | |
667 | ||
668 | static rtx | |
669 | loongarch_function_value_1 (const_tree type, const_tree func, | |
670 | machine_mode mode) | |
671 | { | |
672 | struct loongarch_arg_info info; | |
673 | CUMULATIVE_ARGS args; | |
674 | ||
675 | if (type) | |
676 | { | |
677 | int unsigned_p = TYPE_UNSIGNED (type); | |
678 | ||
679 | mode = TYPE_MODE (type); | |
680 | ||
681 | /* Since TARGET_PROMOTE_FUNCTION_MODE unconditionally promotes, | |
682 | return values, promote the mode here too. */ | |
683 | mode = promote_function_mode (type, mode, &unsigned_p, func, 1); | |
684 | } | |
685 | ||
686 | memset (&args, 0, sizeof (args)); | |
687 | return loongarch_get_arg_info (&info, &args, mode, type, true, true); | |
688 | } | |
689 | ||
690 | ||
691 | /* Implement TARGET_FUNCTION_VALUE. */ | |
692 | ||
693 | static rtx | |
694 | loongarch_function_value (const_tree valtype, const_tree fn_decl_or_type, | |
695 | bool outgoing ATTRIBUTE_UNUSED) | |
696 | { | |
697 | return loongarch_function_value_1 (valtype, fn_decl_or_type, VOIDmode); | |
698 | } | |
699 | ||
700 | /* Implement TARGET_LIBCALL_VALUE. */ | |
701 | ||
702 | static rtx | |
703 | loongarch_libcall_value (machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED) | |
704 | { | |
705 | return loongarch_function_value_1 (NULL_TREE, NULL_TREE, mode); | |
706 | } | |
707 | ||
708 | ||
709 | /* Implement TARGET_PASS_BY_REFERENCE. */ | |
710 | ||
711 | static bool | |
712 | loongarch_pass_by_reference (cumulative_args_t cum_v, | |
713 | const function_arg_info &arg) | |
714 | { | |
715 | HOST_WIDE_INT size = arg.type_size_in_bytes (); | |
716 | struct loongarch_arg_info info; | |
717 | CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); | |
718 | ||
719 | /* ??? std_gimplify_va_arg_expr passes NULL for cum. Fortunately, we | |
720 | never pass variadic arguments in floating-point registers, so we can | |
721 | avoid the call to loongarch_get_arg_info in this case. */ | |
722 | if (cum != NULL) | |
723 | { | |
724 | /* Don't pass by reference if we can use a floating-point register. */ | |
725 | loongarch_get_arg_info (&info, cum, arg.mode, arg.type, arg.named, | |
726 | false); | |
727 | if (info.num_fprs) | |
728 | return false; | |
729 | } | |
730 | ||
731 | /* Pass by reference if the data do not fit in two integer registers. */ | |
732 | return !IN_RANGE (size, 0, 2 * UNITS_PER_WORD); | |
733 | } | |
734 | ||
735 | /* Implement TARGET_RETURN_IN_MEMORY. */ | |
736 | ||
737 | static bool | |
738 | loongarch_return_in_memory (const_tree type, | |
739 | const_tree fndecl ATTRIBUTE_UNUSED) | |
740 | { | |
741 | CUMULATIVE_ARGS args; | |
742 | cumulative_args_t cum = pack_cumulative_args (&args); | |
743 | ||
744 | /* The rules for returning in memory are the same as for passing the | |
745 | first named argument by reference. */ | |
746 | memset (&args, 0, sizeof (args)); | |
747 | function_arg_info arg (const_cast<tree> (type), /*named=*/true); | |
748 | return loongarch_pass_by_reference (cum, arg); | |
749 | } | |
750 | ||
751 | /* Implement TARGET_SETUP_INCOMING_VARARGS. */ | |
752 | ||
753 | static void | |
754 | loongarch_setup_incoming_varargs (cumulative_args_t cum, | |
755 | const function_arg_info &arg, | |
756 | int *pretend_size ATTRIBUTE_UNUSED, | |
757 | int no_rtl) | |
758 | { | |
759 | CUMULATIVE_ARGS local_cum; | |
760 | int gp_saved; | |
761 | ||
762 | /* The caller has advanced CUM up to, but not beyond, the last named | |
763 | argument. Advance a local copy of CUM past the last "real" named | |
764 | argument, to find out how many registers are left over. */ | |
765 | local_cum = *get_cumulative_args (cum); | |
766 | loongarch_function_arg_advance (pack_cumulative_args (&local_cum), arg); | |
767 | ||
768 | /* Found out how many registers we need to save. */ | |
769 | gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs; | |
770 | ||
771 | if (!no_rtl && gp_saved > 0) | |
772 | { | |
773 | rtx ptr = plus_constant (Pmode, virtual_incoming_args_rtx, | |
774 | REG_PARM_STACK_SPACE (cfun->decl) | |
775 | - gp_saved * UNITS_PER_WORD); | |
776 | rtx mem = gen_frame_mem (BLKmode, ptr); | |
777 | set_mem_alias_set (mem, get_varargs_alias_set ()); | |
778 | ||
779 | move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST, mem, gp_saved); | |
780 | } | |
781 | if (REG_PARM_STACK_SPACE (cfun->decl) == 0) | |
782 | cfun->machine->varargs_size = gp_saved * UNITS_PER_WORD; | |
783 | } | |
784 | ||
785 | /* Make the last instruction frame-related and note that it performs | |
786 | the operation described by FRAME_PATTERN. */ | |
787 | ||
788 | static void | |
789 | loongarch_set_frame_expr (rtx frame_pattern) | |
790 | { | |
791 | rtx insn; | |
792 | ||
793 | insn = get_last_insn (); | |
794 | RTX_FRAME_RELATED_P (insn) = 1; | |
795 | REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, frame_pattern, | |
796 | REG_NOTES (insn)); | |
797 | } | |
798 | ||
799 | /* Return a frame-related rtx that stores REG at MEM. | |
800 | REG must be a single register. */ | |
801 | ||
802 | static rtx | |
803 | loongarch_frame_set (rtx mem, rtx reg) | |
804 | { | |
805 | rtx set = gen_rtx_SET (mem, reg); | |
806 | RTX_FRAME_RELATED_P (set) = 1; | |
807 | return set; | |
808 | } | |
809 | ||
810 | /* Return true if the current function must save register REGNO. */ | |
811 | ||
812 | static bool | |
813 | loongarch_save_reg_p (unsigned int regno) | |
814 | { | |
815 | bool call_saved = !global_regs[regno] && !call_used_regs[regno]; | |
816 | bool might_clobber | |
817 | = crtl->saves_all_registers || df_regs_ever_live_p (regno); | |
818 | ||
819 | if (call_saved && might_clobber) | |
820 | return true; | |
821 | ||
822 | if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) | |
823 | return true; | |
824 | ||
825 | if (regno == RETURN_ADDR_REGNUM && crtl->calls_eh_return) | |
826 | return true; | |
827 | ||
828 | return false; | |
829 | } | |
830 | ||
831 | /* Determine which GPR save/restore routine to call. */ | |
832 | ||
833 | static unsigned | |
834 | loongarch_save_libcall_count (unsigned mask) | |
835 | { | |
836 | for (unsigned n = GP_REG_LAST; n > GP_REG_FIRST; n--) | |
837 | if (BITSET_P (mask, n)) | |
838 | return CALLEE_SAVED_REG_NUMBER (n) + 1; | |
839 | abort (); | |
840 | } | |
841 | ||
842 | /* Populate the current function's loongarch_frame_info structure. | |
843 | ||
844 | LoongArch stack frames grown downward. High addresses are at the top. | |
845 | ||
846 | +-------------------------------+ | |
847 | | | | |
848 | | incoming stack arguments | | |
849 | | | | |
850 | +-------------------------------+ <-- incoming stack pointer | |
851 | | | | |
852 | | callee-allocated save area | | |
853 | | for arguments that are | | |
854 | | split between registers and | | |
855 | | the stack | | |
856 | | | | |
857 | +-------------------------------+ <-- arg_pointer_rtx (virtual) | |
858 | | | | |
859 | | callee-allocated save area | | |
860 | | for register varargs | | |
861 | | | | |
862 | +-------------------------------+ <-- hard_frame_pointer_rtx; | |
863 | | | stack_pointer_rtx + gp_sp_offset | |
864 | | GPR save area | + UNITS_PER_WORD | |
865 | | | | |
866 | +-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset | |
867 | | | + UNITS_PER_HWVALUE | |
868 | | FPR save area | | |
869 | | | | |
870 | +-------------------------------+ <-- frame_pointer_rtx (virtual) | |
871 | | | | |
872 | | local variables | | |
873 | | | | |
874 | P +-------------------------------+ | |
875 | | | | |
876 | | outgoing stack arguments | | |
877 | | | | |
878 | +-------------------------------+ <-- stack_pointer_rtx | |
879 | ||
880 | Dynamic stack allocations such as alloca insert data at point P. | |
881 | They decrease stack_pointer_rtx but leave frame_pointer_rtx and | |
882 | hard_frame_pointer_rtx unchanged. */ | |
883 | ||
884 | static void | |
885 | loongarch_compute_frame_info (void) | |
886 | { | |
887 | struct loongarch_frame_info *frame; | |
888 | HOST_WIDE_INT offset; | |
889 | unsigned int regno, i, num_x_saved = 0, num_f_saved = 0; | |
890 | ||
891 | frame = &cfun->machine->frame; | |
892 | memset (frame, 0, sizeof (*frame)); | |
893 | ||
894 | /* Find out which GPRs we need to save. */ | |
895 | for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) | |
896 | if (loongarch_save_reg_p (regno)) | |
897 | frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++; | |
898 | ||
899 | /* If this function calls eh_return, we must also save and restore the | |
900 | EH data registers. */ | |
901 | if (crtl->calls_eh_return) | |
902 | for (i = 0; (regno = EH_RETURN_DATA_REGNO (i)) != INVALID_REGNUM; i++) | |
903 | frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++; | |
904 | ||
905 | /* Find out which FPRs we need to save. This loop must iterate over | |
906 | the same space as its companion in loongarch_for_each_saved_reg. */ | |
907 | if (TARGET_HARD_FLOAT) | |
908 | for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) | |
909 | if (loongarch_save_reg_p (regno)) | |
910 | frame->fmask |= 1 << (regno - FP_REG_FIRST), num_f_saved++; | |
911 | ||
912 | /* At the bottom of the frame are any outgoing stack arguments. */ | |
913 | offset = LARCH_STACK_ALIGN (crtl->outgoing_args_size); | |
914 | /* Next are local stack variables. */ | |
915 | offset += LARCH_STACK_ALIGN (get_frame_size ()); | |
916 | /* The virtual frame pointer points above the local variables. */ | |
917 | frame->frame_pointer_offset = offset; | |
918 | /* Next are the callee-saved FPRs. */ | |
919 | if (frame->fmask) | |
920 | offset += LARCH_STACK_ALIGN (num_f_saved * UNITS_PER_FP_REG); | |
921 | frame->fp_sp_offset = offset - UNITS_PER_FP_REG; | |
922 | /* Next are the callee-saved GPRs. */ | |
923 | if (frame->mask) | |
924 | { | |
925 | unsigned x_save_size = LARCH_STACK_ALIGN (num_x_saved * UNITS_PER_WORD); | |
926 | unsigned num_save_restore | |
927 | = 1 + loongarch_save_libcall_count (frame->mask); | |
928 | ||
929 | /* Only use save/restore routines if they don't alter the stack size. */ | |
930 | if (LARCH_STACK_ALIGN (num_save_restore * UNITS_PER_WORD) == x_save_size) | |
931 | frame->save_libcall_adjustment = x_save_size; | |
932 | ||
933 | offset += x_save_size; | |
934 | } | |
935 | frame->gp_sp_offset = offset - UNITS_PER_WORD; | |
936 | /* The hard frame pointer points above the callee-saved GPRs. */ | |
937 | frame->hard_frame_pointer_offset = offset; | |
938 | /* Above the hard frame pointer is the callee-allocated varags save area. */ | |
939 | offset += LARCH_STACK_ALIGN (cfun->machine->varargs_size); | |
940 | /* Next is the callee-allocated area for pretend stack arguments. */ | |
941 | offset += LARCH_STACK_ALIGN (crtl->args.pretend_args_size); | |
942 | /* Arg pointer must be below pretend args, but must be above alignment | |
943 | padding. */ | |
944 | frame->arg_pointer_offset = offset - crtl->args.pretend_args_size; | |
945 | frame->total_size = offset; | |
946 | /* Next points the incoming stack pointer and any incoming arguments. */ | |
947 | ||
948 | /* Only use save/restore routines when the GPRs are atop the frame. */ | |
949 | if (frame->hard_frame_pointer_offset != frame->total_size) | |
950 | frame->save_libcall_adjustment = 0; | |
951 | } | |
952 | ||
953 | /* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame pointer | |
954 | or argument pointer. TO is either the stack pointer or hard frame | |
955 | pointer. */ | |
956 | ||
957 | HOST_WIDE_INT | |
958 | loongarch_initial_elimination_offset (int from, int to) | |
959 | { | |
960 | HOST_WIDE_INT src, dest; | |
961 | ||
962 | loongarch_compute_frame_info (); | |
963 | ||
964 | if (to == HARD_FRAME_POINTER_REGNUM) | |
965 | dest = cfun->machine->frame.hard_frame_pointer_offset; | |
966 | else if (to == STACK_POINTER_REGNUM) | |
967 | dest = 0; /* The stack pointer is the base of all offsets, hence 0. */ | |
968 | else | |
969 | gcc_unreachable (); | |
970 | ||
971 | if (from == FRAME_POINTER_REGNUM) | |
972 | src = cfun->machine->frame.frame_pointer_offset; | |
973 | else if (from == ARG_POINTER_REGNUM) | |
974 | src = cfun->machine->frame.arg_pointer_offset; | |
975 | else | |
976 | gcc_unreachable (); | |
977 | ||
978 | return src - dest; | |
979 | } | |
980 | ||
981 | /* A function to save or store a register. The first argument is the | |
982 | register and the second is the stack slot. */ | |
983 | typedef void (*loongarch_save_restore_fn) (rtx, rtx); | |
984 | ||
985 | /* Use FN to save or restore register REGNO. MODE is the register's | |
986 | mode and OFFSET is the offset of its save slot from the current | |
987 | stack pointer. */ | |
988 | ||
989 | static void | |
990 | loongarch_save_restore_reg (machine_mode mode, int regno, HOST_WIDE_INT offset, | |
991 | loongarch_save_restore_fn fn) | |
992 | { | |
993 | rtx mem; | |
994 | ||
995 | mem = gen_frame_mem (mode, plus_constant (Pmode, stack_pointer_rtx, offset)); | |
996 | fn (gen_rtx_REG (mode, regno), mem); | |
997 | } | |
998 | ||
999 | /* Call FN for each register that is saved by the current function. | |
1000 | SP_OFFSET is the offset of the current stack pointer from the start | |
1001 | of the frame. */ | |
1002 | ||
1003 | static void | |
1004 | loongarch_for_each_saved_reg (HOST_WIDE_INT sp_offset, | |
1005 | loongarch_save_restore_fn fn) | |
1006 | { | |
1007 | HOST_WIDE_INT offset; | |
1008 | ||
1009 | /* Save the link register and s-registers. */ | |
1010 | offset = cfun->machine->frame.gp_sp_offset - sp_offset; | |
1011 | for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) | |
1012 | if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST)) | |
1013 | { | |
1014 | loongarch_save_restore_reg (word_mode, regno, offset, fn); | |
1015 | offset -= UNITS_PER_WORD; | |
1016 | } | |
1017 | ||
1018 | /* This loop must iterate over the same space as its companion in | |
1019 | loongarch_compute_frame_info. */ | |
1020 | offset = cfun->machine->frame.fp_sp_offset - sp_offset; | |
1021 | for (int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++) | |
1022 | if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST)) | |
1023 | { | |
1024 | machine_mode mode = TARGET_DOUBLE_FLOAT ? DFmode : SFmode; | |
1025 | ||
1026 | loongarch_save_restore_reg (mode, regno, offset, fn); | |
1027 | offset -= GET_MODE_SIZE (mode); | |
1028 | } | |
1029 | } | |
1030 | ||
1031 | /* Emit a move from SRC to DEST. Assume that the move expanders can | |
1032 | handle all moves if !can_create_pseudo_p (). The distinction is | |
1033 | important because, unlike emit_move_insn, the move expanders know | |
1034 | how to force Pmode objects into the constant pool even when the | |
1035 | constant pool address is not itself legitimate. */ | |
1036 | ||
1037 | rtx | |
1038 | loongarch_emit_move (rtx dest, rtx src) | |
1039 | { | |
1040 | return (can_create_pseudo_p () ? emit_move_insn (dest, src) | |
1041 | : emit_move_insn_1 (dest, src)); | |
1042 | } | |
1043 | ||
1044 | /* Save register REG to MEM. Make the instruction frame-related. */ | |
1045 | ||
1046 | static void | |
1047 | loongarch_save_reg (rtx reg, rtx mem) | |
1048 | { | |
1049 | loongarch_emit_move (mem, reg); | |
1050 | loongarch_set_frame_expr (loongarch_frame_set (mem, reg)); | |
1051 | } | |
1052 | ||
1053 | /* Restore register REG from MEM. */ | |
1054 | ||
1055 | static void | |
1056 | loongarch_restore_reg (rtx reg, rtx mem) | |
1057 | { | |
1058 | rtx insn = loongarch_emit_move (reg, mem); | |
1059 | rtx dwarf = NULL_RTX; | |
1060 | dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf); | |
1061 | REG_NOTES (insn) = dwarf; | |
1062 | ||
1063 | RTX_FRAME_RELATED_P (insn) = 1; | |
1064 | } | |
1065 | ||
1066 | /* For stack frames that can't be allocated with a single ADDI instruction, | |
1067 | compute the best value to initially allocate. It must at a minimum | |
1068 | allocate enough space to spill the callee-saved registers. */ | |
1069 | ||
1070 | static HOST_WIDE_INT | |
1071 | loongarch_first_stack_step (struct loongarch_frame_info *frame) | |
1072 | { | |
1073 | if (IMM12_OPERAND (frame->total_size)) | |
1074 | return frame->total_size; | |
1075 | ||
1076 | HOST_WIDE_INT min_first_step | |
1077 | = LARCH_STACK_ALIGN (frame->total_size - frame->fp_sp_offset); | |
1078 | HOST_WIDE_INT max_first_step = IMM_REACH / 2 - PREFERRED_STACK_BOUNDARY / 8; | |
1079 | HOST_WIDE_INT min_second_step = frame->total_size - max_first_step; | |
1080 | gcc_assert (min_first_step <= max_first_step); | |
1081 | ||
1082 | /* As an optimization, use the least-significant bits of the total frame | |
1083 | size, so that the second adjustment step is just LU12I + ADD. */ | |
1084 | if (!IMM12_OPERAND (min_second_step) | |
1085 | && frame->total_size % IMM_REACH < IMM_REACH / 2 | |
1086 | && frame->total_size % IMM_REACH >= min_first_step) | |
1087 | return frame->total_size % IMM_REACH; | |
1088 | ||
1089 | return max_first_step; | |
1090 | } | |
1091 | ||
1092 | static void | |
1093 | loongarch_emit_stack_tie (void) | |
1094 | { | |
1095 | emit_insn (gen_stack_tie (Pmode, stack_pointer_rtx, hard_frame_pointer_rtx)); | |
1096 | } | |
1097 | ||
1098 | #define PROBE_INTERVAL (1 << STACK_CHECK_PROBE_INTERVAL_EXP) | |
1099 | ||
1100 | #if PROBE_INTERVAL > 16384 | |
1101 | #error Cannot use indexed addressing mode for stack probing | |
1102 | #endif | |
1103 | ||
1104 | /* Emit code to probe a range of stack addresses from FIRST to FIRST+SIZE, | |
1105 | inclusive. These are offsets from the current stack pointer. */ | |
1106 | ||
1107 | static void | |
1108 | loongarch_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size) | |
1109 | { | |
1110 | /* See if we have a constant small number of probes to generate. If so, | |
1111 | that's the easy case. */ | |
1112 | if ((TARGET_64BIT && (first + size <= 32768)) | |
1113 | || (!TARGET_64BIT && (first + size <= 2048))) | |
1114 | { | |
1115 | HOST_WIDE_INT i; | |
1116 | ||
1117 | /* Probe at FIRST + N * PROBE_INTERVAL for values of N from 1 until | |
1118 | it exceeds SIZE. If only one probe is needed, this will not | |
1119 | generate any code. Then probe at FIRST + SIZE. */ | |
1120 | for (i = PROBE_INTERVAL; i < size; i += PROBE_INTERVAL) | |
1121 | emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx, | |
1122 | -(first + i))); | |
1123 | ||
1124 | emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx, | |
1125 | -(first + size))); | |
1126 | } | |
1127 | ||
1128 | /* Otherwise, do the same as above, but in a loop. Note that we must be | |
1129 | extra careful with variables wrapping around because we might be at | |
1130 | the very top (or the very bottom) of the address space and we have | |
1131 | to be able to handle this case properly; in particular, we use an | |
1132 | equality test for the loop condition. */ | |
1133 | else | |
1134 | { | |
1135 | HOST_WIDE_INT rounded_size; | |
1136 | rtx r13 = LARCH_PROLOGUE_TEMP (Pmode); | |
1137 | rtx r12 = LARCH_PROLOGUE_TEMP2 (Pmode); | |
1138 | rtx r14 = LARCH_PROLOGUE_TEMP3 (Pmode); | |
1139 | ||
1140 | /* Sanity check for the addressing mode we're going to use. */ | |
1141 | gcc_assert (first <= 16384); | |
1142 | ||
1143 | ||
1144 | /* Step 1: round SIZE to the previous multiple of the interval. */ | |
1145 | ||
1146 | rounded_size = ROUND_DOWN (size, PROBE_INTERVAL); | |
1147 | ||
1148 | /* TEST_ADDR = SP + FIRST */ | |
1149 | if (first != 0) | |
1150 | { | |
1151 | emit_move_insn (r14, GEN_INT (first)); | |
1152 | emit_insn (gen_rtx_SET (r13, gen_rtx_MINUS (Pmode, | |
1153 | stack_pointer_rtx, | |
1154 | r14))); | |
1155 | } | |
1156 | else | |
1157 | emit_move_insn (r13, stack_pointer_rtx); | |
1158 | ||
1159 | /* Step 2: compute initial and final value of the loop counter. */ | |
1160 | ||
1161 | emit_move_insn (r14, GEN_INT (PROBE_INTERVAL)); | |
1162 | /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE. */ | |
1163 | if (rounded_size == 0) | |
1164 | emit_move_insn (r12, r13); | |
1165 | else | |
1166 | { | |
1167 | emit_move_insn (r12, GEN_INT (rounded_size)); | |
1168 | emit_insn (gen_rtx_SET (r12, gen_rtx_MINUS (Pmode, r13, r12))); | |
1169 | /* Step 3: the loop | |
1170 | ||
1171 | do | |
1172 | { | |
1173 | TEST_ADDR = TEST_ADDR + PROBE_INTERVAL | |
1174 | probe at TEST_ADDR | |
1175 | } | |
1176 | while (TEST_ADDR != LAST_ADDR) | |
1177 | ||
1178 | probes at FIRST + N * PROBE_INTERVAL for values of N from 1 | |
1179 | until it is equal to ROUNDED_SIZE. */ | |
1180 | ||
1181 | emit_insn (gen_probe_stack_range (Pmode, r13, r13, r12, r14)); | |
1182 | } | |
1183 | ||
1184 | /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time | |
1185 | that SIZE is equal to ROUNDED_SIZE. */ | |
1186 | ||
1187 | if (size != rounded_size) | |
1188 | { | |
1189 | if (TARGET_64BIT) | |
1190 | emit_stack_probe (plus_constant (Pmode, r12, rounded_size - size)); | |
1191 | else | |
1192 | { | |
1193 | HOST_WIDE_INT i; | |
1194 | for (i = 2048; i < (size - rounded_size); i += 2048) | |
1195 | { | |
1196 | emit_stack_probe (plus_constant (Pmode, r12, -i)); | |
1197 | emit_insn (gen_rtx_SET (r12, | |
1198 | plus_constant (Pmode, r12, -2048))); | |
1199 | } | |
1200 | rtx r1 = plus_constant (Pmode, r12, | |
1201 | -(size - rounded_size - i + 2048)); | |
1202 | emit_stack_probe (r1); | |
1203 | } | |
1204 | } | |
1205 | } | |
1206 | ||
1207 | /* Make sure nothing is scheduled before we are done. */ | |
1208 | emit_insn (gen_blockage ()); | |
1209 | } | |
1210 | ||
1211 | /* Probe a range of stack addresses from REG1 to REG2 inclusive. These are | |
1212 | absolute addresses. */ | |
1213 | const char * | |
1214 | loongarch_output_probe_stack_range (rtx reg1, rtx reg2, rtx reg3) | |
1215 | { | |
1216 | static int labelno = 0; | |
1217 | char loop_lab[32], tmp[64]; | |
1218 | rtx xops[3]; | |
1219 | ||
1220 | ASM_GENERATE_INTERNAL_LABEL (loop_lab, "LPSRL", labelno++); | |
1221 | ||
1222 | /* Loop. */ | |
1223 | ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, loop_lab); | |
1224 | ||
1225 | /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL. */ | |
1226 | xops[0] = reg1; | |
1227 | xops[1] = GEN_INT (-PROBE_INTERVAL); | |
1228 | xops[2] = reg3; | |
1229 | if (TARGET_64BIT) | |
1230 | output_asm_insn ("sub.d\t%0,%0,%2", xops); | |
1231 | else | |
1232 | output_asm_insn ("sub.w\t%0,%0,%2", xops); | |
1233 | ||
1234 | /* Probe at TEST_ADDR, test if TEST_ADDR == LAST_ADDR and branch. */ | |
1235 | xops[1] = reg2; | |
1236 | strcpy (tmp, "bne\t%0,%1,"); | |
1237 | if (TARGET_64BIT) | |
1238 | output_asm_insn ("st.d\t$r0,%0,0", xops); | |
1239 | else | |
1240 | output_asm_insn ("st.w\t$r0,%0,0", xops); | |
1241 | output_asm_insn (strcat (tmp, &loop_lab[1]), xops); | |
1242 | ||
1243 | return ""; | |
1244 | } | |
1245 | ||
1246 | /* Expand the "prologue" pattern. */ | |
1247 | ||
1248 | void | |
1249 | loongarch_expand_prologue (void) | |
1250 | { | |
1251 | struct loongarch_frame_info *frame = &cfun->machine->frame; | |
1252 | HOST_WIDE_INT size = frame->total_size; | |
1253 | HOST_WIDE_INT tmp; | |
1254 | rtx insn; | |
1255 | ||
1256 | if (flag_stack_usage_info) | |
1257 | current_function_static_stack_size = size; | |
1258 | ||
1259 | if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK | |
1260 | || flag_stack_clash_protection) | |
1261 | { | |
1262 | if (crtl->is_leaf && !cfun->calls_alloca) | |
1263 | { | |
1264 | if (size > PROBE_INTERVAL && size > get_stack_check_protect ()) | |
1265 | { | |
1266 | tmp = size - get_stack_check_protect (); | |
1267 | loongarch_emit_probe_stack_range (get_stack_check_protect (), | |
1268 | tmp); | |
1269 | } | |
1270 | } | |
1271 | else if (size > 0) | |
1272 | loongarch_emit_probe_stack_range (get_stack_check_protect (), size); | |
1273 | } | |
1274 | ||
1275 | /* Save the registers. */ | |
1276 | if ((frame->mask | frame->fmask) != 0) | |
1277 | { | |
1278 | HOST_WIDE_INT step1 = MIN (size, loongarch_first_stack_step (frame)); | |
1279 | ||
1280 | insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, | |
1281 | GEN_INT (-step1)); | |
1282 | RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; | |
1283 | size -= step1; | |
1284 | loongarch_for_each_saved_reg (size, loongarch_save_reg); | |
1285 | } | |
1286 | ||
1287 | ||
1288 | /* Set up the frame pointer, if we're using one. */ | |
1289 | if (frame_pointer_needed) | |
1290 | { | |
1291 | insn = gen_add3_insn (hard_frame_pointer_rtx, stack_pointer_rtx, | |
1292 | GEN_INT (frame->hard_frame_pointer_offset - size)); | |
1293 | RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; | |
1294 | ||
1295 | loongarch_emit_stack_tie (); | |
1296 | } | |
1297 | ||
1298 | /* Allocate the rest of the frame. */ | |
1299 | if (size > 0) | |
1300 | { | |
1301 | if (IMM12_OPERAND (-size)) | |
1302 | { | |
1303 | insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, | |
1304 | GEN_INT (-size)); | |
1305 | RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; | |
1306 | } | |
1307 | else | |
1308 | { | |
1309 | loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode), GEN_INT (-size)); | |
1310 | emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, | |
1311 | LARCH_PROLOGUE_TEMP (Pmode))); | |
1312 | ||
1313 | /* Describe the effect of the previous instructions. */ | |
1314 | insn = plus_constant (Pmode, stack_pointer_rtx, -size); | |
1315 | insn = gen_rtx_SET (stack_pointer_rtx, insn); | |
1316 | loongarch_set_frame_expr (insn); | |
1317 | } | |
1318 | } | |
1319 | } | |
1320 | ||
1321 | /* Return nonzero if this function is known to have a null epilogue. | |
1322 | This allows the optimizer to omit jumps to jumps if no stack | |
1323 | was created. */ | |
1324 | ||
1325 | bool | |
1326 | loongarch_can_use_return_insn (void) | |
1327 | { | |
1328 | return reload_completed && cfun->machine->frame.total_size == 0; | |
1329 | } | |
1330 | ||
1331 | /* Expand an "epilogue" or "sibcall_epilogue" pattern; SIBCALL_P | |
1332 | says which. */ | |
1333 | ||
1334 | void | |
1335 | loongarch_expand_epilogue (bool sibcall_p) | |
1336 | { | |
1337 | /* Split the frame into two. STEP1 is the amount of stack we should | |
1338 | deallocate before restoring the registers. STEP2 is the amount we | |
1339 | should deallocate afterwards. | |
1340 | ||
1341 | Start off by assuming that no registers need to be restored. */ | |
1342 | struct loongarch_frame_info *frame = &cfun->machine->frame; | |
1343 | HOST_WIDE_INT step1 = frame->total_size; | |
1344 | HOST_WIDE_INT step2 = 0; | |
1345 | rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM); | |
1346 | rtx insn; | |
1347 | ||
1348 | /* We need to add memory barrier to prevent read from deallocated stack. */ | |
1349 | bool need_barrier_p | |
1350 | = (get_frame_size () + cfun->machine->frame.arg_pointer_offset) != 0; | |
1351 | ||
1352 | if (!sibcall_p && loongarch_can_use_return_insn ()) | |
1353 | { | |
1354 | emit_jump_insn (gen_return ()); | |
1355 | return; | |
1356 | } | |
1357 | ||
1358 | /* Move past any dynamic stack allocations. */ | |
1359 | if (cfun->calls_alloca) | |
1360 | { | |
1361 | /* Emit a barrier to prevent loads from a deallocated stack. */ | |
1362 | loongarch_emit_stack_tie (); | |
1363 | need_barrier_p = false; | |
1364 | ||
1365 | rtx adjust = GEN_INT (-frame->hard_frame_pointer_offset); | |
1366 | if (!IMM12_OPERAND (INTVAL (adjust))) | |
1367 | { | |
1368 | loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode), adjust); | |
1369 | adjust = LARCH_PROLOGUE_TEMP (Pmode); | |
1370 | } | |
1371 | ||
1372 | insn = emit_insn (gen_add3_insn (stack_pointer_rtx, | |
1373 | hard_frame_pointer_rtx, | |
1374 | adjust)); | |
1375 | ||
1376 | rtx dwarf = NULL_RTX; | |
1377 | rtx minus_offset = GEN_INT (-frame->hard_frame_pointer_offset); | |
1378 | rtx cfa_adjust_value = gen_rtx_PLUS (Pmode, | |
1379 | hard_frame_pointer_rtx, | |
1380 | minus_offset); | |
1381 | ||
1382 | rtx cfa_adjust_rtx = gen_rtx_SET (stack_pointer_rtx, cfa_adjust_value); | |
1383 | dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, cfa_adjust_rtx, dwarf); | |
1384 | RTX_FRAME_RELATED_P (insn) = 1; | |
1385 | ||
1386 | REG_NOTES (insn) = dwarf; | |
1387 | } | |
1388 | ||
1389 | /* If we need to restore registers, deallocate as much stack as | |
1390 | possible in the second step without going out of range. */ | |
1391 | if ((frame->mask | frame->fmask) != 0) | |
1392 | { | |
1393 | step2 = loongarch_first_stack_step (frame); | |
1394 | step1 -= step2; | |
1395 | } | |
1396 | ||
1397 | /* Set TARGET to BASE + STEP1. */ | |
1398 | if (step1 > 0) | |
1399 | { | |
1400 | /* Emit a barrier to prevent loads from a deallocated stack. */ | |
1401 | loongarch_emit_stack_tie (); | |
1402 | need_barrier_p = false; | |
1403 | ||
1404 | /* Get an rtx for STEP1 that we can add to BASE. */ | |
1405 | rtx adjust = GEN_INT (step1); | |
1406 | if (!IMM12_OPERAND (step1)) | |
1407 | { | |
1408 | loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode), adjust); | |
1409 | adjust = LARCH_PROLOGUE_TEMP (Pmode); | |
1410 | } | |
1411 | ||
1412 | insn = emit_insn (gen_add3_insn (stack_pointer_rtx, | |
1413 | stack_pointer_rtx, | |
1414 | adjust)); | |
1415 | ||
1416 | rtx dwarf = NULL_RTX; | |
1417 | rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, | |
1418 | GEN_INT (step2)); | |
1419 | ||
1420 | dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf); | |
1421 | RTX_FRAME_RELATED_P (insn) = 1; | |
1422 | ||
1423 | REG_NOTES (insn) = dwarf; | |
1424 | } | |
1425 | ||
1426 | /* Restore the registers. */ | |
1427 | loongarch_for_each_saved_reg (frame->total_size - step2, | |
1428 | loongarch_restore_reg); | |
1429 | ||
1430 | if (need_barrier_p) | |
1431 | loongarch_emit_stack_tie (); | |
1432 | ||
1433 | /* Deallocate the final bit of the frame. */ | |
1434 | if (step2 > 0) | |
1435 | { | |
1436 | insn = emit_insn (gen_add3_insn (stack_pointer_rtx, | |
1437 | stack_pointer_rtx, | |
1438 | GEN_INT (step2))); | |
1439 | ||
1440 | rtx dwarf = NULL_RTX; | |
1441 | rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx, const0_rtx); | |
1442 | dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf); | |
1443 | RTX_FRAME_RELATED_P (insn) = 1; | |
1444 | ||
1445 | REG_NOTES (insn) = dwarf; | |
1446 | } | |
1447 | ||
1448 | /* Add in the __builtin_eh_return stack adjustment. */ | |
1449 | if (crtl->calls_eh_return) | |
1450 | emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, | |
1451 | EH_RETURN_STACKADJ_RTX)); | |
1452 | ||
1453 | if (!sibcall_p) | |
1454 | emit_jump_insn (gen_simple_return_internal (ra)); | |
1455 | } | |
1456 | ||
1457 | #define LU32I_B (0xfffffULL << 32) | |
1458 | #define LU52I_B (0xfffULL << 52) | |
1459 | ||
1460 | /* Fill CODES with a sequence of rtl operations to load VALUE. | |
1461 | Return the number of operations needed. */ | |
1462 | ||
1463 | static unsigned int | |
1464 | loongarch_build_integer (struct loongarch_integer_op *codes, | |
1465 | HOST_WIDE_INT value) | |
1466 | ||
1467 | { | |
1468 | unsigned int cost = 0; | |
1469 | ||
1470 | /* Get the lower 32 bits of the value. */ | |
1471 | HOST_WIDE_INT low_part = TARGET_64BIT ? value << 32 >> 32 : value; | |
1472 | ||
1473 | if (IMM12_OPERAND (low_part) || IMM12_OPERAND_UNSIGNED (low_part)) | |
1474 | { | |
1475 | /* The value of the lower 32 bit be loaded with one instruction. | |
1476 | lu12i.w. */ | |
1477 | codes[0].code = UNKNOWN; | |
1478 | codes[0].method = METHOD_NORMAL; | |
1479 | codes[0].value = low_part; | |
1480 | cost++; | |
1481 | } | |
1482 | else | |
1483 | { | |
1484 | /* lu12i.w + ior. */ | |
1485 | codes[0].code = UNKNOWN; | |
1486 | codes[0].method = METHOD_NORMAL; | |
1487 | codes[0].value = low_part & ~(IMM_REACH - 1); | |
1488 | cost++; | |
1489 | HOST_WIDE_INT iorv = low_part & (IMM_REACH - 1); | |
1490 | if (iorv != 0) | |
1491 | { | |
1492 | codes[1].code = IOR; | |
1493 | codes[1].method = METHOD_NORMAL; | |
1494 | codes[1].value = iorv; | |
1495 | cost++; | |
1496 | } | |
1497 | } | |
1498 | ||
1499 | if (TARGET_64BIT) | |
1500 | { | |
1501 | bool lu32i[2] = {(value & LU32I_B) == 0, (value & LU32I_B) == LU32I_B}; | |
1502 | bool lu52i[2] = {(value & LU52I_B) == 0, (value & LU52I_B) == LU52I_B}; | |
1503 | ||
1504 | int sign31 = (value & (1UL << 31)) >> 31; | |
1505 | /* Determine whether the upper 32 bits are sign-extended from the lower | |
1506 | 32 bits. If it is, the instructions to load the high order can be | |
1507 | ommitted. */ | |
1508 | if (lu32i[sign31] && lu52i[sign31]) | |
1509 | return cost; | |
1510 | /* Determine whether bits 32-51 are sign-extended from the lower 32 | |
1511 | bits. If so, directly load 52-63 bits. */ | |
1512 | else if (lu32i[sign31]) | |
1513 | { | |
1514 | codes[cost].method = METHOD_LU52I; | |
1515 | codes[cost].value = (value >> 52) << 52; | |
1516 | return cost + 1; | |
1517 | } | |
1518 | ||
1519 | codes[cost].method = METHOD_LU32I; | |
1520 | codes[cost].value = ((value << 12) >> 44) << 32; | |
1521 | cost++; | |
1522 | ||
1523 | /* Determine whether the 52-61 bits are sign-extended from the low order, | |
1524 | and if not, load the 52-61 bits. */ | |
1525 | if (!lu52i[(value & (1ULL << 51)) >> 51]) | |
1526 | { | |
1527 | codes[cost].method = METHOD_LU52I; | |
1528 | codes[cost].value = (value >> 52) << 52; | |
1529 | cost++; | |
1530 | } | |
1531 | } | |
1532 | ||
1533 | gcc_assert (cost <= LARCH_MAX_INTEGER_OPS); | |
1534 | ||
1535 | return cost; | |
1536 | } | |
1537 | ||
1538 | /* Fill CODES with a sequence of rtl operations to load VALUE. | |
1539 | Return the number of operations needed. | |
1540 | Split interger in loongarch_output_move. */ | |
1541 | ||
1542 | static unsigned int | |
1543 | loongarch_integer_cost (HOST_WIDE_INT value) | |
1544 | { | |
1545 | struct loongarch_integer_op codes[LARCH_MAX_INTEGER_OPS]; | |
1546 | return loongarch_build_integer (codes, value); | |
1547 | } | |
1548 | ||
1549 | /* Implement TARGET_LEGITIMATE_CONSTANT_P. */ | |
1550 | ||
1551 | static bool | |
1552 | loongarch_legitimate_constant_p (machine_mode mode ATTRIBUTE_UNUSED, rtx x) | |
1553 | { | |
1554 | return loongarch_const_insns (x) > 0; | |
1555 | } | |
1556 | ||
1557 | /* Return true if X is a thread-local symbol. */ | |
1558 | ||
1559 | static bool | |
1560 | loongarch_tls_symbol_p (rtx x) | |
1561 | { | |
1562 | return SYMBOL_REF_P (x) && SYMBOL_REF_TLS_MODEL (x) != 0; | |
1563 | } | |
1564 | ||
1565 | /* Return true if SYMBOL_REF X is associated with a global symbol | |
1566 | (in the STB_GLOBAL sense). */ | |
1567 | ||
1568 | bool | |
1569 | loongarch_global_symbol_p (const_rtx x) | |
1570 | { | |
1571 | if (LABEL_REF_P (x)) | |
1572 | return false; | |
1573 | ||
1574 | const_tree decl = SYMBOL_REF_DECL (x); | |
1575 | ||
1576 | if (!decl) | |
1577 | return !SYMBOL_REF_LOCAL_P (x) || SYMBOL_REF_EXTERNAL_P (x); | |
1578 | ||
1579 | /* Weakref symbols are not TREE_PUBLIC, but their targets are global | |
1580 | or weak symbols. Relocations in the object file will be against | |
1581 | the target symbol, so it's that symbol's binding that matters here. */ | |
1582 | return DECL_P (decl) && (TREE_PUBLIC (decl) || DECL_WEAK (decl)); | |
1583 | } | |
1584 | ||
1585 | bool | |
1586 | loongarch_global_symbol_noweak_p (const_rtx x) | |
1587 | { | |
1588 | if (LABEL_REF_P (x)) | |
1589 | return false; | |
1590 | ||
1591 | const_tree decl = SYMBOL_REF_DECL (x); | |
1592 | ||
1593 | if (!decl) | |
1594 | return !SYMBOL_REF_LOCAL_P (x) || SYMBOL_REF_EXTERNAL_P (x); | |
1595 | ||
1596 | return DECL_P (decl) && TREE_PUBLIC (decl); | |
1597 | } | |
1598 | ||
1599 | bool | |
1600 | loongarch_weak_symbol_p (const_rtx x) | |
1601 | { | |
1602 | const_tree decl; | |
1603 | if (LABEL_REF_P (x) || !(decl = SYMBOL_REF_DECL (x))) | |
1604 | return false; | |
1605 | return DECL_P (decl) && DECL_WEAK (decl); | |
1606 | } | |
1607 | ||
1608 | /* Return true if SYMBOL_REF X binds locally. */ | |
1609 | ||
1610 | bool | |
1611 | loongarch_symbol_binds_local_p (const_rtx x) | |
1612 | { | |
1613 | if (LABEL_REF_P (x)) | |
1614 | return false; | |
1615 | ||
1616 | return (SYMBOL_REF_DECL (x) ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) | |
1617 | : SYMBOL_REF_LOCAL_P (x)); | |
1618 | } | |
1619 | ||
1620 | /* Return true if rtx constants of mode MODE should be put into a small | |
1621 | data section. */ | |
1622 | ||
1623 | static bool | |
1624 | loongarch_rtx_constant_in_small_data_p (machine_mode mode) | |
1625 | { | |
1626 | return (GET_MODE_SIZE (mode) <= g_switch_value); | |
1627 | } | |
1628 | ||
1629 | /* Return the method that should be used to access SYMBOL_REF or | |
1630 | LABEL_REF X. */ | |
1631 | ||
1632 | static enum loongarch_symbol_type | |
1633 | loongarch_classify_symbol (const_rtx x) | |
1634 | { | |
1635 | if (LABEL_REF_P (x)) | |
1636 | return SYMBOL_GOT_DISP; | |
1637 | ||
1638 | gcc_assert (SYMBOL_REF_P (x)); | |
1639 | ||
1640 | if (SYMBOL_REF_TLS_MODEL (x)) | |
1641 | return SYMBOL_TLS; | |
1642 | ||
1643 | if (SYMBOL_REF_P (x)) | |
1644 | return SYMBOL_GOT_DISP; | |
1645 | ||
1646 | return SYMBOL_GOT_DISP; | |
1647 | } | |
1648 | ||
1649 | /* Return true if X is a symbolic constant. If it is, | |
1650 | store the type of the symbol in *SYMBOL_TYPE. */ | |
1651 | ||
1652 | bool | |
1653 | loongarch_symbolic_constant_p (rtx x, enum loongarch_symbol_type *symbol_type) | |
1654 | { | |
1655 | rtx offset; | |
1656 | ||
1657 | split_const (x, &x, &offset); | |
1658 | if (UNSPEC_ADDRESS_P (x)) | |
1659 | { | |
1660 | *symbol_type = UNSPEC_ADDRESS_TYPE (x); | |
1661 | x = UNSPEC_ADDRESS (x); | |
1662 | } | |
1663 | else if (SYMBOL_REF_P (x) || LABEL_REF_P (x)) | |
1664 | { | |
1665 | *symbol_type = loongarch_classify_symbol (x); | |
1666 | if (*symbol_type == SYMBOL_TLS) | |
1667 | return true; | |
1668 | } | |
1669 | else | |
1670 | return false; | |
1671 | ||
1672 | if (offset == const0_rtx) | |
1673 | return true; | |
1674 | ||
1675 | /* Check whether a nonzero offset is valid for the underlying | |
1676 | relocations. */ | |
1677 | switch (*symbol_type) | |
1678 | { | |
1679 | case SYMBOL_GOT_DISP: | |
1680 | case SYMBOL_TLSGD: | |
1681 | case SYMBOL_TLSLDM: | |
1682 | case SYMBOL_TLS: | |
1683 | return false; | |
1684 | } | |
1685 | gcc_unreachable (); | |
1686 | } | |
1687 | ||
1688 | /* Returns the number of instructions necessary to reference a symbol. */ | |
1689 | ||
1690 | static int | |
1691 | loongarch_symbol_insns (enum loongarch_symbol_type type, machine_mode mode) | |
1692 | { | |
1693 | switch (type) | |
1694 | { | |
1695 | case SYMBOL_GOT_DISP: | |
1696 | /* The constant will have to be loaded from the GOT before it | |
1697 | is used in an address. */ | |
1698 | if (mode != MAX_MACHINE_MODE) | |
1699 | return 0; | |
1700 | ||
1701 | return 3; | |
1702 | ||
1703 | case SYMBOL_TLSGD: | |
1704 | case SYMBOL_TLSLDM: | |
1705 | return 1; | |
1706 | ||
1707 | case SYMBOL_TLS: | |
1708 | /* We don't treat a bare TLS symbol as a constant. */ | |
1709 | return 0; | |
1710 | } | |
1711 | gcc_unreachable (); | |
1712 | } | |
1713 | ||
1714 | /* Implement TARGET_CANNOT_FORCE_CONST_MEM. */ | |
1715 | ||
1716 | static bool | |
1717 | loongarch_cannot_force_const_mem (machine_mode mode, rtx x) | |
1718 | { | |
1719 | enum loongarch_symbol_type type; | |
1720 | rtx base, offset; | |
1721 | ||
1722 | /* As an optimization, reject constants that loongarch_legitimize_move | |
1723 | can expand inline. | |
1724 | ||
1725 | Suppose we have a multi-instruction sequence that loads constant C | |
1726 | into register R. If R does not get allocated a hard register, and | |
1727 | R is used in an operand that allows both registers and memory | |
1728 | references, reload will consider forcing C into memory and using | |
1729 | one of the instruction's memory alternatives. Returning false | |
1730 | here will force it to use an input reload instead. */ | |
1731 | if (CONST_INT_P (x) && loongarch_legitimate_constant_p (mode, x)) | |
1732 | return true; | |
1733 | ||
1734 | split_const (x, &base, &offset); | |
1735 | if (loongarch_symbolic_constant_p (base, &type)) | |
1736 | { | |
1737 | /* The same optimization as for CONST_INT. */ | |
1738 | if (IMM12_INT (offset) | |
1739 | && loongarch_symbol_insns (type, MAX_MACHINE_MODE) > 0) | |
1740 | return true; | |
1741 | } | |
1742 | ||
1743 | /* TLS symbols must be computed by loongarch_legitimize_move. */ | |
1744 | if (tls_referenced_p (x)) | |
1745 | return true; | |
1746 | ||
1747 | return false; | |
1748 | } | |
1749 | ||
1750 | /* Return true if register REGNO is a valid base register for mode MODE. | |
1751 | STRICT_P is true if REG_OK_STRICT is in effect. */ | |
1752 | ||
1753 | int | |
1754 | loongarch_regno_mode_ok_for_base_p (int regno, | |
1755 | machine_mode mode ATTRIBUTE_UNUSED, | |
1756 | bool strict_p) | |
1757 | { | |
1758 | if (!HARD_REGISTER_NUM_P (regno)) | |
1759 | { | |
1760 | if (!strict_p) | |
1761 | return true; | |
1762 | regno = reg_renumber[regno]; | |
1763 | } | |
1764 | ||
1765 | /* These fake registers will be eliminated to either the stack or | |
1766 | hard frame pointer, both of which are usually valid base registers. | |
1767 | Reload deals with the cases where the eliminated form isn't valid. */ | |
1768 | if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM) | |
1769 | return true; | |
1770 | ||
1771 | return GP_REG_P (regno); | |
1772 | } | |
1773 | ||
1774 | /* Return true if X is a valid base register for mode MODE. | |
1775 | STRICT_P is true if REG_OK_STRICT is in effect. */ | |
1776 | ||
1777 | static bool | |
1778 | loongarch_valid_base_register_p (rtx x, machine_mode mode, bool strict_p) | |
1779 | { | |
1780 | if (!strict_p && SUBREG_P (x)) | |
1781 | x = SUBREG_REG (x); | |
1782 | ||
1783 | return (REG_P (x) | |
1784 | && loongarch_regno_mode_ok_for_base_p (REGNO (x), mode, strict_p)); | |
1785 | } | |
1786 | ||
1787 | /* Return true if, for every base register BASE_REG, (plus BASE_REG X) | |
1788 | can address a value of mode MODE. */ | |
1789 | ||
1790 | static bool | |
1791 | loongarch_valid_offset_p (rtx x, machine_mode mode) | |
1792 | { | |
1793 | /* Check that X is a signed 12-bit number, | |
1794 | or check that X is a signed 16-bit number | |
1795 | and offset 4 byte aligned. */ | |
1796 | if (!(const_arith_operand (x, Pmode) | |
1797 | || ((mode == E_SImode || mode == E_DImode) | |
1798 | && const_imm16_operand (x, Pmode) | |
1799 | && (loongarch_signed_immediate_p (INTVAL (x), 14, 2))))) | |
1800 | return false; | |
1801 | ||
1802 | /* We may need to split multiword moves, so make sure that every word | |
1803 | is accessible. */ | |
1804 | if (GET_MODE_SIZE (mode) > UNITS_PER_WORD | |
1805 | && !IMM12_OPERAND (INTVAL (x) + GET_MODE_SIZE (mode) - UNITS_PER_WORD)) | |
1806 | return false; | |
1807 | ||
1808 | return true; | |
1809 | } | |
1810 | ||
1811 | static bool | |
1812 | loongarch_valid_index_p (struct loongarch_address_info *info, rtx x, | |
1813 | machine_mode mode, bool strict_p) | |
1814 | { | |
1815 | rtx index; | |
1816 | ||
1817 | if ((REG_P (x) || SUBREG_P (x)) | |
1818 | && GET_MODE (x) == Pmode) | |
1819 | { | |
1820 | index = x; | |
1821 | } | |
1822 | else | |
1823 | return false; | |
1824 | ||
1825 | if (!strict_p | |
1826 | && SUBREG_P (index) | |
1827 | && contains_reg_of_mode[GENERAL_REGS][GET_MODE (SUBREG_REG (index))]) | |
1828 | index = SUBREG_REG (index); | |
1829 | ||
1830 | if (loongarch_valid_base_register_p (index, mode, strict_p)) | |
1831 | { | |
1832 | info->type = ADDRESS_REG_REG; | |
1833 | info->offset = index; | |
1834 | return true; | |
1835 | } | |
1836 | ||
1837 | return false; | |
1838 | } | |
1839 | ||
1840 | /* Return true if X is a valid address for machine mode MODE. If it is, | |
1841 | fill in INFO appropriately. STRICT_P is true if REG_OK_STRICT is in | |
1842 | effect. */ | |
1843 | ||
1844 | static bool | |
1845 | loongarch_classify_address (struct loongarch_address_info *info, rtx x, | |
1846 | machine_mode mode, bool strict_p) | |
1847 | { | |
1848 | switch (GET_CODE (x)) | |
1849 | { | |
1850 | case REG: | |
1851 | case SUBREG: | |
1852 | info->type = ADDRESS_REG; | |
1853 | info->reg = x; | |
1854 | info->offset = const0_rtx; | |
1855 | return loongarch_valid_base_register_p (info->reg, mode, strict_p); | |
1856 | ||
1857 | case PLUS: | |
1858 | if (loongarch_valid_base_register_p (XEXP (x, 0), mode, strict_p) | |
1859 | && loongarch_valid_index_p (info, XEXP (x, 1), mode, strict_p)) | |
1860 | { | |
1861 | info->reg = XEXP (x, 0); | |
1862 | return true; | |
1863 | } | |
1864 | ||
1865 | if (loongarch_valid_base_register_p (XEXP (x, 1), mode, strict_p) | |
1866 | && loongarch_valid_index_p (info, XEXP (x, 0), mode, strict_p)) | |
1867 | { | |
1868 | info->reg = XEXP (x, 1); | |
1869 | return true; | |
1870 | } | |
1871 | ||
1872 | info->type = ADDRESS_REG; | |
1873 | info->reg = XEXP (x, 0); | |
1874 | info->offset = XEXP (x, 1); | |
1875 | return (loongarch_valid_base_register_p (info->reg, mode, strict_p) | |
1876 | && loongarch_valid_offset_p (info->offset, mode)); | |
1877 | default: | |
1878 | return false; | |
1879 | } | |
1880 | } | |
1881 | ||
1882 | /* Implement TARGET_LEGITIMATE_ADDRESS_P. */ | |
1883 | ||
1884 | static bool | |
1885 | loongarch_legitimate_address_p (machine_mode mode, rtx x, bool strict_p) | |
1886 | { | |
1887 | struct loongarch_address_info addr; | |
1888 | ||
1889 | return loongarch_classify_address (&addr, x, mode, strict_p); | |
1890 | } | |
1891 | ||
1892 | /* Return true if ADDR matches the pattern for the indexed address | |
1893 | instruction. */ | |
1894 | ||
1895 | static bool | |
1896 | loongarch_index_address_p (rtx addr, machine_mode mode ATTRIBUTE_UNUSED) | |
1897 | { | |
1898 | if (GET_CODE (addr) != PLUS | |
1899 | || !REG_P (XEXP (addr, 0)) | |
1900 | || !REG_P (XEXP (addr, 1))) | |
1901 | return false; | |
1902 | return true; | |
1903 | } | |
1904 | ||
1905 | /* Return the number of instructions needed to load or store a value | |
1906 | of mode MODE at address X. Return 0 if X isn't valid for MODE. | |
1907 | Assume that multiword moves may need to be split into word moves | |
1908 | if MIGHT_SPLIT_P, otherwise assume that a single load or store is | |
1909 | enough. */ | |
1910 | ||
1911 | int | |
1912 | loongarch_address_insns (rtx x, machine_mode mode, bool might_split_p) | |
1913 | { | |
1914 | struct loongarch_address_info addr; | |
1915 | int factor; | |
1916 | ||
1917 | if (!loongarch_classify_address (&addr, x, mode, false)) | |
1918 | return 0; | |
1919 | ||
1920 | /* BLKmode is used for single unaligned loads and stores and should | |
1921 | not count as a multiword mode. (GET_MODE_SIZE (BLKmode) is pretty | |
1922 | meaningless, so we have to single it out as a special case one way | |
1923 | or the other.) */ | |
1924 | if (mode != BLKmode && might_split_p) | |
1925 | factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; | |
1926 | else | |
1927 | factor = 1; | |
1928 | ||
1929 | if (loongarch_classify_address (&addr, x, mode, false)) | |
1930 | switch (addr.type) | |
1931 | { | |
1932 | case ADDRESS_REG: | |
1933 | return factor; | |
1934 | ||
1935 | case ADDRESS_REG_REG: | |
1936 | return factor; | |
1937 | ||
1938 | case ADDRESS_CONST_INT: | |
1939 | return factor; | |
1940 | ||
1941 | case ADDRESS_SYMBOLIC: | |
1942 | return factor * loongarch_symbol_insns (addr.symbol_type, mode); | |
1943 | } | |
1944 | return 0; | |
1945 | } | |
1946 | ||
1947 | /* Return true if X fits within an unsigned field of BITS bits that is | |
1948 | shifted left SHIFT bits before being used. */ | |
1949 | ||
1950 | bool | |
1951 | loongarch_unsigned_immediate_p (unsigned HOST_WIDE_INT x, int bits, | |
1952 | int shift = 0) | |
1953 | { | |
1954 | return (x & ((1 << shift) - 1)) == 0 && x < ((unsigned) 1 << (shift + bits)); | |
1955 | } | |
1956 | ||
1957 | /* Return true if X fits within a signed field of BITS bits that is | |
1958 | shifted left SHIFT bits before being used. */ | |
1959 | ||
1960 | bool | |
1961 | loongarch_signed_immediate_p (unsigned HOST_WIDE_INT x, int bits, | |
1962 | int shift = 0) | |
1963 | { | |
1964 | x += 1 << (bits + shift - 1); | |
1965 | return loongarch_unsigned_immediate_p (x, bits, shift); | |
1966 | } | |
1967 | ||
1968 | /* Return true if X is a legitimate address with a 12-bit offset. | |
1969 | MODE is the mode of the value being accessed. */ | |
1970 | ||
1971 | bool | |
1972 | loongarch_12bit_offset_address_p (rtx x, machine_mode mode) | |
1973 | { | |
1974 | struct loongarch_address_info addr; | |
1975 | ||
1976 | return (loongarch_classify_address (&addr, x, mode, false) | |
1977 | && addr.type == ADDRESS_REG | |
1978 | && CONST_INT_P (addr.offset) | |
1979 | && LARCH_U12BIT_OFFSET_P (INTVAL (addr.offset))); | |
1980 | } | |
1981 | ||
1982 | /* Return true if X is a legitimate address with a 14-bit offset shifted 2. | |
1983 | MODE is the mode of the value being accessed. */ | |
1984 | ||
1985 | bool | |
1986 | loongarch_14bit_shifted_offset_address_p (rtx x, machine_mode mode) | |
1987 | { | |
1988 | struct loongarch_address_info addr; | |
1989 | ||
1990 | return (loongarch_classify_address (&addr, x, mode, false) | |
1991 | && addr.type == ADDRESS_REG | |
1992 | && CONST_INT_P (addr.offset) | |
1993 | && LARCH_16BIT_OFFSET_P (INTVAL (addr.offset)) | |
1994 | && LARCH_SHIFT_2_OFFSET_P (INTVAL (addr.offset))); | |
1995 | } | |
1996 | ||
1997 | bool | |
1998 | loongarch_base_index_address_p (rtx x, machine_mode mode) | |
1999 | { | |
2000 | struct loongarch_address_info addr; | |
2001 | ||
2002 | return (loongarch_classify_address (&addr, x, mode, false) | |
2003 | && addr.type == ADDRESS_REG_REG | |
2004 | && REG_P (addr.offset)); | |
2005 | } | |
2006 | ||
2007 | /* Return the number of instructions needed to load constant X, | |
2008 | Return 0 if X isn't a valid constant. */ | |
2009 | ||
2010 | int | |
2011 | loongarch_const_insns (rtx x) | |
2012 | { | |
2013 | enum loongarch_symbol_type symbol_type; | |
2014 | rtx offset; | |
2015 | ||
2016 | switch (GET_CODE (x)) | |
2017 | { | |
2018 | case CONST_INT: | |
2019 | return loongarch_integer_cost (INTVAL (x)); | |
2020 | ||
2021 | case CONST_VECTOR: | |
2022 | /* Fall through. */ | |
2023 | case CONST_DOUBLE: | |
2024 | return x == CONST0_RTX (GET_MODE (x)) ? 1 : 0; | |
2025 | ||
2026 | case CONST: | |
2027 | /* See if we can refer to X directly. */ | |
2028 | if (loongarch_symbolic_constant_p (x, &symbol_type)) | |
2029 | return loongarch_symbol_insns (symbol_type, MAX_MACHINE_MODE); | |
2030 | ||
2031 | /* Otherwise try splitting the constant into a base and offset. | |
2032 | If the offset is a 12-bit value, we can load the base address | |
2033 | into a register and then use ADDI.{W/D} to add in the offset. | |
2034 | If the offset is larger, we can load the base and offset | |
2035 | into separate registers and add them together with ADD.{W/D}. | |
2036 | However, the latter is only possible before reload; during | |
2037 | and after reload, we must have the option of forcing the | |
2038 | constant into the pool instead. */ | |
2039 | split_const (x, &x, &offset); | |
2040 | if (offset != 0) | |
2041 | { | |
2042 | int n = loongarch_const_insns (x); | |
2043 | if (n != 0) | |
2044 | { | |
2045 | if (IMM12_INT (offset)) | |
2046 | return n + 1; | |
2047 | else if (!targetm.cannot_force_const_mem (GET_MODE (x), x)) | |
2048 | return n + 1 + loongarch_integer_cost (INTVAL (offset)); | |
2049 | } | |
2050 | } | |
2051 | return 0; | |
2052 | ||
2053 | case SYMBOL_REF: | |
2054 | case LABEL_REF: | |
2055 | return loongarch_symbol_insns ( | |
2056 | loongarch_classify_symbol (x), MAX_MACHINE_MODE); | |
2057 | ||
2058 | default: | |
2059 | return 0; | |
2060 | } | |
2061 | } | |
2062 | ||
2063 | /* X is a doubleword constant that can be handled by splitting it into | |
2064 | two words and loading each word separately. Return the number of | |
2065 | instructions required to do this. */ | |
2066 | ||
2067 | int | |
2068 | loongarch_split_const_insns (rtx x) | |
2069 | { | |
2070 | unsigned int low, high; | |
2071 | ||
2072 | low = loongarch_const_insns (loongarch_subword (x, false)); | |
2073 | high = loongarch_const_insns (loongarch_subword (x, true)); | |
2074 | gcc_assert (low > 0 && high > 0); | |
2075 | return low + high; | |
2076 | } | |
2077 | ||
2078 | /* Return the number of instructions needed to implement INSN, | |
2079 | given that it loads from or stores to MEM. */ | |
2080 | ||
2081 | int | |
2082 | loongarch_load_store_insns (rtx mem, rtx_insn *insn) | |
2083 | { | |
2084 | machine_mode mode; | |
2085 | bool might_split_p; | |
2086 | rtx set; | |
2087 | ||
2088 | gcc_assert (MEM_P (mem)); | |
2089 | mode = GET_MODE (mem); | |
2090 | ||
2091 | /* Try to prove that INSN does not need to be split. */ | |
2092 | might_split_p = GET_MODE_SIZE (mode) > UNITS_PER_WORD; | |
2093 | if (might_split_p) | |
2094 | { | |
2095 | set = single_set (insn); | |
2096 | if (set | |
2097 | && !loongarch_split_move_insn_p (SET_DEST (set), SET_SRC (set))) | |
2098 | might_split_p = false; | |
2099 | } | |
2100 | ||
2101 | return loongarch_address_insns (XEXP (mem, 0), mode, might_split_p); | |
2102 | } | |
2103 | ||
2104 | /* Return the number of instructions needed for an integer division. */ | |
2105 | ||
2106 | int | |
2107 | loongarch_idiv_insns (machine_mode mode ATTRIBUTE_UNUSED) | |
2108 | { | |
2109 | int count; | |
2110 | ||
2111 | count = 1; | |
2112 | if (TARGET_CHECK_ZERO_DIV) | |
2113 | count += 2; | |
2114 | ||
2115 | return count; | |
2116 | } | |
2117 | ||
2118 | /* Emit an instruction of the form (set TARGET (CODE OP0 OP1)). */ | |
2119 | ||
2120 | void | |
2121 | loongarch_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1) | |
2122 | { | |
2123 | emit_insn (gen_rtx_SET (target, gen_rtx_fmt_ee (code, GET_MODE (target), | |
2124 | op0, op1))); | |
2125 | } | |
2126 | ||
2127 | /* Compute (CODE OP0 OP1) and store the result in a new register | |
2128 | of mode MODE. Return that new register. */ | |
2129 | ||
2130 | static rtx | |
2131 | loongarch_force_binary (machine_mode mode, enum rtx_code code, rtx op0, | |
2132 | rtx op1) | |
2133 | { | |
2134 | rtx reg; | |
2135 | ||
2136 | reg = gen_reg_rtx (mode); | |
2137 | loongarch_emit_binary (code, reg, op0, op1); | |
2138 | return reg; | |
2139 | } | |
2140 | ||
2141 | /* Copy VALUE to a register and return that register. If new pseudos | |
2142 | are allowed, copy it into a new register, otherwise use DEST. */ | |
2143 | ||
2144 | static rtx | |
2145 | loongarch_force_temporary (rtx dest, rtx value) | |
2146 | { | |
2147 | if (can_create_pseudo_p ()) | |
2148 | return force_reg (Pmode, value); | |
2149 | else | |
2150 | { | |
2151 | loongarch_emit_move (dest, value); | |
2152 | return dest; | |
2153 | } | |
2154 | } | |
2155 | ||
2156 | /* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE, | |
2157 | then add CONST_INT OFFSET to the result. */ | |
2158 | ||
2159 | static rtx | |
2160 | loongarch_unspec_address_offset (rtx base, rtx offset, | |
2161 | enum loongarch_symbol_type symbol_type) | |
2162 | { | |
2163 | base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), | |
2164 | UNSPEC_ADDRESS_FIRST + symbol_type); | |
2165 | if (offset != const0_rtx) | |
2166 | base = gen_rtx_PLUS (Pmode, base, offset); | |
2167 | return gen_rtx_CONST (Pmode, base); | |
2168 | } | |
2169 | ||
2170 | /* Return an UNSPEC address with underlying address ADDRESS and symbol | |
2171 | type SYMBOL_TYPE. */ | |
2172 | ||
2173 | rtx | |
2174 | loongarch_unspec_address (rtx address, enum loongarch_symbol_type symbol_type) | |
2175 | { | |
2176 | rtx base, offset; | |
2177 | ||
2178 | split_const (address, &base, &offset); | |
2179 | return loongarch_unspec_address_offset (base, offset, symbol_type); | |
2180 | } | |
2181 | ||
2182 | /* If OP is an UNSPEC address, return the address to which it refers, | |
2183 | otherwise return OP itself. */ | |
2184 | ||
2185 | rtx | |
2186 | loongarch_strip_unspec_address (rtx op) | |
2187 | { | |
2188 | rtx base, offset; | |
2189 | ||
2190 | split_const (op, &base, &offset); | |
2191 | if (UNSPEC_ADDRESS_P (base)) | |
2192 | op = plus_constant (Pmode, UNSPEC_ADDRESS (base), INTVAL (offset)); | |
2193 | return op; | |
2194 | } | |
2195 | ||
2196 | /* Return a legitimate address for REG + OFFSET. TEMP is as for | |
2197 | loongarch_force_temporary; it is only needed when OFFSET is not a | |
2198 | IMM12_OPERAND. */ | |
2199 | ||
2200 | static rtx | |
2201 | loongarch_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset) | |
2202 | { | |
2203 | if (!IMM12_OPERAND (offset)) | |
2204 | { | |
2205 | rtx high; | |
2206 | ||
2207 | /* Leave OFFSET as a 12-bit offset and put the excess in HIGH. | |
2208 | The addition inside the macro CONST_HIGH_PART may cause an | |
2209 | overflow, so we need to force a sign-extension check. */ | |
2210 | high = gen_int_mode (CONST_HIGH_PART (offset), Pmode); | |
2211 | offset = CONST_LOW_PART (offset); | |
2212 | high = loongarch_force_temporary (temp, high); | |
2213 | reg = loongarch_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg)); | |
2214 | } | |
2215 | return plus_constant (Pmode, reg, offset); | |
2216 | } | |
2217 | ||
2218 | /* The __tls_get_attr symbol. */ | |
2219 | static GTY (()) rtx loongarch_tls_symbol; | |
2220 | ||
2221 | /* Load an entry from the GOT for a TLS GD access. */ | |
2222 | ||
2223 | static rtx | |
2224 | loongarch_got_load_tls_gd (rtx dest, rtx sym) | |
2225 | { | |
2226 | return gen_got_load_tls_gd (Pmode, dest, sym); | |
2227 | } | |
2228 | ||
2229 | /* Load an entry from the GOT for a TLS LD access. */ | |
2230 | ||
2231 | static rtx | |
2232 | loongarch_got_load_tls_ld (rtx dest, rtx sym) | |
2233 | { | |
2234 | return gen_got_load_tls_ld (Pmode, dest, sym); | |
2235 | } | |
2236 | ||
2237 | /* Load an entry from the GOT for a TLS IE access. */ | |
2238 | ||
2239 | static rtx | |
2240 | loongarch_got_load_tls_ie (rtx dest, rtx sym) | |
2241 | { | |
2242 | return gen_got_load_tls_ie (Pmode, dest, sym); | |
2243 | } | |
2244 | ||
2245 | /* Add in the thread pointer for a TLS LE access. */ | |
2246 | ||
2247 | static rtx | |
2248 | loongarch_got_load_tls_le (rtx dest, rtx sym) | |
2249 | { | |
2250 | return gen_got_load_tls_le (Pmode, dest, sym); | |
2251 | } | |
2252 | ||
2253 | /* Return an instruction sequence that calls __tls_get_addr. SYM is | |
2254 | the TLS symbol we are referencing and TYPE is the symbol type to use | |
2255 | (either global dynamic or local dynamic). V0 is an RTX for the | |
2256 | return value location. */ | |
2257 | ||
2258 | static rtx_insn * | |
2259 | loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) | |
2260 | { | |
2261 | rtx loc, a0; | |
2262 | rtx_insn *insn; | |
2263 | ||
2264 | a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST); | |
2265 | ||
2266 | if (!loongarch_tls_symbol) | |
2267 | loongarch_tls_symbol = init_one_libfunc ("__tls_get_addr"); | |
2268 | ||
2269 | loc = loongarch_unspec_address (sym, type); | |
2270 | ||
2271 | start_sequence (); | |
2272 | ||
2273 | if (type == SYMBOL_TLSLDM) | |
2274 | emit_insn (loongarch_got_load_tls_ld (a0, loc)); | |
2275 | else if (type == SYMBOL_TLSGD) | |
2276 | emit_insn (loongarch_got_load_tls_gd (a0, loc)); | |
2277 | else | |
2278 | gcc_unreachable (); | |
2279 | ||
2280 | insn = emit_call_insn (gen_call_value_internal (v0, loongarch_tls_symbol, | |
2281 | const0_rtx)); | |
2282 | RTL_CONST_CALL_P (insn) = 1; | |
2283 | use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); | |
2284 | insn = get_insns (); | |
2285 | ||
2286 | end_sequence (); | |
2287 | ||
2288 | return insn; | |
2289 | } | |
2290 | ||
2291 | /* Generate the code to access LOC, a thread-local SYMBOL_REF, and return | |
2292 | its address. The return value will be both a valid address and a valid | |
2293 | SET_SRC (either a REG or a LO_SUM). */ | |
2294 | ||
2295 | static rtx | |
2296 | loongarch_legitimize_tls_address (rtx loc) | |
2297 | { | |
2298 | rtx dest, tp, tmp; | |
2299 | enum tls_model model = SYMBOL_REF_TLS_MODEL (loc); | |
2300 | rtx_insn *insn; | |
2301 | ||
2302 | switch (model) | |
2303 | { | |
2304 | case TLS_MODEL_LOCAL_DYNAMIC: | |
2305 | tmp = gen_rtx_REG (Pmode, GP_RETURN); | |
2306 | dest = gen_reg_rtx (Pmode); | |
2307 | insn = loongarch_call_tls_get_addr (loc, SYMBOL_TLSLDM, tmp); | |
2308 | emit_libcall_block (insn, dest, tmp, loc); | |
2309 | break; | |
2310 | ||
2311 | case TLS_MODEL_GLOBAL_DYNAMIC: | |
2312 | tmp = gen_rtx_REG (Pmode, GP_RETURN); | |
2313 | dest = gen_reg_rtx (Pmode); | |
2314 | insn = loongarch_call_tls_get_addr (loc, SYMBOL_TLSGD, tmp); | |
2315 | emit_libcall_block (insn, dest, tmp, loc); | |
2316 | break; | |
2317 | ||
2318 | case TLS_MODEL_INITIAL_EXEC: | |
2319 | /* la.tls.ie; tp-relative add */ | |
2320 | tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); | |
2321 | tmp = gen_reg_rtx (Pmode); | |
2322 | emit_insn (loongarch_got_load_tls_ie (tmp, loc)); | |
2323 | dest = gen_reg_rtx (Pmode); | |
2324 | emit_insn (gen_add3_insn (dest, tmp, tp)); | |
2325 | break; | |
2326 | ||
2327 | case TLS_MODEL_LOCAL_EXEC: | |
2328 | /* la.tls.le; tp-relative add */ | |
2329 | tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); | |
2330 | tmp = gen_reg_rtx (Pmode); | |
2331 | emit_insn (loongarch_got_load_tls_le (tmp, loc)); | |
2332 | dest = gen_reg_rtx (Pmode); | |
2333 | emit_insn (gen_add3_insn (dest, tmp, tp)); | |
2334 | break; | |
2335 | ||
2336 | default: | |
2337 | gcc_unreachable (); | |
2338 | } | |
2339 | return dest; | |
2340 | } | |
2341 | ||
2342 | rtx | |
2343 | loongarch_legitimize_call_address (rtx addr) | |
2344 | { | |
2345 | if (!call_insn_operand (addr, VOIDmode)) | |
2346 | { | |
2347 | rtx reg = gen_reg_rtx (Pmode); | |
2348 | loongarch_emit_move (reg, addr); | |
2349 | return reg; | |
2350 | } | |
2351 | return addr; | |
2352 | } | |
2353 | ||
2354 | /* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR | |
2355 | and *OFFSET_PTR. Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise. */ | |
2356 | ||
2357 | static void | |
2358 | loongarch_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr) | |
2359 | { | |
2360 | if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1))) | |
2361 | { | |
2362 | *base_ptr = XEXP (x, 0); | |
2363 | *offset_ptr = INTVAL (XEXP (x, 1)); | |
2364 | } | |
2365 | else | |
2366 | { | |
2367 | *base_ptr = x; | |
2368 | *offset_ptr = 0; | |
2369 | } | |
2370 | } | |
2371 | ||
2372 | /* If X is not a valid address for mode MODE, force it into a register. */ | |
2373 | ||
2374 | static rtx | |
2375 | loongarch_force_address (rtx x, machine_mode mode) | |
2376 | { | |
2377 | if (!loongarch_legitimate_address_p (mode, x, false)) | |
2378 | x = force_reg (Pmode, x); | |
2379 | return x; | |
2380 | } | |
2381 | ||
2382 | /* This function is used to implement LEGITIMIZE_ADDRESS. If X can | |
2383 | be legitimized in a way that the generic machinery might not expect, | |
2384 | return a new address, otherwise return NULL. MODE is the mode of | |
2385 | the memory being accessed. */ | |
2386 | ||
2387 | static rtx | |
2388 | loongarch_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, | |
2389 | machine_mode mode) | |
2390 | { | |
2391 | rtx base, addr; | |
2392 | HOST_WIDE_INT offset; | |
2393 | ||
2394 | if (loongarch_tls_symbol_p (x)) | |
2395 | return loongarch_legitimize_tls_address (x); | |
2396 | ||
2397 | /* Handle BASE + OFFSET using loongarch_add_offset. */ | |
2398 | loongarch_split_plus (x, &base, &offset); | |
2399 | if (offset != 0) | |
2400 | { | |
2401 | if (!loongarch_valid_base_register_p (base, mode, false)) | |
2402 | base = copy_to_mode_reg (Pmode, base); | |
2403 | addr = loongarch_add_offset (NULL, base, offset); | |
2404 | return loongarch_force_address (addr, mode); | |
2405 | } | |
2406 | ||
2407 | return x; | |
2408 | } | |
2409 | ||
2410 | /* Load VALUE into DEST. TEMP is as for loongarch_force_temporary. */ | |
2411 | ||
2412 | void | |
2413 | loongarch_move_integer (rtx temp, rtx dest, unsigned HOST_WIDE_INT value) | |
2414 | { | |
2415 | struct loongarch_integer_op codes[LARCH_MAX_INTEGER_OPS]; | |
2416 | machine_mode mode; | |
2417 | unsigned int i, num_ops; | |
2418 | rtx x; | |
2419 | ||
2420 | mode = GET_MODE (dest); | |
2421 | num_ops = loongarch_build_integer (codes, value); | |
2422 | ||
2423 | /* Apply each binary operation to X. Invariant: X is a legitimate | |
2424 | source operand for a SET pattern. */ | |
2425 | x = GEN_INT (codes[0].value); | |
2426 | for (i = 1; i < num_ops; i++) | |
2427 | { | |
2428 | if (!can_create_pseudo_p ()) | |
2429 | { | |
2430 | emit_insn (gen_rtx_SET (temp, x)); | |
2431 | x = temp; | |
2432 | } | |
2433 | else | |
2434 | x = force_reg (mode, x); | |
2435 | ||
2436 | switch (codes[i].method) | |
2437 | { | |
2438 | case METHOD_NORMAL: | |
2439 | x = gen_rtx_fmt_ee (codes[i].code, mode, x, | |
2440 | GEN_INT (codes[i].value)); | |
2441 | break; | |
2442 | case METHOD_LU32I: | |
2443 | emit_insn ( | |
2444 | gen_rtx_SET (x, | |
2445 | gen_rtx_IOR (DImode, | |
2446 | gen_rtx_ZERO_EXTEND ( | |
2447 | DImode, gen_rtx_SUBREG (SImode, x, 0)), | |
2448 | GEN_INT (codes[i].value)))); | |
2449 | break; | |
2450 | case METHOD_LU52I: | |
2451 | emit_insn (gen_lu52i_d (x, x, GEN_INT (0xfffffffffffff), | |
2452 | GEN_INT (codes[i].value))); | |
2453 | break; | |
2454 | case METHOD_INSV: | |
2455 | emit_insn ( | |
2456 | gen_rtx_SET (gen_rtx_ZERO_EXTRACT (DImode, x, GEN_INT (20), | |
2457 | GEN_INT (32)), | |
2458 | gen_rtx_REG (DImode, 0))); | |
2459 | break; | |
2460 | default: | |
2461 | gcc_unreachable (); | |
2462 | } | |
2463 | } | |
2464 | ||
2465 | emit_insn (gen_rtx_SET (dest, x)); | |
2466 | } | |
2467 | ||
2468 | /* Subroutine of loongarch_legitimize_move. Move constant SRC into register | |
2469 | DEST given that SRC satisfies immediate_operand but doesn't satisfy | |
2470 | move_operand. */ | |
2471 | ||
2472 | static void | |
2473 | loongarch_legitimize_const_move (machine_mode mode, rtx dest, rtx src) | |
2474 | { | |
2475 | rtx base, offset; | |
2476 | ||
2477 | /* Split moves of big integers into smaller pieces. */ | |
2478 | if (splittable_const_int_operand (src, mode)) | |
2479 | { | |
2480 | loongarch_move_integer (dest, dest, INTVAL (src)); | |
2481 | return; | |
2482 | } | |
2483 | ||
2484 | /* Generate the appropriate access sequences for TLS symbols. */ | |
2485 | if (loongarch_tls_symbol_p (src)) | |
2486 | { | |
2487 | loongarch_emit_move (dest, loongarch_legitimize_tls_address (src)); | |
2488 | return; | |
2489 | } | |
2490 | ||
2491 | /* If we have (const (plus symbol offset)), and that expression cannot | |
2492 | be forced into memory, load the symbol first and add in the offset. | |
2493 | prefer to do this even if the constant _can_ be forced into memory, | |
2494 | as it usually produces better code. */ | |
2495 | split_const (src, &base, &offset); | |
2496 | if (offset != const0_rtx | |
2497 | && (targetm.cannot_force_const_mem (mode, src) | |
2498 | || (can_create_pseudo_p ()))) | |
2499 | { | |
2500 | base = loongarch_force_temporary (dest, base); | |
2501 | loongarch_emit_move (dest, | |
2502 | loongarch_add_offset (NULL, base, INTVAL (offset))); | |
2503 | return; | |
2504 | } | |
2505 | ||
2506 | src = force_const_mem (mode, src); | |
2507 | ||
2508 | loongarch_emit_move (dest, src); | |
2509 | } | |
2510 | ||
2511 | /* If (set DEST SRC) is not a valid move instruction, emit an equivalent | |
2512 | sequence that is valid. */ | |
2513 | ||
2514 | bool | |
2515 | loongarch_legitimize_move (machine_mode mode, rtx dest, rtx src) | |
2516 | { | |
2517 | if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode)) | |
2518 | { | |
2519 | loongarch_emit_move (dest, force_reg (mode, src)); | |
2520 | return true; | |
2521 | } | |
2522 | ||
2523 | /* Both src and dest are non-registers; one special case is supported where | |
2524 | the source is (const_int 0) and the store can source the zero register. | |
2525 | */ | |
2526 | if (!register_operand (dest, mode) && !register_operand (src, mode) | |
2527 | && !const_0_operand (src, mode)) | |
2528 | { | |
2529 | loongarch_emit_move (dest, force_reg (mode, src)); | |
2530 | return true; | |
2531 | } | |
2532 | ||
2533 | /* We need to deal with constants that would be legitimate | |
2534 | immediate_operands but aren't legitimate move_operands. */ | |
2535 | if (CONSTANT_P (src) && !move_operand (src, mode)) | |
2536 | { | |
2537 | loongarch_legitimize_const_move (mode, dest, src); | |
2538 | set_unique_reg_note (get_last_insn (), REG_EQUAL, copy_rtx (src)); | |
2539 | return true; | |
2540 | } | |
2541 | ||
2542 | return false; | |
2543 | } | |
2544 | ||
2545 | /* Return true if OP refers to small data symbols directly. */ | |
2546 | ||
2547 | static int | |
2548 | loongarch_small_data_pattern_1 (rtx x) | |
2549 | { | |
2550 | subrtx_var_iterator::array_type array; | |
2551 | FOR_EACH_SUBRTX_VAR (iter, array, x, ALL) | |
2552 | { | |
2553 | rtx x = *iter; | |
2554 | ||
2555 | /* We make no particular guarantee about which symbolic constants are | |
2556 | acceptable as asm operands versus which must be forced into a GPR. */ | |
2557 | if (GET_CODE (x) == ASM_OPERANDS) | |
2558 | iter.skip_subrtxes (); | |
2559 | else if (MEM_P (x)) | |
2560 | { | |
2561 | if (loongarch_small_data_pattern_1 (XEXP (x, 0))) | |
2562 | return true; | |
2563 | iter.skip_subrtxes (); | |
2564 | } | |
2565 | } | |
2566 | return false; | |
2567 | } | |
2568 | ||
2569 | /* Return true if OP refers to small data symbols directly. */ | |
2570 | ||
2571 | bool | |
2572 | loongarch_small_data_pattern_p (rtx op) | |
2573 | { | |
2574 | return loongarch_small_data_pattern_1 (op); | |
2575 | } | |
2576 | ||
2577 | /* Rewrite *LOC so that it refers to small data using explicit | |
2578 | relocations. */ | |
2579 | ||
2580 | static void | |
2581 | loongarch_rewrite_small_data_1 (rtx *loc) | |
2582 | { | |
2583 | subrtx_ptr_iterator::array_type array; | |
2584 | FOR_EACH_SUBRTX_PTR (iter, array, loc, ALL) | |
2585 | { | |
2586 | rtx *loc = *iter; | |
2587 | if (MEM_P (*loc)) | |
2588 | { | |
2589 | loongarch_rewrite_small_data_1 (&XEXP (*loc, 0)); | |
2590 | iter.skip_subrtxes (); | |
2591 | } | |
2592 | } | |
2593 | } | |
2594 | ||
2595 | /* Rewrite instruction pattern PATTERN so that it refers to small data | |
2596 | using explicit relocations. */ | |
2597 | ||
2598 | rtx | |
2599 | loongarch_rewrite_small_data (rtx pattern) | |
2600 | { | |
2601 | pattern = copy_insn (pattern); | |
2602 | loongarch_rewrite_small_data_1 (&pattern); | |
2603 | return pattern; | |
2604 | } | |
2605 | ||
2606 | /* The cost of loading values from the constant pool. It should be | |
2607 | larger than the cost of any constant we want to synthesize inline. */ | |
2608 | #define CONSTANT_POOL_COST COSTS_N_INSNS (8) | |
2609 | ||
2610 | /* Return true if there is a instruction that implements CODE | |
2611 | and if that instruction accepts X as an immediate operand. */ | |
2612 | ||
2613 | static int | |
2614 | loongarch_immediate_operand_p (int code, HOST_WIDE_INT x) | |
2615 | { | |
2616 | switch (code) | |
2617 | { | |
2618 | case ASHIFT: | |
2619 | case ASHIFTRT: | |
2620 | case LSHIFTRT: | |
2621 | /* All shift counts are truncated to a valid constant. */ | |
2622 | return true; | |
2623 | ||
2624 | case ROTATE: | |
2625 | case ROTATERT: | |
2626 | return true; | |
2627 | ||
2628 | case AND: | |
2629 | case IOR: | |
2630 | case XOR: | |
2631 | /* These instructions take 12-bit unsigned immediates. */ | |
2632 | return IMM12_OPERAND_UNSIGNED (x); | |
2633 | ||
2634 | case PLUS: | |
2635 | case LT: | |
2636 | case LTU: | |
2637 | /* These instructions take 12-bit signed immediates. */ | |
2638 | return IMM12_OPERAND (x); | |
2639 | ||
2640 | case EQ: | |
2641 | case NE: | |
2642 | case GT: | |
2643 | case GTU: | |
2644 | /* The "immediate" forms of these instructions are really | |
2645 | implemented as comparisons with register 0. */ | |
2646 | return x == 0; | |
2647 | ||
2648 | case GE: | |
2649 | case GEU: | |
2650 | /* Likewise, meaning that the only valid immediate operand is 1. */ | |
2651 | return x == 1; | |
2652 | ||
2653 | case LE: | |
2654 | /* We add 1 to the immediate and use SLT. */ | |
2655 | return IMM12_OPERAND (x + 1); | |
2656 | ||
2657 | case LEU: | |
2658 | /* Likewise SLTU, but reject the always-true case. */ | |
2659 | return IMM12_OPERAND (x + 1) && x + 1 != 0; | |
2660 | ||
2661 | case SIGN_EXTRACT: | |
2662 | case ZERO_EXTRACT: | |
2663 | /* The bit position and size are immediate operands. */ | |
2664 | return 1; | |
2665 | ||
2666 | default: | |
2667 | /* By default assume that $0 can be used for 0. */ | |
2668 | return x == 0; | |
2669 | } | |
2670 | } | |
2671 | ||
2672 | /* Return the cost of binary operation X, given that the instruction | |
2673 | sequence for a word-sized or smaller operation has cost SINGLE_COST | |
2674 | and that the sequence of a double-word operation has cost DOUBLE_COST. | |
2675 | If SPEED is true, optimize for speed otherwise optimize for size. */ | |
2676 | ||
2677 | static int | |
2678 | loongarch_binary_cost (rtx x, int single_cost, int double_cost, bool speed) | |
2679 | { | |
2680 | int cost; | |
2681 | ||
2682 | if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD * 2) | |
2683 | cost = double_cost; | |
2684 | else | |
2685 | cost = single_cost; | |
2686 | return (cost | |
2687 | + set_src_cost (XEXP (x, 0), GET_MODE (x), speed) | |
2688 | + rtx_cost (XEXP (x, 1), GET_MODE (x), GET_CODE (x), 1, speed)); | |
2689 | } | |
2690 | ||
2691 | /* Return the cost of floating-point multiplications of mode MODE. */ | |
2692 | ||
2693 | static int | |
2694 | loongarch_fp_mult_cost (machine_mode mode) | |
2695 | { | |
2696 | return mode == DFmode ? loongarch_cost->fp_mult_df | |
2697 | : loongarch_cost->fp_mult_sf; | |
2698 | } | |
2699 | ||
2700 | /* Return the cost of floating-point divisions of mode MODE. */ | |
2701 | ||
2702 | static int | |
2703 | loongarch_fp_div_cost (machine_mode mode) | |
2704 | { | |
2705 | return mode == DFmode ? loongarch_cost->fp_div_df | |
2706 | : loongarch_cost->fp_div_sf; | |
2707 | } | |
2708 | ||
2709 | /* Return the cost of sign-extending OP to mode MODE, not including the | |
2710 | cost of OP itself. */ | |
2711 | ||
2712 | static int | |
2713 | loongarch_sign_extend_cost (rtx op) | |
2714 | { | |
2715 | if (MEM_P (op)) | |
2716 | /* Extended loads are as cheap as unextended ones. */ | |
2717 | return 0; | |
2718 | ||
2719 | return COSTS_N_INSNS (1); | |
2720 | } | |
2721 | ||
2722 | /* Return the cost of zero-extending OP to mode MODE, not including the | |
2723 | cost of OP itself. */ | |
2724 | ||
2725 | static int | |
2726 | loongarch_zero_extend_cost (rtx op) | |
2727 | { | |
2728 | if (MEM_P (op)) | |
2729 | /* Extended loads are as cheap as unextended ones. */ | |
2730 | return 0; | |
2731 | ||
2732 | /* We can use ANDI. */ | |
2733 | return COSTS_N_INSNS (1); | |
2734 | } | |
2735 | ||
2736 | /* Return the cost of moving between two registers of mode MODE, | |
2737 | assuming that the move will be in pieces of at most UNITS bytes. */ | |
2738 | ||
2739 | static int | |
2740 | loongarch_set_reg_reg_piece_cost (machine_mode mode, unsigned int units) | |
2741 | { | |
2742 | return COSTS_N_INSNS ((GET_MODE_SIZE (mode) + units - 1) / units); | |
2743 | } | |
2744 | ||
2745 | /* Return the cost of moving between two registers of mode MODE. */ | |
2746 | ||
2747 | static int | |
2748 | loongarch_set_reg_reg_cost (machine_mode mode) | |
2749 | { | |
2750 | switch (GET_MODE_CLASS (mode)) | |
2751 | { | |
2752 | case MODE_CC: | |
2753 | return loongarch_set_reg_reg_piece_cost (mode, GET_MODE_SIZE (CCmode)); | |
2754 | ||
2755 | case MODE_FLOAT: | |
2756 | case MODE_COMPLEX_FLOAT: | |
2757 | case MODE_VECTOR_FLOAT: | |
2758 | if (TARGET_HARD_FLOAT) | |
2759 | return loongarch_set_reg_reg_piece_cost (mode, UNITS_PER_HWFPVALUE); | |
2760 | /* Fall through. */ | |
2761 | ||
2762 | default: | |
2763 | return loongarch_set_reg_reg_piece_cost (mode, UNITS_PER_WORD); | |
2764 | } | |
2765 | } | |
2766 | ||
2767 | /* Implement TARGET_RTX_COSTS. */ | |
2768 | ||
2769 | static bool | |
2770 | loongarch_rtx_costs (rtx x, machine_mode mode, int outer_code, | |
2771 | int opno ATTRIBUTE_UNUSED, int *total, bool speed) | |
2772 | { | |
2773 | int code = GET_CODE (x); | |
2774 | bool float_mode_p = FLOAT_MODE_P (mode); | |
2775 | int cost; | |
2776 | rtx addr; | |
2777 | ||
2778 | if (outer_code == COMPARE) | |
2779 | { | |
2780 | gcc_assert (CONSTANT_P (x)); | |
2781 | *total = 0; | |
2782 | return true; | |
2783 | } | |
2784 | ||
2785 | switch (code) | |
2786 | { | |
2787 | case CONST_INT: | |
2788 | if (TARGET_64BIT && outer_code == AND && UINTVAL (x) == 0xffffffff) | |
2789 | { | |
2790 | *total = 0; | |
2791 | return true; | |
2792 | } | |
2793 | ||
2794 | /* When not optimizing for size, we care more about the cost | |
2795 | of hot code, and hot code is often in a loop. If a constant | |
2796 | operand needs to be forced into a register, we will often be | |
2797 | able to hoist the constant load out of the loop, so the load | |
2798 | should not contribute to the cost. */ | |
2799 | if (speed || loongarch_immediate_operand_p (outer_code, INTVAL (x))) | |
2800 | { | |
2801 | *total = 0; | |
2802 | return true; | |
2803 | } | |
2804 | /* Fall through. */ | |
2805 | ||
2806 | case CONST: | |
2807 | case SYMBOL_REF: | |
2808 | case LABEL_REF: | |
2809 | case CONST_DOUBLE: | |
2810 | cost = loongarch_const_insns (x); | |
2811 | if (cost > 0) | |
2812 | { | |
2813 | if (cost == 1 && outer_code == SET | |
2814 | && !(float_mode_p && TARGET_HARD_FLOAT)) | |
2815 | cost = 0; | |
2816 | else if ((outer_code == SET || GET_MODE (x) == VOIDmode)) | |
2817 | cost = 1; | |
2818 | *total = COSTS_N_INSNS (cost); | |
2819 | return true; | |
2820 | } | |
2821 | /* The value will need to be fetched from the constant pool. */ | |
2822 | *total = CONSTANT_POOL_COST; | |
2823 | return true; | |
2824 | ||
2825 | case MEM: | |
2826 | /* If the address is legitimate, return the number of | |
2827 | instructions it needs. */ | |
2828 | addr = XEXP (x, 0); | |
2829 | /* Check for a scaled indexed address. */ | |
2830 | if (loongarch_index_address_p (addr, mode)) | |
2831 | { | |
2832 | *total = COSTS_N_INSNS (2); | |
2833 | return true; | |
2834 | } | |
2835 | cost = loongarch_address_insns (addr, mode, true); | |
2836 | if (cost > 0) | |
2837 | { | |
2838 | *total = COSTS_N_INSNS (cost + 1); | |
2839 | return true; | |
2840 | } | |
2841 | /* Otherwise use the default handling. */ | |
2842 | return false; | |
2843 | ||
2844 | case FFS: | |
2845 | *total = COSTS_N_INSNS (6); | |
2846 | return false; | |
2847 | ||
2848 | case NOT: | |
2849 | *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1); | |
2850 | return false; | |
2851 | ||
2852 | case AND: | |
2853 | /* Check for a *clear_upper32 pattern and treat it like a zero | |
2854 | extension. See the pattern's comment for details. */ | |
2855 | if (TARGET_64BIT && mode == DImode && CONST_INT_P (XEXP (x, 1)) | |
2856 | && UINTVAL (XEXP (x, 1)) == 0xffffffff) | |
2857 | { | |
2858 | *total = (loongarch_zero_extend_cost (XEXP (x, 0)) | |
2859 | + set_src_cost (XEXP (x, 0), mode, speed)); | |
2860 | return true; | |
2861 | } | |
2862 | /* (AND (NOT op0) (NOT op1) is a nor operation that can be done in | |
2863 | a single instruction. */ | |
2864 | if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == NOT) | |
2865 | { | |
2866 | cost = GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1; | |
2867 | *total = (COSTS_N_INSNS (cost) | |
2868 | + set_src_cost (XEXP (XEXP (x, 0), 0), mode, speed) | |
2869 | + set_src_cost (XEXP (XEXP (x, 1), 0), mode, speed)); | |
2870 | return true; | |
2871 | } | |
2872 | ||
2873 | /* Fall through. */ | |
2874 | ||
2875 | case IOR: | |
2876 | case XOR: | |
2877 | /* Double-word operations use two single-word operations. */ | |
2878 | *total = loongarch_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2), | |
2879 | speed); | |
2880 | return true; | |
2881 | ||
2882 | case ASHIFT: | |
2883 | case ASHIFTRT: | |
2884 | case LSHIFTRT: | |
2885 | case ROTATE: | |
2886 | case ROTATERT: | |
2887 | if (CONSTANT_P (XEXP (x, 1))) | |
2888 | *total = loongarch_binary_cost (x, COSTS_N_INSNS (1), | |
2889 | COSTS_N_INSNS (4), speed); | |
2890 | else | |
2891 | *total = loongarch_binary_cost (x, COSTS_N_INSNS (1), | |
2892 | COSTS_N_INSNS (12), speed); | |
2893 | return true; | |
2894 | ||
2895 | case ABS: | |
2896 | if (float_mode_p) | |
2897 | *total = loongarch_cost->fp_add; | |
2898 | else | |
2899 | *total = COSTS_N_INSNS (4); | |
2900 | return false; | |
2901 | ||
2902 | case LT: | |
2903 | case LTU: | |
2904 | case LE: | |
2905 | case LEU: | |
2906 | case GT: | |
2907 | case GTU: | |
2908 | case GE: | |
2909 | case GEU: | |
2910 | case EQ: | |
2911 | case NE: | |
2912 | case UNORDERED: | |
2913 | case LTGT: | |
2914 | case UNGE: | |
2915 | case UNGT: | |
2916 | case UNLE: | |
2917 | case UNLT: | |
2918 | /* Branch comparisons have VOIDmode, so use the first operand's | |
2919 | mode instead. */ | |
2920 | mode = GET_MODE (XEXP (x, 0)); | |
2921 | if (FLOAT_MODE_P (mode)) | |
2922 | { | |
2923 | *total = loongarch_cost->fp_add; | |
2924 | return false; | |
2925 | } | |
2926 | *total = loongarch_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4), | |
2927 | speed); | |
2928 | return true; | |
2929 | ||
2930 | case MINUS: | |
2931 | case PLUS: | |
2932 | if (float_mode_p) | |
2933 | { | |
2934 | *total = loongarch_cost->fp_add; | |
2935 | return false; | |
2936 | } | |
2937 | ||
2938 | /* If it's an add + mult (which is equivalent to shift left) and | |
2939 | it's immediate operand satisfies const_immalsl_operand predicate. */ | |
2940 | if ((mode == SImode || (TARGET_64BIT && mode == DImode)) | |
2941 | && GET_CODE (XEXP (x, 0)) == MULT) | |
2942 | { | |
2943 | rtx op2 = XEXP (XEXP (x, 0), 1); | |
2944 | if (const_immalsl_operand (op2, mode)) | |
2945 | { | |
2946 | *total = (COSTS_N_INSNS (1) | |
2947 | + set_src_cost (XEXP (XEXP (x, 0), 0), mode, speed) | |
2948 | + set_src_cost (XEXP (x, 1), mode, speed)); | |
2949 | return true; | |
2950 | } | |
2951 | } | |
2952 | ||
2953 | /* Double-word operations require three single-word operations and | |
2954 | an SLTU. */ | |
2955 | *total = loongarch_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4), | |
2956 | speed); | |
2957 | return true; | |
2958 | ||
2959 | case NEG: | |
2960 | if (float_mode_p) | |
2961 | *total = loongarch_cost->fp_add; | |
2962 | else | |
2963 | *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1); | |
2964 | return false; | |
2965 | ||
2966 | case FMA: | |
2967 | *total = loongarch_fp_mult_cost (mode); | |
2968 | return false; | |
2969 | ||
2970 | case MULT: | |
2971 | if (float_mode_p) | |
2972 | *total = loongarch_fp_mult_cost (mode); | |
2973 | else if (mode == DImode && !TARGET_64BIT) | |
2974 | *total = (speed | |
2975 | ? loongarch_cost->int_mult_si * 3 + 6 | |
2976 | : COSTS_N_INSNS (7)); | |
2977 | else if (!speed) | |
2978 | *total = COSTS_N_INSNS (1) + 1; | |
2979 | else if (mode == DImode) | |
2980 | *total = loongarch_cost->int_mult_di; | |
2981 | else | |
2982 | *total = loongarch_cost->int_mult_si; | |
2983 | return false; | |
2984 | ||
2985 | case DIV: | |
2986 | /* Check for a reciprocal. */ | |
2987 | if (float_mode_p | |
2988 | && flag_unsafe_math_optimizations | |
2989 | && XEXP (x, 0) == CONST1_RTX (mode)) | |
2990 | { | |
2991 | if (outer_code == SQRT || GET_CODE (XEXP (x, 1)) == SQRT) | |
2992 | /* An rsqrt<mode>a or rsqrt<mode>b pattern. Count the | |
2993 | division as being free. */ | |
2994 | *total = set_src_cost (XEXP (x, 1), mode, speed); | |
2995 | else | |
2996 | *total = (loongarch_fp_div_cost (mode) | |
2997 | + set_src_cost (XEXP (x, 1), mode, speed)); | |
2998 | return true; | |
2999 | } | |
3000 | /* Fall through. */ | |
3001 | ||
3002 | case SQRT: | |
3003 | case MOD: | |
3004 | if (float_mode_p) | |
3005 | { | |
3006 | *total = loongarch_fp_div_cost (mode); | |
3007 | return false; | |
3008 | } | |
3009 | /* Fall through. */ | |
3010 | ||
3011 | case UDIV: | |
3012 | case UMOD: | |
3013 | if (!speed) | |
3014 | { | |
3015 | *total = COSTS_N_INSNS (loongarch_idiv_insns (mode)); | |
3016 | } | |
3017 | else if (mode == DImode) | |
3018 | *total = loongarch_cost->int_div_di; | |
3019 | else | |
3020 | *total = loongarch_cost->int_div_si; | |
3021 | return false; | |
3022 | ||
3023 | case SIGN_EXTEND: | |
3024 | *total = loongarch_sign_extend_cost (XEXP (x, 0)); | |
3025 | return false; | |
3026 | ||
3027 | case ZERO_EXTEND: | |
3028 | *total = loongarch_zero_extend_cost (XEXP (x, 0)); | |
3029 | return false; | |
3030 | case TRUNCATE: | |
3031 | /* Costings for highpart multiplies. Matching patterns of the form: | |
3032 | ||
3033 | (lshiftrt:DI (mult:DI (sign_extend:DI (...) | |
3034 | (sign_extend:DI (...)) | |
3035 | (const_int 32) | |
3036 | */ | |
3037 | if ((GET_CODE (XEXP (x, 0)) == ASHIFTRT | |
3038 | || GET_CODE (XEXP (x, 0)) == LSHIFTRT) | |
3039 | && CONST_INT_P (XEXP (XEXP (x, 0), 1)) | |
3040 | && ((INTVAL (XEXP (XEXP (x, 0), 1)) == 32 | |
3041 | && GET_MODE (XEXP (x, 0)) == DImode) | |
3042 | || (TARGET_64BIT | |
3043 | && INTVAL (XEXP (XEXP (x, 0), 1)) == 64 | |
3044 | && GET_MODE (XEXP (x, 0)) == TImode)) | |
3045 | && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT | |
3046 | && ((GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SIGN_EXTEND | |
3047 | && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == SIGN_EXTEND) | |
3048 | || (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ZERO_EXTEND | |
3049 | && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) | |
3050 | == ZERO_EXTEND)))) | |
3051 | { | |
3052 | if (!speed) | |
3053 | *total = COSTS_N_INSNS (1) + 1; | |
3054 | else if (mode == DImode) | |
3055 | *total = loongarch_cost->int_mult_di; | |
3056 | else | |
3057 | *total = loongarch_cost->int_mult_si; | |
3058 | ||
3059 | /* Sign extension is free, zero extension costs for DImode when | |
3060 | on a 64bit core / when DMUL is present. */ | |
3061 | for (int i = 0; i < 2; ++i) | |
3062 | { | |
3063 | rtx op = XEXP (XEXP (XEXP (x, 0), 0), i); | |
3064 | if (TARGET_64BIT | |
3065 | && GET_CODE (op) == ZERO_EXTEND | |
3066 | && GET_MODE (op) == DImode) | |
3067 | *total += rtx_cost (op, DImode, MULT, i, speed); | |
3068 | else | |
3069 | *total += rtx_cost (XEXP (op, 0), VOIDmode, GET_CODE (op), 0, | |
3070 | speed); | |
3071 | } | |
3072 | ||
3073 | return true; | |
3074 | } | |
3075 | return false; | |
3076 | ||
3077 | case FLOAT: | |
3078 | case UNSIGNED_FLOAT: | |
3079 | case FIX: | |
3080 | case FLOAT_EXTEND: | |
3081 | case FLOAT_TRUNCATE: | |
3082 | *total = loongarch_cost->fp_add; | |
3083 | return false; | |
3084 | ||
3085 | case SET: | |
3086 | if (register_operand (SET_DEST (x), VOIDmode) | |
3087 | && reg_or_0_operand (SET_SRC (x), VOIDmode)) | |
3088 | { | |
3089 | *total = loongarch_set_reg_reg_cost (GET_MODE (SET_DEST (x))); | |
3090 | return true; | |
3091 | } | |
3092 | return false; | |
3093 | ||
3094 | default: | |
3095 | return false; | |
3096 | } | |
3097 | } | |
3098 | ||
3099 | /* Implement TARGET_ADDRESS_COST. */ | |
3100 | ||
3101 | static int | |
3102 | loongarch_address_cost (rtx addr, machine_mode mode, | |
3103 | addr_space_t as ATTRIBUTE_UNUSED, | |
3104 | bool speed ATTRIBUTE_UNUSED) | |
3105 | { | |
3106 | return loongarch_address_insns (addr, mode, false); | |
3107 | } | |
3108 | ||
3109 | /* Return one word of double-word value OP, taking into account the fixed | |
3110 | endianness of certain registers. HIGH_P is true to select the high part, | |
3111 | false to select the low part. */ | |
3112 | ||
3113 | rtx | |
3114 | loongarch_subword (rtx op, bool high_p) | |
3115 | { | |
3116 | unsigned int byte; | |
3117 | machine_mode mode; | |
3118 | ||
3119 | byte = high_p ? UNITS_PER_WORD : 0; | |
3120 | mode = GET_MODE (op); | |
3121 | if (mode == VOIDmode) | |
3122 | mode = TARGET_64BIT ? TImode : DImode; | |
3123 | ||
3124 | if (FP_REG_RTX_P (op)) | |
3125 | return gen_rtx_REG (word_mode, REGNO (op) + high_p); | |
3126 | ||
3127 | if (MEM_P (op)) | |
3128 | return loongarch_rewrite_small_data (adjust_address (op, word_mode, byte)); | |
3129 | ||
3130 | return simplify_gen_subreg (word_mode, op, mode, byte); | |
3131 | } | |
3132 | ||
3133 | /* Return true if a move from SRC to DEST should be split into two. | |
3134 | SPLIT_TYPE describes the split condition. */ | |
3135 | ||
3136 | bool | |
3137 | loongarch_split_move_p (rtx dest, rtx src) | |
3138 | { | |
3139 | /* FPR-to-FPR moves can be done in a single instruction, if they're | |
3140 | allowed at all. */ | |
3141 | unsigned int size = GET_MODE_SIZE (GET_MODE (dest)); | |
3142 | if (size == 8 && FP_REG_RTX_P (src) && FP_REG_RTX_P (dest)) | |
3143 | return false; | |
3144 | ||
3145 | /* Check for floating-point loads and stores. */ | |
3146 | if (size == 8) | |
3147 | { | |
3148 | if (FP_REG_RTX_P (dest) && MEM_P (src)) | |
3149 | return false; | |
3150 | if (FP_REG_RTX_P (src) && MEM_P (dest)) | |
3151 | return false; | |
3152 | } | |
3153 | /* Otherwise split all multiword moves. */ | |
3154 | return size > UNITS_PER_WORD; | |
3155 | } | |
3156 | ||
3157 | /* Split a move from SRC to DEST, given that loongarch_split_move_p holds. | |
3158 | SPLIT_TYPE describes the split condition. */ | |
3159 | ||
3160 | void | |
3161 | loongarch_split_move (rtx dest, rtx src, rtx insn_) | |
3162 | { | |
3163 | rtx low_dest; | |
3164 | ||
3165 | gcc_checking_assert (loongarch_split_move_p (dest, src)); | |
3166 | if (FP_REG_RTX_P (dest) || FP_REG_RTX_P (src)) | |
3167 | { | |
3168 | if (!TARGET_64BIT && GET_MODE (dest) == DImode) | |
3169 | emit_insn (gen_move_doubleword_fprdi (dest, src)); | |
3170 | else if (!TARGET_64BIT && GET_MODE (dest) == DFmode) | |
3171 | emit_insn (gen_move_doubleword_fprdf (dest, src)); | |
3172 | else if (TARGET_64BIT && GET_MODE (dest) == TFmode) | |
3173 | emit_insn (gen_move_doubleword_fprtf (dest, src)); | |
3174 | else | |
3175 | gcc_unreachable (); | |
3176 | } | |
3177 | else | |
3178 | { | |
3179 | /* The operation can be split into two normal moves. Decide in | |
3180 | which order to do them. */ | |
3181 | low_dest = loongarch_subword (dest, false); | |
3182 | if (REG_P (low_dest) && reg_overlap_mentioned_p (low_dest, src)) | |
3183 | { | |
3184 | loongarch_emit_move (loongarch_subword (dest, true), | |
3185 | loongarch_subword (src, true)); | |
3186 | loongarch_emit_move (low_dest, loongarch_subword (src, false)); | |
3187 | } | |
3188 | else | |
3189 | { | |
3190 | loongarch_emit_move (low_dest, loongarch_subword (src, false)); | |
3191 | loongarch_emit_move (loongarch_subword (dest, true), | |
3192 | loongarch_subword (src, true)); | |
3193 | } | |
3194 | } | |
3195 | ||
3196 | /* This is a hack. See if the next insn uses DEST and if so, see if we | |
3197 | can forward SRC for DEST. This is most useful if the next insn is a | |
3198 | simple store. */ | |
3199 | rtx_insn *insn = (rtx_insn *) insn_; | |
3200 | struct loongarch_address_info addr = {}; | |
3201 | if (insn) | |
3202 | { | |
3203 | rtx_insn *next = next_nonnote_nondebug_insn_bb (insn); | |
3204 | if (next) | |
3205 | { | |
3206 | rtx set = single_set (next); | |
3207 | if (set && SET_SRC (set) == dest) | |
3208 | { | |
3209 | if (MEM_P (src)) | |
3210 | { | |
3211 | rtx tmp = XEXP (src, 0); | |
3212 | loongarch_classify_address (&addr, tmp, GET_MODE (tmp), | |
3213 | true); | |
3214 | if (addr.reg && !reg_overlap_mentioned_p (dest, addr.reg)) | |
3215 | validate_change (next, &SET_SRC (set), src, false); | |
3216 | } | |
3217 | else | |
3218 | validate_change (next, &SET_SRC (set), src, false); | |
3219 | } | |
3220 | } | |
3221 | } | |
3222 | } | |
3223 | ||
3224 | /* Return true if a move from SRC to DEST in INSN should be split. */ | |
3225 | ||
3226 | bool | |
3227 | loongarch_split_move_insn_p (rtx dest, rtx src) | |
3228 | { | |
3229 | return loongarch_split_move_p (dest, src); | |
3230 | } | |
3231 | ||
3232 | /* Split a move from SRC to DEST in INSN, given that | |
3233 | loongarch_split_move_insn_p holds. */ | |
3234 | ||
3235 | void | |
3236 | loongarch_split_move_insn (rtx dest, rtx src, rtx insn) | |
3237 | { | |
3238 | loongarch_split_move (dest, src, insn); | |
3239 | } | |
3240 | ||
3241 | /* Implement TARGET_CONSTANT_ALIGNMENT. */ | |
3242 | ||
3243 | static HOST_WIDE_INT | |
3244 | loongarch_constant_alignment (const_tree exp, HOST_WIDE_INT align) | |
3245 | { | |
3246 | if (TREE_CODE (exp) == STRING_CST || TREE_CODE (exp) == CONSTRUCTOR) | |
3247 | return MAX (align, BITS_PER_WORD); | |
3248 | return align; | |
3249 | } | |
3250 | ||
3251 | const char * | |
3252 | loongarch_output_move_index (rtx x, machine_mode mode, bool ldr) | |
3253 | { | |
3254 | int index = exact_log2 (GET_MODE_SIZE (mode)); | |
3255 | if (!IN_RANGE (index, 0, 3)) | |
3256 | return NULL; | |
3257 | ||
3258 | struct loongarch_address_info info; | |
3259 | if ((loongarch_classify_address (&info, x, mode, false) | |
3260 | && !(info.type == ADDRESS_REG_REG)) | |
3261 | || !loongarch_legitimate_address_p (mode, x, false)) | |
3262 | return NULL; | |
3263 | ||
3264 | const char *const insn[][4] = | |
3265 | { | |
3266 | { | |
3267 | "stx.b\t%z1,%0", | |
3268 | "stx.h\t%z1,%0", | |
3269 | "stx.w\t%z1,%0", | |
3270 | "stx.d\t%z1,%0", | |
3271 | }, | |
3272 | { | |
3273 | "ldx.bu\t%0,%1", | |
3274 | "ldx.hu\t%0,%1", | |
3275 | "ldx.w\t%0,%1", | |
3276 | "ldx.d\t%0,%1", | |
3277 | } | |
3278 | }; | |
3279 | ||
3280 | return insn[ldr][index]; | |
3281 | } | |
3282 | ||
3283 | const char * | |
3284 | loongarch_output_move_index_float (rtx x, machine_mode mode, bool ldr) | |
3285 | { | |
3286 | int index = exact_log2 (GET_MODE_SIZE (mode)); | |
3287 | if (!IN_RANGE (index, 2, 3)) | |
3288 | return NULL; | |
3289 | ||
3290 | struct loongarch_address_info info; | |
3291 | if ((loongarch_classify_address (&info, x, mode, false) | |
3292 | && !(info.type == ADDRESS_REG_REG)) | |
3293 | || !loongarch_legitimate_address_p (mode, x, false)) | |
3294 | return NULL; | |
3295 | ||
3296 | const char *const insn[][2] = | |
3297 | { | |
3298 | { | |
3299 | "fstx.s\t%1,%0", | |
3300 | "fstx.d\t%1,%0" | |
3301 | }, | |
3302 | { | |
3303 | "fldx.s\t%0,%1", | |
3304 | "fldx.d\t%0,%1" | |
3305 | }, | |
3306 | }; | |
3307 | ||
3308 | return insn[ldr][index-2]; | |
3309 | } | |
3310 | ||
3311 | /* Return the appropriate instructions to move SRC into DEST. Assume | |
3312 | that SRC is operand 1 and DEST is operand 0. */ | |
3313 | ||
3314 | const char * | |
3315 | loongarch_output_move (rtx dest, rtx src) | |
3316 | { | |
3317 | enum rtx_code dest_code = GET_CODE (dest); | |
3318 | enum rtx_code src_code = GET_CODE (src); | |
3319 | machine_mode mode = GET_MODE (dest); | |
3320 | bool dbl_p = (GET_MODE_SIZE (mode) == 8); | |
3321 | ||
3322 | if (loongarch_split_move_p (dest, src)) | |
3323 | return "#"; | |
3324 | ||
3325 | if ((src_code == REG && GP_REG_P (REGNO (src))) | |
3326 | || (src == CONST0_RTX (mode))) | |
3327 | { | |
3328 | if (dest_code == REG) | |
3329 | { | |
3330 | if (GP_REG_P (REGNO (dest))) | |
3331 | return "or\t%0,%z1,$r0"; | |
3332 | ||
3333 | if (FP_REG_P (REGNO (dest))) | |
3334 | return dbl_p ? "movgr2fr.d\t%0,%z1" : "movgr2fr.w\t%0,%z1"; | |
3335 | } | |
3336 | if (dest_code == MEM) | |
3337 | { | |
3338 | const char *insn = NULL; | |
3339 | insn = loongarch_output_move_index (XEXP (dest, 0), GET_MODE (dest), | |
3340 | false); | |
3341 | if (insn) | |
3342 | return insn; | |
3343 | ||
3344 | rtx offset = XEXP (dest, 0); | |
3345 | if (GET_CODE (offset) == PLUS) | |
3346 | offset = XEXP (offset, 1); | |
3347 | switch (GET_MODE_SIZE (mode)) | |
3348 | { | |
3349 | case 1: | |
3350 | return "st.b\t%z1,%0"; | |
3351 | case 2: | |
3352 | return "st.h\t%z1,%0"; | |
3353 | case 4: | |
3354 | if (const_arith_operand (offset, Pmode)) | |
3355 | return "st.w\t%z1,%0"; | |
3356 | else | |
3357 | return "stptr.w\t%z1,%0"; | |
3358 | case 8: | |
3359 | if (const_arith_operand (offset, Pmode)) | |
3360 | return "st.d\t%z1,%0"; | |
3361 | else | |
3362 | return "stptr.d\t%z1,%0"; | |
3363 | default: | |
3364 | gcc_unreachable (); | |
3365 | } | |
3366 | } | |
3367 | } | |
3368 | if (dest_code == REG && GP_REG_P (REGNO (dest))) | |
3369 | { | |
3370 | if (src_code == REG) | |
3371 | if (FP_REG_P (REGNO (src))) | |
3372 | return dbl_p ? "movfr2gr.d\t%0,%1" : "movfr2gr.s\t%0,%1"; | |
3373 | ||
3374 | if (src_code == MEM) | |
3375 | { | |
3376 | const char *insn = NULL; | |
3377 | insn = loongarch_output_move_index (XEXP (src, 0), GET_MODE (src), | |
3378 | true); | |
3379 | if (insn) | |
3380 | return insn; | |
3381 | ||
3382 | rtx offset = XEXP (src, 0); | |
3383 | if (GET_CODE (offset) == PLUS) | |
3384 | offset = XEXP (offset, 1); | |
3385 | switch (GET_MODE_SIZE (mode)) | |
3386 | { | |
3387 | case 1: | |
3388 | return "ld.bu\t%0,%1"; | |
3389 | case 2: | |
3390 | return "ld.hu\t%0,%1"; | |
3391 | case 4: | |
3392 | if (const_arith_operand (offset, Pmode)) | |
3393 | return "ld.w\t%0,%1"; | |
3394 | else | |
3395 | return "ldptr.w\t%0,%1"; | |
3396 | case 8: | |
3397 | if (const_arith_operand (offset, Pmode)) | |
3398 | return "ld.d\t%0,%1"; | |
3399 | else | |
3400 | return "ldptr.d\t%0,%1"; | |
3401 | default: | |
3402 | gcc_unreachable (); | |
3403 | } | |
3404 | } | |
3405 | ||
3406 | if (src_code == CONST_INT) | |
3407 | { | |
3408 | if (LU12I_INT (src)) | |
3409 | return "lu12i.w\t%0,%1>>12\t\t\t# %X1"; | |
3410 | else if (IMM12_INT (src)) | |
3411 | return "addi.w\t%0,$r0,%1\t\t\t# %X1"; | |
3412 | else if (IMM12_INT_UNSIGNED (src)) | |
3413 | return "ori\t%0,$r0,%1\t\t\t# %X1"; | |
3414 | else if (LU52I_INT (src)) | |
3415 | return "lu52i.d\t%0,$r0,%X1>>52\t\t\t# %1"; | |
3416 | else | |
3417 | gcc_unreachable (); | |
3418 | } | |
3419 | ||
3420 | if (symbolic_operand (src, VOIDmode)) | |
3421 | { | |
3422 | if ((TARGET_CMODEL_TINY && (!loongarch_global_symbol_p (src) | |
3423 | || loongarch_symbol_binds_local_p (src))) | |
3424 | || (TARGET_CMODEL_TINY_STATIC && !loongarch_weak_symbol_p (src))) | |
3425 | { | |
3426 | /* The symbol must be aligned to 4 byte. */ | |
3427 | unsigned int align; | |
3428 | ||
3429 | if (LABEL_REF_P (src)) | |
3430 | align = 32 /* Whatever. */; | |
3431 | else if (CONSTANT_POOL_ADDRESS_P (src)) | |
3432 | align = GET_MODE_ALIGNMENT (get_pool_mode (src)); | |
3433 | else if (TREE_CONSTANT_POOL_ADDRESS_P (src)) | |
3434 | { | |
3435 | tree exp = SYMBOL_REF_DECL (src); | |
3436 | align = TYPE_ALIGN (TREE_TYPE (exp)); | |
3437 | align = loongarch_constant_alignment (exp, align); | |
3438 | } | |
3439 | else if (SYMBOL_REF_DECL (src)) | |
3440 | align = DECL_ALIGN (SYMBOL_REF_DECL (src)); | |
3441 | else if (SYMBOL_REF_HAS_BLOCK_INFO_P (src) | |
3442 | && SYMBOL_REF_BLOCK (src) != NULL) | |
3443 | align = SYMBOL_REF_BLOCK (src)->alignment; | |
3444 | else | |
3445 | align = BITS_PER_UNIT; | |
3446 | ||
3447 | if (align % (4 * 8) == 0) | |
3448 | return "pcaddi\t%0,%%pcrel(%1)>>2"; | |
3449 | } | |
3450 | if (TARGET_CMODEL_TINY | |
3451 | || TARGET_CMODEL_TINY_STATIC | |
3452 | || TARGET_CMODEL_NORMAL | |
3453 | || TARGET_CMODEL_LARGE) | |
3454 | { | |
3455 | if (!loongarch_global_symbol_p (src) | |
3456 | || loongarch_symbol_binds_local_p (src)) | |
3457 | return "la.local\t%0,%1"; | |
3458 | else | |
3459 | return "la.global\t%0,%1"; | |
3460 | } | |
3461 | if (TARGET_CMODEL_EXTREME) | |
3462 | { | |
3463 | sorry ("Normal symbol loading not implemented in extreme mode."); | |
3464 | gcc_unreachable (); | |
3465 | } | |
3466 | ||
3467 | } | |
3468 | } | |
3469 | if (src_code == REG && FP_REG_P (REGNO (src))) | |
3470 | { | |
3471 | if (dest_code == REG && FP_REG_P (REGNO (dest))) | |
3472 | return dbl_p ? "fmov.d\t%0,%1" : "fmov.s\t%0,%1"; | |
3473 | ||
3474 | if (dest_code == MEM) | |
3475 | { | |
3476 | const char *insn = NULL; | |
3477 | insn = loongarch_output_move_index_float (XEXP (dest, 0), | |
3478 | GET_MODE (dest), | |
3479 | false); | |
3480 | if (insn) | |
3481 | return insn; | |
3482 | ||
3483 | return dbl_p ? "fst.d\t%1,%0" : "fst.s\t%1,%0"; | |
3484 | } | |
3485 | } | |
3486 | if (dest_code == REG && FP_REG_P (REGNO (dest))) | |
3487 | { | |
3488 | if (src_code == MEM) | |
3489 | { | |
3490 | const char *insn = NULL; | |
3491 | insn = loongarch_output_move_index_float (XEXP (src, 0), | |
3492 | GET_MODE (src), | |
3493 | true); | |
3494 | if (insn) | |
3495 | return insn; | |
3496 | ||
3497 | return dbl_p ? "fld.d\t%0,%1" : "fld.s\t%0,%1"; | |
3498 | } | |
3499 | } | |
3500 | gcc_unreachable (); | |
3501 | } | |
3502 | ||
3503 | /* Return true if CMP1 is a suitable second operand for integer ordering | |
3504 | test CODE. */ | |
3505 | ||
3506 | static bool | |
3507 | loongarch_int_order_operand_ok_p (enum rtx_code code, rtx cmp1) | |
3508 | { | |
3509 | switch (code) | |
3510 | { | |
3511 | case GT: | |
3512 | case GTU: | |
3513 | return reg_or_0_operand (cmp1, VOIDmode); | |
3514 | ||
3515 | case GE: | |
3516 | case GEU: | |
3517 | return cmp1 == const1_rtx; | |
3518 | ||
3519 | case LT: | |
3520 | case LTU: | |
3521 | return arith_operand (cmp1, VOIDmode); | |
3522 | ||
3523 | case LE: | |
3524 | return sle_operand (cmp1, VOIDmode); | |
3525 | ||
3526 | case LEU: | |
3527 | return sleu_operand (cmp1, VOIDmode); | |
3528 | ||
3529 | default: | |
3530 | gcc_unreachable (); | |
3531 | } | |
3532 | } | |
3533 | ||
3534 | /* Return true if *CMP1 (of mode MODE) is a valid second operand for | |
3535 | integer ordering test *CODE, or if an equivalent combination can | |
3536 | be formed by adjusting *CODE and *CMP1. When returning true, update | |
3537 | *CODE and *CMP1 with the chosen code and operand, otherwise leave | |
3538 | them alone. */ | |
3539 | ||
3540 | static bool | |
3541 | loongarch_canonicalize_int_order_test (enum rtx_code *code, rtx *cmp1, | |
3542 | machine_mode mode) | |
3543 | { | |
3544 | HOST_WIDE_INT plus_one; | |
3545 | ||
3546 | if (loongarch_int_order_operand_ok_p (*code, *cmp1)) | |
3547 | return true; | |
3548 | ||
3549 | if (CONST_INT_P (*cmp1)) | |
3550 | switch (*code) | |
3551 | { | |
3552 | case LE: | |
3553 | plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode); | |
3554 | if (INTVAL (*cmp1) < plus_one) | |
3555 | { | |
3556 | *code = LT; | |
3557 | *cmp1 = force_reg (mode, GEN_INT (plus_one)); | |
3558 | return true; | |
3559 | } | |
3560 | break; | |
3561 | ||
3562 | case LEU: | |
3563 | plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode); | |
3564 | if (plus_one != 0) | |
3565 | { | |
3566 | *code = LTU; | |
3567 | *cmp1 = force_reg (mode, GEN_INT (plus_one)); | |
3568 | return true; | |
3569 | } | |
3570 | break; | |
3571 | ||
3572 | default: | |
3573 | break; | |
3574 | } | |
3575 | return false; | |
3576 | } | |
3577 | ||
3578 | /* Compare CMP0 and CMP1 using ordering test CODE and store the result | |
3579 | in TARGET. CMP0 and TARGET are register_operands. If INVERT_PTR | |
3580 | is nonnull, it's OK to set TARGET to the inverse of the result and | |
3581 | flip *INVERT_PTR instead. */ | |
3582 | ||
3583 | static void | |
3584 | loongarch_emit_int_order_test (enum rtx_code code, bool *invert_ptr, | |
3585 | rtx target, rtx cmp0, rtx cmp1) | |
3586 | { | |
3587 | machine_mode mode; | |
3588 | ||
3589 | /* First see if there is a LoongArch instruction that can do this operation. | |
3590 | If not, try doing the same for the inverse operation. If that also | |
3591 | fails, force CMP1 into a register and try again. */ | |
3592 | mode = GET_MODE (cmp0); | |
3593 | if (loongarch_canonicalize_int_order_test (&code, &cmp1, mode)) | |
3594 | loongarch_emit_binary (code, target, cmp0, cmp1); | |
3595 | else | |
3596 | { | |
3597 | enum rtx_code inv_code = reverse_condition (code); | |
3598 | if (!loongarch_canonicalize_int_order_test (&inv_code, &cmp1, mode)) | |
3599 | { | |
3600 | cmp1 = force_reg (mode, cmp1); | |
3601 | loongarch_emit_int_order_test (code, invert_ptr, target, cmp0, cmp1); | |
3602 | } | |
3603 | else if (invert_ptr == 0) | |
3604 | { | |
3605 | rtx inv_target; | |
3606 | ||
3607 | inv_target = loongarch_force_binary (GET_MODE (target), | |
3608 | inv_code, cmp0, cmp1); | |
3609 | loongarch_emit_binary (XOR, target, inv_target, const1_rtx); | |
3610 | } | |
3611 | else | |
3612 | { | |
3613 | *invert_ptr = !*invert_ptr; | |
3614 | loongarch_emit_binary (inv_code, target, cmp0, cmp1); | |
3615 | } | |
3616 | } | |
3617 | } | |
3618 | ||
3619 | /* Return a register that is zero if CMP0 and CMP1 are equal. | |
3620 | The register will have the same mode as CMP0. */ | |
3621 | ||
3622 | static rtx | |
3623 | loongarch_zero_if_equal (rtx cmp0, rtx cmp1) | |
3624 | { | |
3625 | if (cmp1 == const0_rtx) | |
3626 | return cmp0; | |
3627 | ||
3628 | if (uns_arith_operand (cmp1, VOIDmode)) | |
3629 | return expand_binop (GET_MODE (cmp0), xor_optab, cmp0, cmp1, 0, 0, | |
3630 | OPTAB_DIRECT); | |
3631 | ||
3632 | return expand_binop (GET_MODE (cmp0), sub_optab, cmp0, cmp1, 0, 0, | |
3633 | OPTAB_DIRECT); | |
3634 | } | |
3635 | ||
3636 | /* Allocate a floating-point condition-code register of mode MODE. */ | |
3637 | ||
3638 | static rtx | |
3639 | loongarch_allocate_fcc (machine_mode mode) | |
3640 | { | |
3641 | unsigned int regno, count; | |
3642 | ||
3643 | gcc_assert (TARGET_HARD_FLOAT); | |
3644 | ||
3645 | if (mode == FCCmode) | |
3646 | count = 1; | |
3647 | else | |
3648 | gcc_unreachable (); | |
3649 | ||
3650 | cfun->machine->next_fcc += -cfun->machine->next_fcc & (count - 1); | |
3651 | if (cfun->machine->next_fcc > FCC_REG_LAST - FCC_REG_FIRST) | |
3652 | cfun->machine->next_fcc = 0; | |
3653 | ||
3654 | regno = FCC_REG_FIRST + cfun->machine->next_fcc; | |
3655 | cfun->machine->next_fcc += count; | |
3656 | return gen_rtx_REG (mode, regno); | |
3657 | } | |
3658 | ||
3659 | /* Sign- or zero-extend OP0 and OP1 for integer comparisons. */ | |
3660 | ||
3661 | static void | |
3662 | loongarch_extend_comparands (rtx_code code, rtx *op0, rtx *op1) | |
3663 | { | |
3664 | /* Comparisons consider all XLEN bits, so extend sub-XLEN values. */ | |
3665 | if (GET_MODE_SIZE (word_mode) > GET_MODE_SIZE (GET_MODE (*op0))) | |
3666 | { | |
3667 | /* TODO: checkout It is more profitable to zero-extend QImode values. */ | |
3668 | if (unsigned_condition (code) == code && GET_MODE (*op0) == QImode) | |
3669 | { | |
3670 | *op0 = gen_rtx_ZERO_EXTEND (word_mode, *op0); | |
3671 | if (CONST_INT_P (*op1)) | |
3672 | *op1 = GEN_INT ((uint8_t) INTVAL (*op1)); | |
3673 | else | |
3674 | *op1 = gen_rtx_ZERO_EXTEND (word_mode, *op1); | |
3675 | } | |
3676 | else | |
3677 | { | |
3678 | *op0 = gen_rtx_SIGN_EXTEND (word_mode, *op0); | |
3679 | if (*op1 != const0_rtx) | |
3680 | *op1 = gen_rtx_SIGN_EXTEND (word_mode, *op1); | |
3681 | } | |
3682 | } | |
3683 | } | |
3684 | ||
3685 | /* Convert a comparison into something that can be used in a branch. On | |
3686 | entry, *OP0 and *OP1 are the values being compared and *CODE is the code | |
3687 | used to compare them. Update them to describe the final comparison. */ | |
3688 | ||
3689 | static void | |
3690 | loongarch_emit_int_compare (enum rtx_code *code, rtx *op0, rtx *op1) | |
3691 | { | |
3692 | static const enum rtx_code | |
3693 | mag_comparisons[][2] = {{LEU, LTU}, {GTU, GEU}, {LE, LT}, {GT, GE}}; | |
3694 | ||
3695 | if (splittable_const_int_operand (*op1, VOIDmode)) | |
3696 | { | |
3697 | HOST_WIDE_INT rhs = INTVAL (*op1); | |
3698 | ||
3699 | if (*code == EQ || *code == NE) | |
3700 | { | |
3701 | /* Convert e.g. OP0 == 2048 into OP0 - 2048 == 0. */ | |
3702 | if (IMM12_OPERAND (-rhs)) | |
3703 | { | |
3704 | *op0 = loongarch_force_binary (GET_MODE (*op0), PLUS, *op0, | |
3705 | GEN_INT (-rhs)); | |
3706 | *op1 = const0_rtx; | |
3707 | } | |
3708 | } | |
3709 | else | |
3710 | { | |
3711 | /* Convert e.g. (OP0 <= 0xFFF) into (OP0 < 0x1000). */ | |
3712 | for (size_t i = 0; i < ARRAY_SIZE (mag_comparisons); i++) | |
3713 | { | |
3714 | HOST_WIDE_INT new_rhs; | |
3715 | bool increment = *code == mag_comparisons[i][0]; | |
3716 | bool decrement = *code == mag_comparisons[i][1]; | |
3717 | if (!increment && !decrement) | |
3718 | continue; | |
3719 | ||
3720 | new_rhs = rhs + (increment ? 1 : -1); | |
3721 | if (loongarch_integer_cost (new_rhs) | |
3722 | < loongarch_integer_cost (rhs) | |
3723 | && (rhs < 0) == (new_rhs < 0)) | |
3724 | { | |
3725 | *op1 = GEN_INT (new_rhs); | |
3726 | *code = mag_comparisons[i][increment]; | |
3727 | } | |
3728 | break; | |
3729 | } | |
3730 | } | |
3731 | } | |
3732 | ||
3733 | loongarch_extend_comparands (*code, op0, op1); | |
3734 | ||
3735 | *op0 = force_reg (word_mode, *op0); | |
3736 | if (*op1 != const0_rtx) | |
3737 | *op1 = force_reg (word_mode, *op1); | |
3738 | } | |
3739 | ||
3740 | /* Like loongarch_emit_int_compare, but for floating-point comparisons. */ | |
3741 | ||
3742 | static void | |
3743 | loongarch_emit_float_compare (enum rtx_code *code, rtx *op0, rtx *op1) | |
3744 | { | |
3745 | rtx cmp_op0 = *op0; | |
3746 | rtx cmp_op1 = *op1; | |
3747 | ||
3748 | /* Floating-point tests use a separate FCMP.cond.fmt | |
3749 | comparison to set a register. The branch or conditional move will | |
3750 | then compare that register against zero. | |
3751 | ||
3752 | Set CMP_CODE to the code of the comparison instruction and | |
3753 | *CODE to the code that the branch or move should use. */ | |
3754 | enum rtx_code cmp_code = *code; | |
3755 | /* Three FP conditions cannot be implemented by reversing the | |
3756 | operands for FCMP.cond.fmt, instead a reversed condition code is | |
3757 | required and a test for false. */ | |
3758 | *code = NE; | |
3759 | *op0 = loongarch_allocate_fcc (FCCmode); | |
3760 | ||
3761 | *op1 = const0_rtx; | |
3762 | loongarch_emit_binary (cmp_code, *op0, cmp_op0, cmp_op1); | |
3763 | } | |
3764 | ||
3765 | /* Try performing the comparison in OPERANDS[1], whose arms are OPERANDS[2] | |
3766 | and OPERAND[3]. Store the result in OPERANDS[0]. | |
3767 | ||
3768 | On 64-bit targets, the mode of the comparison and target will always be | |
3769 | SImode, thus possibly narrower than that of the comparison's operands. */ | |
3770 | ||
3771 | void | |
3772 | loongarch_expand_scc (rtx operands[]) | |
3773 | { | |
3774 | rtx target = operands[0]; | |
3775 | enum rtx_code code = GET_CODE (operands[1]); | |
3776 | rtx op0 = operands[2]; | |
3777 | rtx op1 = operands[3]; | |
3778 | ||
3779 | loongarch_extend_comparands (code, &op0, &op1); | |
3780 | op0 = force_reg (word_mode, op0); | |
3781 | ||
3782 | gcc_assert (GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT); | |
3783 | ||
3784 | if (code == EQ || code == NE) | |
3785 | { | |
3786 | rtx zie = loongarch_zero_if_equal (op0, op1); | |
3787 | loongarch_emit_binary (code, target, zie, const0_rtx); | |
3788 | } | |
3789 | else | |
3790 | loongarch_emit_int_order_test (code, 0, target, op0, op1); | |
3791 | } | |
3792 | ||
3793 | /* Compare OPERANDS[1] with OPERANDS[2] using comparison code | |
3794 | CODE and jump to OPERANDS[3] if the condition holds. */ | |
3795 | ||
3796 | void | |
3797 | loongarch_expand_conditional_branch (rtx *operands) | |
3798 | { | |
3799 | enum rtx_code code = GET_CODE (operands[0]); | |
3800 | rtx op0 = operands[1]; | |
3801 | rtx op1 = operands[2]; | |
3802 | rtx condition; | |
3803 | ||
3804 | if (FLOAT_MODE_P (GET_MODE (op1))) | |
3805 | loongarch_emit_float_compare (&code, &op0, &op1); | |
3806 | else | |
3807 | loongarch_emit_int_compare (&code, &op0, &op1); | |
3808 | ||
3809 | condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1); | |
3810 | emit_jump_insn (gen_condjump (condition, operands[3])); | |
3811 | } | |
3812 | ||
3813 | /* Perform the comparison in OPERANDS[1]. Move OPERANDS[2] into OPERANDS[0] | |
3814 | if the condition holds, otherwise move OPERANDS[3] into OPERANDS[0]. */ | |
3815 | ||
3816 | void | |
3817 | loongarch_expand_conditional_move (rtx *operands) | |
3818 | { | |
3819 | enum rtx_code code = GET_CODE (operands[1]); | |
3820 | rtx op0 = XEXP (operands[1], 0); | |
3821 | rtx op1 = XEXP (operands[1], 1); | |
3822 | ||
3823 | if (FLOAT_MODE_P (GET_MODE (op1))) | |
3824 | loongarch_emit_float_compare (&code, &op0, &op1); | |
3825 | else | |
3826 | { | |
3827 | loongarch_extend_comparands (code, &op0, &op1); | |
3828 | ||
3829 | op0 = force_reg (word_mode, op0); | |
3830 | ||
3831 | if (code == EQ || code == NE) | |
3832 | { | |
3833 | op0 = loongarch_zero_if_equal (op0, op1); | |
3834 | op1 = const0_rtx; | |
3835 | } | |
3836 | else | |
3837 | { | |
3838 | /* The comparison needs a separate scc instruction. Store the | |
3839 | result of the scc in *OP0 and compare it against zero. */ | |
3840 | bool invert = false; | |
3841 | rtx target = gen_reg_rtx (GET_MODE (op0)); | |
3842 | loongarch_emit_int_order_test (code, &invert, target, op0, op1); | |
3843 | code = invert ? EQ : NE; | |
3844 | op0 = target; | |
3845 | op1 = const0_rtx; | |
3846 | } | |
3847 | } | |
3848 | ||
3849 | rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1); | |
3850 | /* There is no direct support for general conditional GP move involving | |
3851 | two registers using SEL. */ | |
3852 | if (INTEGRAL_MODE_P (GET_MODE (operands[2])) | |
3853 | && register_operand (operands[2], VOIDmode) | |
3854 | && register_operand (operands[3], VOIDmode)) | |
3855 | { | |
3856 | machine_mode mode = GET_MODE (operands[0]); | |
3857 | rtx temp = gen_reg_rtx (mode); | |
3858 | rtx temp2 = gen_reg_rtx (mode); | |
3859 | ||
3860 | emit_insn (gen_rtx_SET (temp, | |
3861 | gen_rtx_IF_THEN_ELSE (mode, cond, | |
3862 | operands[2], const0_rtx))); | |
3863 | ||
3864 | /* Flip the test for the second operand. */ | |
3865 | cond = gen_rtx_fmt_ee ((code == EQ) ? NE : EQ, GET_MODE (op0), op0, op1); | |
3866 | ||
3867 | emit_insn (gen_rtx_SET (temp2, | |
3868 | gen_rtx_IF_THEN_ELSE (mode, cond, | |
3869 | operands[3], const0_rtx))); | |
3870 | ||
3871 | /* Merge the two results, at least one is guaranteed to be zero. */ | |
3872 | emit_insn (gen_rtx_SET (operands[0], gen_rtx_IOR (mode, temp, temp2))); | |
3873 | } | |
3874 | else | |
3875 | emit_insn (gen_rtx_SET (operands[0], | |
3876 | gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), cond, | |
3877 | operands[2], operands[3]))); | |
3878 | } | |
3879 | ||
3880 | /* Implement TARGET_EXPAND_BUILTIN_VA_START. */ | |
3881 | ||
3882 | static void | |
3883 | loongarch_va_start (tree valist, rtx nextarg) | |
3884 | { | |
3885 | nextarg = plus_constant (Pmode, nextarg, -cfun->machine->varargs_size); | |
3886 | std_expand_builtin_va_start (valist, nextarg); | |
3887 | } | |
3888 | ||
3889 | /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */ | |
3890 | ||
3891 | static bool | |
3892 | loongarch_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, | |
3893 | tree exp ATTRIBUTE_UNUSED) | |
3894 | { | |
3895 | /* Always OK. */ | |
3896 | return true; | |
3897 | } | |
3898 | ||
3899 | /* Emit straight-line code to move LENGTH bytes from SRC to DEST. | |
3900 | Assume that the areas do not overlap. */ | |
3901 | ||
3902 | static void | |
3903 | loongarch_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length) | |
3904 | { | |
3905 | HOST_WIDE_INT offset, delta; | |
3906 | unsigned HOST_WIDE_INT bits; | |
3907 | int i; | |
3908 | machine_mode mode; | |
3909 | rtx *regs; | |
3910 | ||
3911 | bits = MIN (BITS_PER_WORD, MIN (MEM_ALIGN (src), MEM_ALIGN (dest))); | |
3912 | ||
3913 | mode = int_mode_for_size (bits, 0).require (); | |
3914 | delta = bits / BITS_PER_UNIT; | |
3915 | ||
3916 | /* Allocate a buffer for the temporary registers. */ | |
3917 | regs = XALLOCAVEC (rtx, length / delta); | |
3918 | ||
3919 | /* Load as many BITS-sized chunks as possible. Use a normal load if | |
3920 | the source has enough alignment, otherwise use left/right pairs. */ | |
3921 | for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) | |
3922 | { | |
3923 | regs[i] = gen_reg_rtx (mode); | |
3924 | loongarch_emit_move (regs[i], adjust_address (src, mode, offset)); | |
3925 | } | |
3926 | ||
3927 | for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++) | |
3928 | loongarch_emit_move (adjust_address (dest, mode, offset), regs[i]); | |
3929 | ||
3930 | /* Mop up any left-over bytes. */ | |
3931 | if (offset < length) | |
3932 | { | |
3933 | src = adjust_address (src, BLKmode, offset); | |
3934 | dest = adjust_address (dest, BLKmode, offset); | |
3935 | move_by_pieces (dest, src, length - offset, | |
3936 | MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), | |
3937 | (enum memop_ret) 0); | |
3938 | } | |
3939 | } | |
3940 | ||
3941 | /* Helper function for doing a loop-based block operation on memory | |
3942 | reference MEM. Each iteration of the loop will operate on LENGTH | |
3943 | bytes of MEM. | |
3944 | ||
3945 | Create a new base register for use within the loop and point it to | |
3946 | the start of MEM. Create a new memory reference that uses this | |
3947 | register. Store them in *LOOP_REG and *LOOP_MEM respectively. */ | |
3948 | ||
3949 | static void | |
3950 | loongarch_adjust_block_mem (rtx mem, HOST_WIDE_INT length, rtx *loop_reg, | |
3951 | rtx *loop_mem) | |
3952 | { | |
3953 | *loop_reg = copy_addr_to_reg (XEXP (mem, 0)); | |
3954 | ||
3955 | /* Although the new mem does not refer to a known location, | |
3956 | it does keep up to LENGTH bytes of alignment. */ | |
3957 | *loop_mem = change_address (mem, BLKmode, *loop_reg); | |
3958 | set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT)); | |
3959 | } | |
3960 | ||
3961 | /* Move LENGTH bytes from SRC to DEST using a loop that moves BYTES_PER_ITER | |
3962 | bytes at a time. LENGTH must be at least BYTES_PER_ITER. Assume that | |
3963 | the memory regions do not overlap. */ | |
3964 | ||
3965 | static void | |
3966 | loongarch_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length, | |
3967 | HOST_WIDE_INT bytes_per_iter) | |
3968 | { | |
3969 | rtx_code_label *label; | |
3970 | rtx src_reg, dest_reg, final_src, test; | |
3971 | HOST_WIDE_INT leftover; | |
3972 | ||
3973 | leftover = length % bytes_per_iter; | |
3974 | length -= leftover; | |
3975 | ||
3976 | /* Create registers and memory references for use within the loop. */ | |
3977 | loongarch_adjust_block_mem (src, bytes_per_iter, &src_reg, &src); | |
3978 | loongarch_adjust_block_mem (dest, bytes_per_iter, &dest_reg, &dest); | |
3979 | ||
3980 | /* Calculate the value that SRC_REG should have after the last iteration | |
3981 | of the loop. */ | |
3982 | final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length), 0, | |
3983 | 0, OPTAB_WIDEN); | |
3984 | ||
3985 | /* Emit the start of the loop. */ | |
3986 | label = gen_label_rtx (); | |
3987 | emit_label (label); | |
3988 | ||
3989 | /* Emit the loop body. */ | |
3990 | loongarch_block_move_straight (dest, src, bytes_per_iter); | |
3991 | ||
3992 | /* Move on to the next block. */ | |
3993 | loongarch_emit_move (src_reg, | |
3994 | plus_constant (Pmode, src_reg, bytes_per_iter)); | |
3995 | loongarch_emit_move (dest_reg, | |
3996 | plus_constant (Pmode, dest_reg, bytes_per_iter)); | |
3997 | ||
3998 | /* Emit the loop condition. */ | |
3999 | test = gen_rtx_NE (VOIDmode, src_reg, final_src); | |
4000 | if (Pmode == DImode) | |
4001 | emit_jump_insn (gen_cbranchdi4 (test, src_reg, final_src, label)); | |
4002 | else | |
4003 | emit_jump_insn (gen_cbranchsi4 (test, src_reg, final_src, label)); | |
4004 | ||
4005 | /* Mop up any left-over bytes. */ | |
4006 | if (leftover) | |
4007 | loongarch_block_move_straight (dest, src, leftover); | |
4008 | else | |
4009 | /* Temporary fix for PR79150. */ | |
4010 | emit_insn (gen_nop ()); | |
4011 | } | |
4012 | ||
4013 | /* Expand a cpymemsi instruction, which copies LENGTH bytes from | |
4014 | memory reference SRC to memory reference DEST. */ | |
4015 | ||
4016 | bool | |
4017 | loongarch_expand_block_move (rtx dest, rtx src, rtx length) | |
4018 | { | |
4019 | int max_move_bytes = LARCH_MAX_MOVE_BYTES_STRAIGHT; | |
4020 | ||
4021 | if (CONST_INT_P (length) | |
4022 | && INTVAL (length) <= loongarch_max_inline_memcpy_size) | |
4023 | { | |
4024 | if (INTVAL (length) <= max_move_bytes) | |
4025 | { | |
4026 | loongarch_block_move_straight (dest, src, INTVAL (length)); | |
4027 | return true; | |
4028 | } | |
4029 | else if (optimize) | |
4030 | { | |
4031 | loongarch_block_move_loop (dest, src, INTVAL (length), | |
4032 | LARCH_MAX_MOVE_BYTES_PER_LOOP_ITER); | |
4033 | return true; | |
4034 | } | |
4035 | } | |
4036 | return false; | |
4037 | } | |
4038 | ||
4039 | /* Return true if loongarch_expand_block_move is the preferred | |
4040 | implementation of the 'cpymemsi' template. */ | |
4041 | ||
4042 | bool | |
4043 | loongarch_do_optimize_block_move_p (void) | |
4044 | { | |
4045 | /* if -m[no-]memcpy is given explicitly. */ | |
4046 | if (target_flags_explicit & MASK_MEMCPY) | |
4047 | return !TARGET_MEMCPY; | |
4048 | ||
4049 | /* if not, don't optimize under -Os. */ | |
4050 | return !optimize_size; | |
4051 | } | |
4052 | ||
4053 | ||
4054 | /* Expand a QI or HI mode atomic memory operation. | |
4055 | ||
4056 | GENERATOR contains a pointer to the gen_* function that generates | |
4057 | the SI mode underlying atomic operation using masks that we | |
4058 | calculate. | |
4059 | ||
4060 | RESULT is the return register for the operation. Its value is NULL | |
4061 | if unused. | |
4062 | ||
4063 | MEM is the location of the atomic access. | |
4064 | ||
4065 | OLDVAL is the first operand for the operation. | |
4066 | ||
4067 | NEWVAL is the optional second operand for the operation. Its value | |
4068 | is NULL if unused. */ | |
4069 | ||
4070 | void | |
4071 | loongarch_expand_atomic_qihi (union loongarch_gen_fn_ptrs generator, | |
4072 | rtx result, rtx mem, rtx oldval, rtx newval, | |
4073 | rtx model) | |
4074 | { | |
4075 | rtx orig_addr, memsi_addr, memsi, shift, shiftsi, unshifted_mask; | |
4076 | rtx unshifted_mask_reg, mask, inverted_mask, si_op; | |
4077 | rtx res = NULL; | |
4078 | machine_mode mode; | |
4079 | ||
4080 | mode = GET_MODE (mem); | |
4081 | ||
4082 | /* Compute the address of the containing SImode value. */ | |
4083 | orig_addr = force_reg (Pmode, XEXP (mem, 0)); | |
4084 | memsi_addr = loongarch_force_binary (Pmode, AND, orig_addr, | |
4085 | force_reg (Pmode, GEN_INT (-4))); | |
4086 | ||
4087 | /* Create a memory reference for it. */ | |
4088 | memsi = gen_rtx_MEM (SImode, memsi_addr); | |
4089 | set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER); | |
4090 | MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem); | |
4091 | ||
4092 | /* Work out the byte offset of the QImode or HImode value, | |
4093 | counting from the least significant byte. */ | |
4094 | shift = loongarch_force_binary (Pmode, AND, orig_addr, GEN_INT (3)); | |
4095 | /* Multiply by eight to convert the shift value from bytes to bits. */ | |
4096 | loongarch_emit_binary (ASHIFT, shift, shift, GEN_INT (3)); | |
4097 | ||
4098 | /* Make the final shift an SImode value, so that it can be used in | |
4099 | SImode operations. */ | |
4100 | shiftsi = force_reg (SImode, gen_lowpart (SImode, shift)); | |
4101 | ||
4102 | /* Set MASK to an inclusive mask of the QImode or HImode value. */ | |
4103 | unshifted_mask = GEN_INT (GET_MODE_MASK (mode)); | |
4104 | unshifted_mask_reg = force_reg (SImode, unshifted_mask); | |
4105 | mask = loongarch_force_binary (SImode, ASHIFT, unshifted_mask_reg, shiftsi); | |
4106 | ||
4107 | /* Compute the equivalent exclusive mask. */ | |
4108 | inverted_mask = gen_reg_rtx (SImode); | |
4109 | emit_insn (gen_rtx_SET (inverted_mask, gen_rtx_NOT (SImode, mask))); | |
4110 | ||
4111 | /* Shift the old value into place. */ | |
4112 | if (oldval != const0_rtx) | |
4113 | { | |
4114 | oldval = convert_modes (SImode, mode, oldval, true); | |
4115 | oldval = force_reg (SImode, oldval); | |
4116 | oldval = loongarch_force_binary (SImode, ASHIFT, oldval, shiftsi); | |
4117 | } | |
4118 | ||
4119 | /* Do the same for the new value. */ | |
4120 | if (newval && newval != const0_rtx) | |
4121 | { | |
4122 | newval = convert_modes (SImode, mode, newval, true); | |
4123 | newval = force_reg (SImode, newval); | |
4124 | newval = loongarch_force_binary (SImode, ASHIFT, newval, shiftsi); | |
4125 | } | |
4126 | ||
4127 | /* Do the SImode atomic access. */ | |
4128 | if (result) | |
4129 | res = gen_reg_rtx (SImode); | |
4130 | ||
4131 | if (newval) | |
4132 | si_op = generator.fn_7 (res, memsi, mask, inverted_mask, oldval, newval, | |
4133 | model); | |
4134 | else if (result) | |
4135 | si_op = generator.fn_6 (res, memsi, mask, inverted_mask, oldval, model); | |
4136 | else | |
4137 | si_op = generator.fn_5 (memsi, mask, inverted_mask, oldval, model); | |
4138 | ||
4139 | emit_insn (si_op); | |
4140 | ||
4141 | if (result) | |
4142 | { | |
4143 | /* Shift and convert the result. */ | |
4144 | loongarch_emit_binary (AND, res, res, mask); | |
4145 | loongarch_emit_binary (LSHIFTRT, res, res, shiftsi); | |
4146 | loongarch_emit_move (result, gen_lowpart (GET_MODE (result), res)); | |
4147 | } | |
4148 | } | |
4149 | ||
4150 | /* Return true if (zero_extract OP WIDTH BITPOS) can be used as the | |
4151 | source of an "ext" instruction or the destination of an "ins" | |
4152 | instruction. OP must be a register operand and the following | |
4153 | conditions must hold: | |
4154 | ||
4155 | 0 <= BITPOS < GET_MODE_BITSIZE (GET_MODE (op)) | |
4156 | 0 < WIDTH <= GET_MODE_BITSIZE (GET_MODE (op)) | |
4157 | 0 < BITPOS + WIDTH <= GET_MODE_BITSIZE (GET_MODE (op)) | |
4158 | ||
4159 | Also reject lengths equal to a word as they are better handled | |
4160 | by the move patterns. */ | |
4161 | ||
4162 | bool | |
4163 | loongarch_use_ins_ext_p (rtx op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos) | |
4164 | { | |
4165 | if (!register_operand (op, VOIDmode) | |
4166 | || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD) | |
4167 | return false; | |
4168 | ||
4169 | if (!IN_RANGE (width, 1, GET_MODE_BITSIZE (GET_MODE (op)) - 1)) | |
4170 | return false; | |
4171 | ||
4172 | if (bitpos < 0 || bitpos + width > GET_MODE_BITSIZE (GET_MODE (op))) | |
4173 | return false; | |
4174 | ||
4175 | return true; | |
4176 | } | |
4177 | ||
4178 | /* Print the text for PRINT_OPERAND punctation character CH to FILE. | |
4179 | The punctuation characters are: | |
4180 | ||
4181 | '.' Print the name of the register with a hard-wired zero (zero or $r0). | |
4182 | '$' Print the name of the stack pointer register (sp or $r3). | |
4183 | ||
4184 | See also loongarch_init_print_operand_punct. */ | |
4185 | ||
4186 | static void | |
4187 | loongarch_print_operand_punctuation (FILE *file, int ch) | |
4188 | { | |
4189 | switch (ch) | |
4190 | { | |
4191 | case '.': | |
4192 | fputs (reg_names[GP_REG_FIRST + 0], file); | |
4193 | break; | |
4194 | ||
4195 | case '$': | |
4196 | fputs (reg_names[STACK_POINTER_REGNUM], file); | |
4197 | break; | |
4198 | ||
4199 | default: | |
4200 | gcc_unreachable (); | |
4201 | break; | |
4202 | } | |
4203 | } | |
4204 | ||
4205 | /* Initialize loongarch_print_operand_punct. */ | |
4206 | ||
4207 | static void | |
4208 | loongarch_init_print_operand_punct (void) | |
4209 | { | |
4210 | const char *p; | |
4211 | ||
4212 | for (p = ".$"; *p; p++) | |
4213 | loongarch_print_operand_punct[(unsigned char) *p] = true; | |
4214 | } | |
4215 | ||
4216 | /* PRINT_OPERAND prefix LETTER refers to the integer branch instruction | |
4217 | associated with condition CODE. Print the condition part of the | |
4218 | opcode to FILE. */ | |
4219 | ||
4220 | static void | |
4221 | loongarch_print_int_branch_condition (FILE *file, enum rtx_code code, | |
4222 | int letter) | |
4223 | { | |
4224 | switch (code) | |
4225 | { | |
4226 | case EQ: | |
4227 | case NE: | |
4228 | case GT: | |
4229 | case GE: | |
4230 | case LT: | |
4231 | case LE: | |
4232 | case GTU: | |
4233 | case GEU: | |
4234 | case LTU: | |
4235 | case LEU: | |
4236 | /* Conveniently, the LoongArch names for these conditions are the same | |
4237 | as their RTL equivalents. */ | |
4238 | fputs (GET_RTX_NAME (code), file); | |
4239 | break; | |
4240 | ||
4241 | default: | |
4242 | output_operand_lossage ("'%%%c' is not a valid operand prefix", letter); | |
4243 | break; | |
4244 | } | |
4245 | } | |
4246 | ||
4247 | /* Likewise floating-point branches. */ | |
4248 | ||
4249 | static void | |
4250 | loongarch_print_float_branch_condition (FILE *file, enum rtx_code code, | |
4251 | int letter) | |
4252 | { | |
4253 | switch (code) | |
4254 | { | |
4255 | case EQ: | |
4256 | fputs ("ceqz", file); | |
4257 | break; | |
4258 | ||
4259 | case NE: | |
4260 | fputs ("cnez", file); | |
4261 | break; | |
4262 | ||
4263 | default: | |
4264 | output_operand_lossage ("'%%%c' is not a valid operand prefix", letter); | |
4265 | break; | |
4266 | } | |
4267 | } | |
4268 | ||
4269 | /* Implement TARGET_PRINT_OPERAND_PUNCT_VALID_P. */ | |
4270 | ||
4271 | static bool | |
4272 | loongarch_print_operand_punct_valid_p (unsigned char code) | |
4273 | { | |
4274 | return loongarch_print_operand_punct[code]; | |
4275 | } | |
4276 | ||
4277 | /* Return true if a FENCE should be emitted to before a memory access to | |
4278 | implement the release portion of memory model MODEL. */ | |
4279 | ||
4280 | static bool | |
4281 | loongarch_memmodel_needs_rel_acq_fence (enum memmodel model) | |
4282 | { | |
4283 | switch (model) | |
4284 | { | |
4285 | case MEMMODEL_ACQ_REL: | |
4286 | case MEMMODEL_SEQ_CST: | |
4287 | case MEMMODEL_SYNC_SEQ_CST: | |
4288 | case MEMMODEL_RELEASE: | |
4289 | case MEMMODEL_SYNC_RELEASE: | |
4290 | case MEMMODEL_ACQUIRE: | |
4291 | case MEMMODEL_CONSUME: | |
4292 | case MEMMODEL_SYNC_ACQUIRE: | |
4293 | return true; | |
4294 | ||
4295 | case MEMMODEL_RELAXED: | |
4296 | return false; | |
4297 | ||
4298 | default: | |
4299 | gcc_unreachable (); | |
4300 | } | |
4301 | } | |
4302 | ||
4303 | /* Return true if a FENCE should be emitted to before a memory access to | |
4304 | implement the release portion of memory model MODEL. */ | |
4305 | ||
4306 | static bool | |
4307 | loongarch_memmodel_needs_release_fence (enum memmodel model) | |
4308 | { | |
4309 | switch (model) | |
4310 | { | |
4311 | case MEMMODEL_ACQ_REL: | |
4312 | case MEMMODEL_SEQ_CST: | |
4313 | case MEMMODEL_SYNC_SEQ_CST: | |
4314 | case MEMMODEL_RELEASE: | |
4315 | case MEMMODEL_SYNC_RELEASE: | |
4316 | return true; | |
4317 | ||
4318 | case MEMMODEL_ACQUIRE: | |
4319 | case MEMMODEL_CONSUME: | |
4320 | case MEMMODEL_SYNC_ACQUIRE: | |
4321 | case MEMMODEL_RELAXED: | |
4322 | return false; | |
4323 | ||
4324 | default: | |
4325 | gcc_unreachable (); | |
4326 | } | |
4327 | } | |
4328 | ||
4329 | /* Implement TARGET_PRINT_OPERAND. The LoongArch-specific operand codes are: | |
4330 | ||
4331 | 'X' Print CONST_INT OP in hexadecimal format. | |
4332 | 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format. | |
4333 | 'd' Print CONST_INT OP in decimal. | |
4334 | 'm' Print one less than CONST_INT OP in decimal. | |
4335 | 'y' Print exact log2 of CONST_INT OP in decimal. | |
4336 | 'C' Print the integer branch condition for comparison OP. | |
4337 | 'N' Print the inverse of the integer branch condition for comparison OP. | |
4338 | 'F' Print the FPU branch condition for comparison OP. | |
4339 | 'W' Print the inverse of the FPU branch condition for comparison OP. | |
4340 | 'T' Print 'f' for (eq:CC ...), 't' for (ne:CC ...), | |
4341 | 'z' for (eq:?I ...), 'n' for (ne:?I ...). | |
4342 | 't' Like 'T', but with the EQ/NE cases reversed | |
4343 | 'Y' Print loongarch_fp_conditions[INTVAL (OP)] | |
4344 | 'Z' Print OP and a comma for 8CC, otherwise print nothing. | |
4345 | 'z' Print $0 if OP is zero, otherwise print OP normally. | |
4346 | 'b' Print the address of a memory operand, without offset. | |
4347 | 'V' Print exact log2 of CONST_INT OP element 0 of a replicated | |
4348 | CONST_VECTOR in decimal. | |
4349 | 'A' Print a _DB suffix if the memory model requires a release. | |
4350 | 'G' Print a DBAR insn if the memory model requires a release. | |
4351 | 'i' Print i if the operand is not a register. */ | |
4352 | ||
4353 | static void | |
4354 | loongarch_print_operand (FILE *file, rtx op, int letter) | |
4355 | { | |
4356 | enum rtx_code code; | |
4357 | ||
4358 | if (loongarch_print_operand_punct_valid_p (letter)) | |
4359 | { | |
4360 | loongarch_print_operand_punctuation (file, letter); | |
4361 | return; | |
4362 | } | |
4363 | ||
4364 | gcc_assert (op); | |
4365 | code = GET_CODE (op); | |
4366 | ||
4367 | switch (letter) | |
4368 | { | |
4369 | case 'X': | |
4370 | if (CONST_INT_P (op)) | |
4371 | fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op)); | |
4372 | else | |
4373 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4374 | break; | |
4375 | ||
4376 | case 'x': | |
4377 | if (CONST_INT_P (op)) | |
4378 | fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff); | |
4379 | else | |
4380 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4381 | break; | |
4382 | ||
4383 | case 'd': | |
4384 | if (CONST_INT_P (op)) | |
4385 | fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op)); | |
4386 | else | |
4387 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4388 | break; | |
4389 | ||
4390 | case 'm': | |
4391 | if (CONST_INT_P (op)) | |
4392 | fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1); | |
4393 | else | |
4394 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4395 | break; | |
4396 | ||
4397 | case 'y': | |
4398 | if (CONST_INT_P (op)) | |
4399 | { | |
4400 | int val = exact_log2 (INTVAL (op)); | |
4401 | if (val != -1) | |
4402 | fprintf (file, "%d", val); | |
4403 | else | |
4404 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4405 | } | |
4406 | else | |
4407 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4408 | break; | |
4409 | ||
4410 | case 'V': | |
4411 | if (CONST_VECTOR_P (op)) | |
4412 | { | |
4413 | machine_mode mode = GET_MODE_INNER (GET_MODE (op)); | |
4414 | unsigned HOST_WIDE_INT val = UINTVAL (CONST_VECTOR_ELT (op, 0)); | |
4415 | int vlog2 = exact_log2 (val & GET_MODE_MASK (mode)); | |
4416 | if (vlog2 != -1) | |
4417 | fprintf (file, "%d", vlog2); | |
4418 | else | |
4419 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4420 | } | |
4421 | else | |
4422 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4423 | break; | |
4424 | ||
4425 | case 'C': | |
4426 | loongarch_print_int_branch_condition (file, code, letter); | |
4427 | break; | |
4428 | ||
4429 | case 'N': | |
4430 | loongarch_print_int_branch_condition (file, reverse_condition (code), | |
4431 | letter); | |
4432 | break; | |
4433 | ||
4434 | case 'F': | |
4435 | loongarch_print_float_branch_condition (file, code, letter); | |
4436 | break; | |
4437 | ||
4438 | case 'W': | |
4439 | loongarch_print_float_branch_condition (file, reverse_condition (code), | |
4440 | letter); | |
4441 | break; | |
4442 | ||
4443 | case 'T': | |
4444 | case 't': | |
4445 | { | |
4446 | int truth = (code == NE) == (letter == 'T'); | |
4447 | fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file); | |
4448 | } | |
4449 | break; | |
4450 | ||
4451 | case 'Y': | |
4452 | if (code == CONST_INT | |
4453 | && UINTVAL (op) < ARRAY_SIZE (loongarch_fp_conditions)) | |
4454 | fputs (loongarch_fp_conditions[UINTVAL (op)], file); | |
4455 | else | |
4456 | output_operand_lossage ("'%%%c' is not a valid operand prefix", | |
4457 | letter); | |
4458 | break; | |
4459 | ||
4460 | case 'Z': | |
4461 | loongarch_print_operand (file, op, 0); | |
4462 | fputc (',', file); | |
4463 | break; | |
4464 | ||
4465 | case 'A': | |
4466 | if (loongarch_memmodel_needs_rel_acq_fence ((enum memmodel) INTVAL (op))) | |
4467 | fputs ("_db", file); | |
4468 | break; | |
4469 | ||
4470 | case 'G': | |
4471 | if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op))) | |
4472 | fputs ("dbar\t0", file); | |
4473 | break; | |
4474 | ||
4475 | case 'i': | |
4476 | if (code != REG) | |
4477 | fputs ("i", file); | |
4478 | break; | |
4479 | ||
4480 | default: | |
4481 | switch (code) | |
4482 | { | |
4483 | case REG: | |
4484 | { | |
4485 | unsigned int regno = REGNO (op); | |
4486 | if (letter && letter != 'z') | |
4487 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4488 | fprintf (file, "%s", reg_names[regno]); | |
4489 | } | |
4490 | break; | |
4491 | ||
4492 | case MEM: | |
4493 | if (letter == 'D') | |
4494 | output_address (GET_MODE (op), | |
4495 | plus_constant (Pmode, XEXP (op, 0), 4)); | |
4496 | else if (letter == 'b') | |
4497 | { | |
4498 | gcc_assert (REG_P (XEXP (op, 0))); | |
4499 | loongarch_print_operand (file, XEXP (op, 0), 0); | |
4500 | } | |
4501 | else if (letter && letter != 'z') | |
4502 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4503 | else | |
4504 | output_address (GET_MODE (op), XEXP (op, 0)); | |
4505 | break; | |
4506 | ||
4507 | default: | |
4508 | if (letter == 'z' && op == CONST0_RTX (GET_MODE (op))) | |
4509 | fputs (reg_names[GP_REG_FIRST], file); | |
4510 | else if (letter && letter != 'z') | |
4511 | output_operand_lossage ("invalid use of '%%%c'", letter); | |
4512 | else | |
4513 | output_addr_const (file, loongarch_strip_unspec_address (op)); | |
4514 | break; | |
4515 | } | |
4516 | } | |
4517 | } | |
4518 | ||
4519 | /* Implement TARGET_PRINT_OPERAND_ADDRESS. */ | |
4520 | ||
4521 | static void | |
4522 | loongarch_print_operand_address (FILE *file, machine_mode /* mode */, rtx x) | |
4523 | { | |
4524 | struct loongarch_address_info addr; | |
4525 | ||
4526 | if (loongarch_classify_address (&addr, x, word_mode, true)) | |
4527 | switch (addr.type) | |
4528 | { | |
4529 | case ADDRESS_REG: | |
4530 | fprintf (file, "%s,", reg_names[REGNO (addr.reg)]); | |
4531 | loongarch_print_operand (file, addr.offset, 0); | |
4532 | return; | |
4533 | ||
4534 | case ADDRESS_REG_REG: | |
4535 | fprintf (file, "%s,%s", reg_names[REGNO (addr.reg)], | |
4536 | reg_names[REGNO (addr.offset)]); | |
4537 | return; | |
4538 | ||
4539 | case ADDRESS_CONST_INT: | |
4540 | fprintf (file, "%s,", reg_names[GP_REG_FIRST]); | |
4541 | output_addr_const (file, x); | |
4542 | return; | |
4543 | ||
4544 | case ADDRESS_SYMBOLIC: | |
4545 | output_addr_const (file, loongarch_strip_unspec_address (x)); | |
4546 | return; | |
4547 | } | |
4548 | if (CONST_INT_P (x)) | |
4549 | output_addr_const (file, x); | |
4550 | else | |
4551 | gcc_unreachable (); | |
4552 | } | |
4553 | ||
4554 | /* Implement TARGET_ASM_SELECT_RTX_SECTION. */ | |
4555 | ||
4556 | static section * | |
4557 | loongarch_select_rtx_section (machine_mode mode, rtx x, | |
4558 | unsigned HOST_WIDE_INT align) | |
4559 | { | |
4560 | /* ??? Consider using mergeable small data sections. */ | |
4561 | if (loongarch_rtx_constant_in_small_data_p (mode)) | |
4562 | return get_named_section (NULL, ".sdata", 0); | |
4563 | ||
4564 | return default_elf_select_rtx_section (mode, x, align); | |
4565 | } | |
4566 | ||
4567 | /* Implement TARGET_ASM_FUNCTION_RODATA_SECTION. | |
4568 | ||
4569 | The complication here is that jump tables will use absolute addresses, | |
4570 | and should therefore not be included in the read-only part of a DSO. | |
4571 | Handle such cases by selecting a normal data section instead of a | |
4572 | read-only one. The logic apes that in default_function_rodata_section. */ | |
4573 | ||
4574 | static section * | |
4575 | loongarch_function_rodata_section (tree decl, bool) | |
4576 | { | |
4577 | return default_function_rodata_section (decl, false); | |
4578 | } | |
4579 | ||
4580 | /* Implement TARGET_IN_SMALL_DATA_P. */ | |
4581 | ||
4582 | static bool | |
4583 | loongarch_in_small_data_p (const_tree decl) | |
4584 | { | |
4585 | int size; | |
4586 | ||
4587 | if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL) | |
4588 | return false; | |
4589 | ||
4590 | if (VAR_P (decl) && DECL_SECTION_NAME (decl) != 0) | |
4591 | { | |
4592 | const char *name; | |
4593 | ||
4594 | /* Reject anything that isn't in a known small-data section. */ | |
4595 | name = DECL_SECTION_NAME (decl); | |
4596 | if (strcmp (name, ".sdata") != 0 && strcmp (name, ".sbss") != 0) | |
4597 | return false; | |
4598 | ||
4599 | /* If a symbol is defined externally, the assembler will use the | |
4600 | usual -G rules when deciding how to implement macros. */ | |
4601 | if (!DECL_EXTERNAL (decl)) | |
4602 | return true; | |
4603 | } | |
4604 | ||
4605 | /* We have traditionally not treated zero-sized objects as small data, | |
4606 | so this is now effectively part of the ABI. */ | |
4607 | size = int_size_in_bytes (TREE_TYPE (decl)); | |
4608 | return size > 0 && size <= g_switch_value; | |
4609 | } | |
4610 | ||
4611 | /* The LoongArch debug format wants all automatic variables and arguments | |
4612 | to be in terms of the virtual frame pointer (stack pointer before | |
4613 | any adjustment in the function), while the LoongArch linker wants | |
4614 | the frame pointer to be the stack pointer after the initial | |
4615 | adjustment. So, we do the adjustment here. The arg pointer (which | |
4616 | is eliminated) points to the virtual frame pointer, while the frame | |
4617 | pointer (which may be eliminated) points to the stack pointer after | |
4618 | the initial adjustments. */ | |
4619 | ||
4620 | HOST_WIDE_INT | |
4621 | loongarch_debugger_offset (rtx addr, HOST_WIDE_INT offset) | |
4622 | { | |
4623 | rtx offset2 = const0_rtx; | |
4624 | rtx reg = eliminate_constant_term (addr, &offset2); | |
4625 | ||
4626 | if (offset == 0) | |
4627 | offset = INTVAL (offset2); | |
4628 | ||
4629 | if (reg == stack_pointer_rtx | |
4630 | || reg == frame_pointer_rtx | |
4631 | || reg == hard_frame_pointer_rtx) | |
4632 | { | |
4633 | offset -= cfun->machine->frame.total_size; | |
4634 | if (reg == hard_frame_pointer_rtx) | |
4635 | offset += cfun->machine->frame.hard_frame_pointer_offset; | |
4636 | } | |
4637 | ||
4638 | return offset; | |
4639 | } | |
4640 | ||
4641 | /* Implement ASM_OUTPUT_EXTERNAL. */ | |
4642 | ||
4643 | void | |
4644 | loongarch_output_external (FILE *file, tree decl, const char *name) | |
4645 | { | |
4646 | default_elf_asm_output_external (file, decl, name); | |
4647 | ||
4648 | /* We output the name if and only if TREE_SYMBOL_REFERENCED is | |
4649 | set in order to avoid putting out names that are never really | |
4650 | used. */ | |
4651 | if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))) | |
4652 | { | |
4653 | if (loongarch_in_small_data_p (decl)) | |
4654 | { | |
4655 | /* When using assembler macros, emit .extern directives for | |
4656 | all small-data externs so that the assembler knows how | |
4657 | big they are. | |
4658 | ||
4659 | In most cases it would be safe (though pointless) to emit | |
4660 | .externs for other symbols too. One exception is when an | |
4661 | object is within the -G limit but declared by the user to | |
4662 | be in a section other than .sbss or .sdata. */ | |
4663 | fputs ("\t.extern\t", file); | |
4664 | assemble_name (file, name); | |
4665 | fprintf (file, ", " HOST_WIDE_INT_PRINT_DEC "\n", | |
4666 | int_size_in_bytes (TREE_TYPE (decl))); | |
4667 | } | |
4668 | } | |
4669 | } | |
4670 | ||
4671 | /* Implement TARGET_ASM_OUTPUT_DWARF_DTPREL. */ | |
4672 | ||
4673 | static void ATTRIBUTE_UNUSED | |
4674 | loongarch_output_dwarf_dtprel (FILE *file, int size, rtx x) | |
4675 | { | |
4676 | switch (size) | |
4677 | { | |
4678 | case 4: | |
4679 | fputs ("\t.dtprelword\t", file); | |
4680 | break; | |
4681 | ||
4682 | case 8: | |
4683 | fputs ("\t.dtpreldword\t", file); | |
4684 | break; | |
4685 | ||
4686 | default: | |
4687 | gcc_unreachable (); | |
4688 | } | |
4689 | output_addr_const (file, x); | |
4690 | fputs ("+0x8000", file); | |
4691 | } | |
4692 | ||
4693 | /* Implement ASM_OUTPUT_ASCII. */ | |
4694 | ||
4695 | void | |
4696 | loongarch_output_ascii (FILE *stream, const char *string, size_t len) | |
4697 | { | |
4698 | size_t i; | |
4699 | int cur_pos; | |
4700 | ||
4701 | cur_pos = 17; | |
4702 | fprintf (stream, "\t.ascii\t\""); | |
4703 | for (i = 0; i < len; i++) | |
4704 | { | |
4705 | int c; | |
4706 | ||
4707 | c = (unsigned char) string[i]; | |
4708 | if (ISPRINT (c)) | |
4709 | { | |
4710 | if (c == '\\' || c == '\"') | |
4711 | { | |
4712 | putc ('\\', stream); | |
4713 | cur_pos++; | |
4714 | } | |
4715 | putc (c, stream); | |
4716 | cur_pos++; | |
4717 | } | |
4718 | else | |
4719 | { | |
4720 | fprintf (stream, "\\%03o", c); | |
4721 | cur_pos += 4; | |
4722 | } | |
4723 | ||
4724 | if (cur_pos > 72 && i + 1 < len) | |
4725 | { | |
4726 | cur_pos = 17; | |
4727 | fprintf (stream, "\"\n\t.ascii\t\""); | |
4728 | } | |
4729 | } | |
4730 | fprintf (stream, "\"\n"); | |
4731 | } | |
4732 | ||
4733 | /* Implement TARGET_FRAME_POINTER_REQUIRED. */ | |
4734 | ||
4735 | static bool | |
4736 | loongarch_frame_pointer_required (void) | |
4737 | { | |
4738 | /* If the function contains dynamic stack allocations, we need to | |
4739 | use the frame pointer to access the static parts of the frame. */ | |
4740 | if (cfun->calls_alloca) | |
4741 | return true; | |
4742 | ||
4743 | return false; | |
4744 | } | |
4745 | ||
4746 | /* Implement TARGET_CAN_ELIMINATE. Make sure that we're not trying | |
4747 | to eliminate to the wrong hard frame pointer. */ | |
4748 | ||
4749 | static bool | |
4750 | loongarch_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) | |
4751 | { | |
4752 | return (to == HARD_FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM); | |
4753 | } | |
4754 | ||
4755 | /* Implement RETURN_ADDR_RTX. We do not support moving back to a | |
4756 | previous frame. */ | |
4757 | ||
4758 | rtx | |
4759 | loongarch_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) | |
4760 | { | |
4761 | if (count != 0) | |
4762 | return const0_rtx; | |
4763 | ||
4764 | return get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM); | |
4765 | } | |
4766 | ||
4767 | /* Emit code to change the current function's return address to | |
4768 | ADDRESS. SCRATCH is available as a scratch register, if needed. | |
4769 | ADDRESS and SCRATCH are both word-mode GPRs. */ | |
4770 | ||
4771 | void | |
4772 | loongarch_set_return_address (rtx address, rtx scratch) | |
4773 | { | |
4774 | rtx slot_address; | |
4775 | ||
4776 | gcc_assert (BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM)); | |
4777 | ||
4778 | if (frame_pointer_needed) | |
4779 | slot_address = loongarch_add_offset (scratch, hard_frame_pointer_rtx, | |
4780 | -UNITS_PER_WORD); | |
4781 | else | |
4782 | slot_address = loongarch_add_offset (scratch, stack_pointer_rtx, | |
4783 | cfun->machine->frame.gp_sp_offset); | |
4784 | ||
4785 | loongarch_emit_move (gen_frame_mem (GET_MODE (address), slot_address), | |
4786 | address); | |
4787 | } | |
4788 | ||
4789 | /* Return true if register REGNO can store a value of mode MODE. | |
4790 | The result of this function is cached in loongarch_hard_regno_mode_ok. */ | |
4791 | ||
4792 | static bool | |
4793 | loongarch_hard_regno_mode_ok_uncached (unsigned int regno, machine_mode mode) | |
4794 | { | |
4795 | unsigned int size; | |
4796 | enum mode_class mclass; | |
4797 | ||
4798 | if (mode == FCCmode) | |
4799 | return FCC_REG_P (regno); | |
4800 | ||
4801 | size = GET_MODE_SIZE (mode); | |
4802 | mclass = GET_MODE_CLASS (mode); | |
4803 | ||
4804 | if (GP_REG_P (regno)) | |
4805 | return ((regno - GP_REG_FIRST) & 1) == 0 || size <= UNITS_PER_WORD; | |
4806 | ||
4807 | if (FP_REG_P (regno)) | |
4808 | { | |
4809 | if (mclass == MODE_FLOAT | |
4810 | || mclass == MODE_COMPLEX_FLOAT | |
4811 | || mclass == MODE_VECTOR_FLOAT) | |
4812 | return size <= UNITS_PER_FPVALUE; | |
4813 | ||
4814 | /* Allow integer modes that fit into a single register. We need | |
4815 | to put integers into FPRs when using instructions like CVT | |
4816 | and TRUNC. There's no point allowing sizes smaller than a word, | |
4817 | because the FPU has no appropriate load/store instructions. */ | |
4818 | if (mclass == MODE_INT) | |
4819 | return size >= MIN_UNITS_PER_WORD && size <= UNITS_PER_FPREG; | |
4820 | } | |
4821 | ||
4822 | return false; | |
4823 | } | |
4824 | ||
4825 | /* Implement TARGET_HARD_REGNO_MODE_OK. */ | |
4826 | ||
4827 | static bool | |
4828 | loongarch_hard_regno_mode_ok (unsigned int regno, machine_mode mode) | |
4829 | { | |
4830 | return loongarch_hard_regno_mode_ok_p[mode][regno]; | |
4831 | } | |
4832 | ||
4833 | /* Implement TARGET_HARD_REGNO_NREGS. */ | |
4834 | ||
4835 | static unsigned int | |
4836 | loongarch_hard_regno_nregs (unsigned int regno, machine_mode mode) | |
4837 | { | |
4838 | if (FCC_REG_P (regno)) | |
4839 | /* The size of FP status registers is always 4, because they only hold | |
4840 | FCCmode values, and FCCmode is always considered to be 4 bytes wide. */ | |
4841 | return (GET_MODE_SIZE (mode) + 3) / 4; | |
4842 | ||
4843 | if (FP_REG_P (regno)) | |
4844 | return (GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG; | |
4845 | ||
4846 | /* All other registers are word-sized. */ | |
4847 | return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; | |
4848 | } | |
4849 | ||
4850 | /* Implement CLASS_MAX_NREGS, taking the maximum of the cases | |
4851 | in loongarch_hard_regno_nregs. */ | |
4852 | ||
4853 | int | |
4854 | loongarch_class_max_nregs (enum reg_class rclass, machine_mode mode) | |
4855 | { | |
4856 | int size; | |
4857 | HARD_REG_SET left; | |
4858 | ||
4859 | size = 0x8000; | |
4860 | left = reg_class_contents[rclass]; | |
4861 | if (hard_reg_set_intersect_p (left, reg_class_contents[(int) FCC_REGS])) | |
4862 | { | |
4863 | if (loongarch_hard_regno_mode_ok (FCC_REG_FIRST, mode)) | |
4864 | size = MIN (size, 4); | |
4865 | ||
4866 | left &= ~reg_class_contents[FCC_REGS]; | |
4867 | } | |
4868 | if (hard_reg_set_intersect_p (left, reg_class_contents[(int) FP_REGS])) | |
4869 | { | |
4870 | if (loongarch_hard_regno_mode_ok (FP_REG_FIRST, mode)) | |
4871 | size = MIN (size, UNITS_PER_FPREG); | |
4872 | ||
4873 | left &= ~reg_class_contents[FP_REGS]; | |
4874 | } | |
4875 | if (!hard_reg_set_empty_p (left)) | |
4876 | size = MIN (size, UNITS_PER_WORD); | |
4877 | return (GET_MODE_SIZE (mode) + size - 1) / size; | |
4878 | } | |
4879 | ||
4880 | /* Implement TARGET_CAN_CHANGE_MODE_CLASS. */ | |
4881 | ||
4882 | static bool | |
4883 | loongarch_can_change_mode_class (machine_mode, machine_mode, | |
4884 | reg_class_t rclass) | |
4885 | { | |
4886 | return !reg_classes_intersect_p (FP_REGS, rclass); | |
4887 | } | |
4888 | ||
4889 | /* Return true if moves in mode MODE can use the FPU's fmov.fmt instruction, | |
4890 | */ | |
4891 | ||
4892 | static bool | |
4893 | loongarch_mode_ok_for_mov_fmt_p (machine_mode mode) | |
4894 | { | |
4895 | switch (mode) | |
4896 | { | |
4897 | case E_FCCmode: | |
4898 | case E_SFmode: | |
4899 | return TARGET_HARD_FLOAT; | |
4900 | ||
4901 | case E_DFmode: | |
4902 | return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT; | |
4903 | ||
4904 | default: | |
4905 | return 0; | |
4906 | } | |
4907 | } | |
4908 | ||
4909 | /* Implement TARGET_MODES_TIEABLE_P. */ | |
4910 | ||
4911 | static bool | |
4912 | loongarch_modes_tieable_p (machine_mode mode1, machine_mode mode2) | |
4913 | { | |
4914 | /* FPRs allow no mode punning, so it's not worth tying modes if we'd | |
4915 | prefer to put one of them in FPRs. */ | |
4916 | return (mode1 == mode2 | |
4917 | || (!loongarch_mode_ok_for_mov_fmt_p (mode1) | |
4918 | && !loongarch_mode_ok_for_mov_fmt_p (mode2))); | |
4919 | } | |
4920 | ||
4921 | /* Implement TARGET_PREFERRED_RELOAD_CLASS. */ | |
4922 | ||
4923 | static reg_class_t | |
4924 | loongarch_preferred_reload_class (rtx x, reg_class_t rclass) | |
4925 | { | |
4926 | if (reg_class_subset_p (FP_REGS, rclass) | |
4927 | && loongarch_mode_ok_for_mov_fmt_p (GET_MODE (x))) | |
4928 | return FP_REGS; | |
4929 | ||
4930 | if (reg_class_subset_p (GR_REGS, rclass)) | |
4931 | rclass = GR_REGS; | |
4932 | ||
4933 | return rclass; | |
4934 | } | |
4935 | ||
4936 | /* RCLASS is a class involved in a REGISTER_MOVE_COST calculation. | |
4937 | Return a "canonical" class to represent it in later calculations. */ | |
4938 | ||
4939 | static reg_class_t | |
4940 | loongarch_canonicalize_move_class (reg_class_t rclass) | |
4941 | { | |
4942 | if (reg_class_subset_p (rclass, GENERAL_REGS)) | |
4943 | rclass = GENERAL_REGS; | |
4944 | ||
4945 | return rclass; | |
4946 | } | |
4947 | ||
4948 | /* Return the cost of moving a value from a register of class FROM to a GPR. | |
4949 | Return 0 for classes that are unions of other classes handled by this | |
4950 | function. */ | |
4951 | ||
4952 | static int | |
4953 | loongarch_move_to_gpr_cost (reg_class_t from) | |
4954 | { | |
4955 | switch (from) | |
4956 | { | |
4957 | case GENERAL_REGS: | |
4958 | /* MOVE macro. */ | |
4959 | return 2; | |
4960 | ||
4961 | case FP_REGS: | |
4962 | /* MOVFR2GR, etc. */ | |
4963 | return 4; | |
4964 | ||
4965 | default: | |
4966 | return 0; | |
4967 | } | |
4968 | } | |
4969 | ||
4970 | /* Return the cost of moving a value from a GPR to a register of class TO. | |
4971 | Return 0 for classes that are unions of other classes handled by this | |
4972 | function. */ | |
4973 | ||
4974 | static int | |
4975 | loongarch_move_from_gpr_cost (reg_class_t to) | |
4976 | { | |
4977 | switch (to) | |
4978 | { | |
4979 | case GENERAL_REGS: | |
4980 | /*MOVE macro. */ | |
4981 | return 2; | |
4982 | ||
4983 | case FP_REGS: | |
4984 | /* MOVGR2FR, etc. */ | |
4985 | return 4; | |
4986 | ||
4987 | default: | |
4988 | return 0; | |
4989 | } | |
4990 | } | |
4991 | ||
4992 | /* Implement TARGET_REGISTER_MOVE_COST. Return 0 for classes that are the | |
4993 | maximum of the move costs for subclasses; regclass will work out | |
4994 | the maximum for us. */ | |
4995 | ||
4996 | static int | |
4997 | loongarch_register_move_cost (machine_mode mode, reg_class_t from, | |
4998 | reg_class_t to) | |
4999 | { | |
5000 | reg_class_t dregs; | |
5001 | int cost1, cost2; | |
5002 | ||
5003 | from = loongarch_canonicalize_move_class (from); | |
5004 | to = loongarch_canonicalize_move_class (to); | |
5005 | ||
5006 | /* Handle moves that can be done without using general-purpose registers. */ | |
5007 | if (from == FP_REGS) | |
5008 | { | |
5009 | if (to == FP_REGS && loongarch_mode_ok_for_mov_fmt_p (mode)) | |
5010 | /* FMOV.FMT. */ | |
5011 | return 4; | |
5012 | } | |
5013 | ||
5014 | /* Handle cases in which only one class deviates from the ideal. */ | |
5015 | dregs = GENERAL_REGS; | |
5016 | if (from == dregs) | |
5017 | return loongarch_move_from_gpr_cost (to); | |
5018 | if (to == dregs) | |
5019 | return loongarch_move_to_gpr_cost (from); | |
5020 | ||
5021 | /* Handles cases that require a GPR temporary. */ | |
5022 | cost1 = loongarch_move_to_gpr_cost (from); | |
5023 | if (cost1 != 0) | |
5024 | { | |
5025 | cost2 = loongarch_move_from_gpr_cost (to); | |
5026 | if (cost2 != 0) | |
5027 | return cost1 + cost2; | |
5028 | } | |
5029 | ||
5030 | return 0; | |
5031 | } | |
5032 | ||
5033 | /* Implement TARGET_MEMORY_MOVE_COST. */ | |
5034 | ||
5035 | static int | |
5036 | loongarch_memory_move_cost (machine_mode mode, reg_class_t rclass, bool in) | |
5037 | { | |
5038 | return (loongarch_cost->memory_latency | |
5039 | + memory_move_secondary_cost (mode, rclass, in)); | |
5040 | } | |
5041 | ||
5042 | /* Return the register class required for a secondary register when | |
5043 | copying between one of the registers in RCLASS and value X, which | |
5044 | has mode MODE. X is the source of the move if IN_P, otherwise it | |
5045 | is the destination. Return NO_REGS if no secondary register is | |
5046 | needed. */ | |
5047 | ||
5048 | static reg_class_t | |
5049 | loongarch_secondary_reload (bool in_p ATTRIBUTE_UNUSED, rtx x, | |
5050 | reg_class_t rclass, machine_mode mode, | |
5051 | secondary_reload_info *sri ATTRIBUTE_UNUSED) | |
5052 | { | |
5053 | int regno; | |
5054 | ||
5055 | regno = true_regnum (x); | |
5056 | ||
5057 | if (reg_class_subset_p (rclass, FP_REGS)) | |
5058 | { | |
5059 | if (regno < 0 | |
5060 | || (MEM_P (x) | |
5061 | && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))) | |
5062 | /* In this case we can use fld.s, fst.s, fld.d or fst.d. */ | |
5063 | return NO_REGS; | |
5064 | ||
5065 | if (GP_REG_P (regno) || x == CONST0_RTX (mode)) | |
5066 | /* In this case we can use movgr2fr.s, movfr2gr.s, movgr2fr.d or | |
5067 | * movfr2gr.d. */ | |
5068 | return NO_REGS; | |
5069 | ||
5070 | if (CONSTANT_P (x) && !targetm.cannot_force_const_mem (mode, x)) | |
5071 | /* We can force the constant to memory and use fld.s | |
5072 | and fld.d. As above, we will use pairs of lwc1s if | |
5073 | ldc1 is not supported. */ | |
5074 | return NO_REGS; | |
5075 | ||
5076 | if (FP_REG_P (regno) && loongarch_mode_ok_for_mov_fmt_p (mode)) | |
5077 | /* In this case we can use fmov.{s/d}. */ | |
5078 | return NO_REGS; | |
5079 | ||
5080 | /* Otherwise, we need to reload through an integer register. */ | |
5081 | return GR_REGS; | |
5082 | } | |
5083 | if (FP_REG_P (regno)) | |
5084 | return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS; | |
5085 | ||
5086 | return NO_REGS; | |
5087 | } | |
5088 | ||
5089 | /* Implement TARGET_VALID_POINTER_MODE. */ | |
5090 | ||
5091 | static bool | |
5092 | loongarch_valid_pointer_mode (scalar_int_mode mode) | |
5093 | { | |
5094 | return mode == SImode || (TARGET_64BIT && mode == DImode); | |
5095 | } | |
5096 | ||
5097 | /* Implement TARGET_SCALAR_MODE_SUPPORTED_P. */ | |
5098 | ||
5099 | static bool | |
5100 | loongarch_scalar_mode_supported_p (scalar_mode mode) | |
5101 | { | |
5102 | if (ALL_FIXED_POINT_MODE_P (mode) | |
5103 | && GET_MODE_PRECISION (mode) <= 2 * BITS_PER_WORD) | |
5104 | return true; | |
5105 | ||
5106 | return default_scalar_mode_supported_p (mode); | |
5107 | } | |
5108 | ||
5109 | /* Return the assembly code for INSN, which has the operands given by | |
5110 | OPERANDS, and which branches to OPERANDS[0] if some condition is true. | |
5111 | BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[0] | |
5112 | is in range of a direct branch. BRANCH_IF_FALSE is an inverted | |
5113 | version of BRANCH_IF_TRUE. */ | |
5114 | ||
5115 | const char * | |
5116 | loongarch_output_conditional_branch (rtx_insn *insn, rtx *operands, | |
5117 | const char *branch_if_true, | |
5118 | const char *branch_if_false) | |
5119 | { | |
5120 | unsigned int length; | |
5121 | rtx taken; | |
5122 | ||
5123 | gcc_assert (LABEL_P (operands[0])); | |
5124 | ||
5125 | length = get_attr_length (insn); | |
5126 | if (length <= 4) | |
5127 | { | |
5128 | return branch_if_true; | |
5129 | } | |
5130 | ||
5131 | /* Generate a reversed branch around a direct jump. */ | |
5132 | rtx_code_label *not_taken = gen_label_rtx (); | |
5133 | taken = operands[0]; | |
5134 | ||
5135 | /* Generate the reversed branch to NOT_TAKEN. */ | |
5136 | operands[0] = not_taken; | |
5137 | output_asm_insn (branch_if_false, operands); | |
5138 | ||
5139 | output_asm_insn ("b\t%0", &taken); | |
5140 | ||
5141 | /* Output NOT_TAKEN. */ | |
5142 | targetm.asm_out.internal_label (asm_out_file, "L", | |
5143 | CODE_LABEL_NUMBER (not_taken)); | |
5144 | return ""; | |
5145 | } | |
5146 | ||
5147 | /* Return the assembly code for INSN, which branches to OPERANDS[0] | |
5148 | if some equality condition is true. The condition is given by | |
5149 | OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of | |
5150 | OPERANDS[1]. OPERANDS[2] is the comparison's first operand; | |
5151 | OPERANDS[3] is the second operand and may be zero or a register. */ | |
5152 | ||
5153 | const char * | |
5154 | loongarch_output_equal_conditional_branch (rtx_insn *insn, rtx *operands, | |
5155 | bool inverted_p) | |
5156 | { | |
5157 | const char *branch[2]; | |
5158 | if (operands[3] == const0_rtx) | |
5159 | { | |
5160 | branch[!inverted_p] = LARCH_BRANCH ("b%C1z", "%2,%0"); | |
5161 | branch[inverted_p] = LARCH_BRANCH ("b%N1z", "%2,%0"); | |
5162 | } | |
5163 | else | |
5164 | { | |
5165 | branch[!inverted_p] = LARCH_BRANCH ("b%C1", "%2,%z3,%0"); | |
5166 | branch[inverted_p] = LARCH_BRANCH ("b%N1", "%2,%z3,%0"); | |
5167 | } | |
5168 | ||
5169 | return loongarch_output_conditional_branch (insn, operands, branch[1], | |
5170 | branch[0]); | |
5171 | } | |
5172 | ||
5173 | /* Return the assembly code for INSN, which branches to OPERANDS[0] | |
5174 | if some ordering condition is true. The condition is given by | |
5175 | OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of | |
5176 | OPERANDS[1]. OPERANDS[2] is the comparison's first operand; | |
5177 | OPERANDS[3] is the second operand and may be zero or a register. */ | |
5178 | ||
5179 | const char * | |
5180 | loongarch_output_order_conditional_branch (rtx_insn *insn, rtx *operands, | |
5181 | bool inverted_p) | |
5182 | { | |
5183 | const char *branch[2]; | |
5184 | ||
5185 | /* Make BRANCH[1] branch to OPERANDS[0] when the condition is true. | |
5186 | Make BRANCH[0] branch on the inverse condition. */ | |
5187 | if (operands[3] != const0_rtx) | |
5188 | { | |
5189 | /* Handle degenerate cases that should not, but do, occur. */ | |
5190 | if (REGNO (operands[2]) == REGNO (operands[3])) | |
5191 | { | |
5192 | switch (GET_CODE (operands[1])) | |
5193 | { | |
5194 | case LT: | |
5195 | case LTU: | |
5196 | case GT: | |
5197 | case GTU: | |
5198 | inverted_p = !inverted_p; | |
5199 | /* Fall through. */ | |
5200 | case LE: | |
5201 | case LEU: | |
5202 | case GE: | |
5203 | case GEU: | |
5204 | branch[!inverted_p] = LARCH_BRANCH ("b", "%0"); | |
5205 | branch[inverted_p] = "\t# branch never"; | |
5206 | break; | |
5207 | default: | |
5208 | gcc_unreachable (); | |
5209 | } | |
5210 | } | |
5211 | else | |
5212 | { | |
5213 | switch (GET_CODE (operands[1])) | |
5214 | { | |
5215 | case LE: | |
5216 | case LEU: | |
5217 | case GT: | |
5218 | case GTU: | |
5219 | case LT: | |
5220 | case LTU: | |
5221 | case GE: | |
5222 | case GEU: | |
5223 | branch[!inverted_p] = LARCH_BRANCH ("b%C1", "%2,%3,%0"); | |
5224 | branch[inverted_p] = LARCH_BRANCH ("b%N1", "%2,%3,%0"); | |
5225 | break; | |
5226 | default: | |
5227 | gcc_unreachable (); | |
5228 | } | |
5229 | } | |
5230 | } | |
5231 | else | |
5232 | { | |
5233 | switch (GET_CODE (operands[1])) | |
5234 | { | |
5235 | /* These cases are equivalent to comparisons against zero. */ | |
5236 | case LEU: | |
5237 | case GTU: | |
5238 | case LTU: | |
5239 | case GEU: | |
5240 | case LE: | |
5241 | case GT: | |
5242 | case LT: | |
5243 | case GE: | |
5244 | branch[!inverted_p] = LARCH_BRANCH ("b%C1", "%2,$r0,%0"); | |
5245 | branch[inverted_p] = LARCH_BRANCH ("b%N1", "%2,$r0,%0"); | |
5246 | break; | |
5247 | default: | |
5248 | gcc_unreachable (); | |
5249 | } | |
5250 | } | |
5251 | return loongarch_output_conditional_branch (insn, operands, branch[1], | |
5252 | branch[0]); | |
5253 | } | |
5254 | ||
5255 | /* Return the assembly code for DIV.{W/D} instruction DIVISION, which has | |
5256 | the operands given by OPERANDS. Add in a divide-by-zero check if needed. | |
5257 | */ | |
5258 | ||
5259 | const char * | |
5260 | loongarch_output_division (const char *division, rtx *operands) | |
5261 | { | |
5262 | const char *s; | |
5263 | ||
5264 | s = division; | |
5265 | if (TARGET_CHECK_ZERO_DIV) | |
5266 | { | |
5267 | output_asm_insn (s, operands); | |
5268 | s = "bne\t%2,%.,1f\n\tbreak\t7\n1:"; | |
5269 | } | |
5270 | return s; | |
5271 | } | |
5272 | ||
5273 | /* Implement TARGET_SCHED_ADJUST_COST. We assume that anti and output | |
5274 | dependencies have no cost. */ | |
5275 | ||
5276 | static int | |
5277 | loongarch_adjust_cost (rtx_insn *, int dep_type, rtx_insn *, int cost, | |
5278 | unsigned int) | |
5279 | { | |
5280 | if (dep_type != 0 && (dep_type != REG_DEP_OUTPUT)) | |
5281 | return 0; | |
5282 | return cost; | |
5283 | } | |
5284 | ||
5285 | /* Return the number of instructions that can be issued per cycle. */ | |
5286 | ||
5287 | static int | |
5288 | loongarch_issue_rate (void) | |
5289 | { | |
5290 | if ((unsigned long) LARCH_ACTUAL_TUNE < N_TUNE_TYPES) | |
5291 | return loongarch_cpu_issue_rate[LARCH_ACTUAL_TUNE]; | |
5292 | else | |
5293 | return 1; | |
5294 | } | |
5295 | ||
5296 | /* Implement TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD. This should | |
5297 | be as wide as the scheduling freedom in the DFA. */ | |
5298 | ||
5299 | static int | |
5300 | loongarch_multipass_dfa_lookahead (void) | |
5301 | { | |
5302 | if ((unsigned long) LARCH_ACTUAL_TUNE < N_ARCH_TYPES) | |
5303 | return loongarch_cpu_multipass_dfa_lookahead[LARCH_ACTUAL_TUNE]; | |
5304 | else | |
5305 | return 0; | |
5306 | } | |
5307 | ||
5308 | /* Implement TARGET_SCHED_REORDER. */ | |
5309 | ||
5310 | static int | |
5311 | loongarch_sched_reorder (FILE *file ATTRIBUTE_UNUSED, | |
5312 | int verbose ATTRIBUTE_UNUSED, | |
5313 | rtx_insn **ready ATTRIBUTE_UNUSED, | |
5314 | int *nreadyp ATTRIBUTE_UNUSED, | |
5315 | int cycle ATTRIBUTE_UNUSED) | |
5316 | { | |
5317 | return loongarch_issue_rate (); | |
5318 | } | |
5319 | ||
5320 | /* Implement TARGET_SCHED_REORDER2. */ | |
5321 | ||
5322 | static int | |
5323 | loongarch_sched_reorder2 (FILE *file ATTRIBUTE_UNUSED, | |
5324 | int verbose ATTRIBUTE_UNUSED, | |
5325 | rtx_insn **ready ATTRIBUTE_UNUSED, | |
5326 | int *nreadyp ATTRIBUTE_UNUSED, | |
5327 | int cycle ATTRIBUTE_UNUSED) | |
5328 | { | |
5329 | return cached_can_issue_more; | |
5330 | } | |
5331 | ||
5332 | /* Implement TARGET_SCHED_INIT. */ | |
5333 | ||
5334 | static void | |
5335 | loongarch_sched_init (FILE *file ATTRIBUTE_UNUSED, | |
5336 | int verbose ATTRIBUTE_UNUSED, | |
5337 | int max_ready ATTRIBUTE_UNUSED) | |
5338 | {} | |
5339 | ||
5340 | /* Implement TARGET_SCHED_VARIABLE_ISSUE. */ | |
5341 | ||
5342 | static int | |
5343 | loongarch_variable_issue (FILE *file ATTRIBUTE_UNUSED, | |
5344 | int verbose ATTRIBUTE_UNUSED, rtx_insn *insn, | |
5345 | int more) | |
5346 | { | |
5347 | /* Ignore USEs and CLOBBERs; don't count them against the issue rate. */ | |
5348 | if (USEFUL_INSN_P (insn)) | |
5349 | { | |
5350 | if (get_attr_type (insn) != TYPE_GHOST) | |
5351 | more--; | |
5352 | } | |
5353 | ||
5354 | /* Instructions of type 'multi' should all be split before | |
5355 | the second scheduling pass. */ | |
5356 | gcc_assert (!reload_completed | |
5357 | || recog_memoized (insn) < 0 | |
5358 | || get_attr_type (insn) != TYPE_MULTI); | |
5359 | ||
5360 | cached_can_issue_more = more; | |
5361 | return more; | |
5362 | } | |
5363 | ||
5364 | /* Given that we have an rtx of the form (prefetch ... WRITE LOCALITY), | |
5365 | return the first operand of the associated PREF or PREFX insn. */ | |
5366 | ||
5367 | rtx | |
5368 | loongarch_prefetch_cookie (rtx write, rtx locality) | |
5369 | { | |
5370 | /* store_streamed / load_streamed. */ | |
5371 | if (INTVAL (locality) <= 0) | |
5372 | return GEN_INT (INTVAL (write) + 4); | |
5373 | ||
5374 | /* store / load. */ | |
5375 | if (INTVAL (locality) <= 2) | |
5376 | return write; | |
5377 | ||
5378 | /* store_retained / load_retained. */ | |
5379 | return GEN_INT (INTVAL (write) + 6); | |
5380 | } | |
5381 | ||
5382 | /* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text | |
5383 | in order to avoid duplicating too much logic from elsewhere. */ | |
5384 | ||
5385 | static void | |
5386 | loongarch_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, | |
5387 | HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, | |
5388 | tree function) | |
5389 | { | |
5390 | const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl)); | |
5391 | rtx this_rtx, temp1, temp2, fnaddr; | |
5392 | rtx_insn *insn; | |
5393 | bool use_sibcall_p; | |
5394 | ||
5395 | /* Pretend to be a post-reload pass while generating rtl. */ | |
5396 | reload_completed = 1; | |
5397 | ||
5398 | /* Mark the end of the (empty) prologue. */ | |
5399 | emit_note (NOTE_INSN_PROLOGUE_END); | |
5400 | ||
5401 | /* Determine if we can use a sibcall to call FUNCTION directly. */ | |
5402 | fnaddr = XEXP (DECL_RTL (function), 0); | |
5403 | use_sibcall_p = const_call_insn_operand (fnaddr, Pmode); | |
5404 | ||
5405 | /* We need two temporary registers in some cases. */ | |
5406 | temp1 = gen_rtx_REG (Pmode, 12); | |
5407 | temp2 = gen_rtx_REG (Pmode, 13); | |
5408 | ||
5409 | /* Find out which register contains the "this" pointer. */ | |
5410 | if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) | |
5411 | this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1); | |
5412 | else | |
5413 | this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST); | |
5414 | ||
5415 | /* Add DELTA to THIS_RTX. */ | |
5416 | if (delta != 0) | |
5417 | { | |
5418 | rtx offset = GEN_INT (delta); | |
5419 | if (!IMM12_OPERAND (delta)) | |
5420 | { | |
5421 | loongarch_emit_move (temp1, offset); | |
5422 | offset = temp1; | |
5423 | } | |
5424 | emit_insn (gen_add3_insn (this_rtx, this_rtx, offset)); | |
5425 | } | |
5426 | ||
5427 | /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX. */ | |
5428 | if (vcall_offset != 0) | |
5429 | { | |
5430 | rtx addr; | |
5431 | ||
5432 | /* Set TEMP1 to *THIS_RTX. */ | |
5433 | loongarch_emit_move (temp1, gen_rtx_MEM (Pmode, this_rtx)); | |
5434 | ||
5435 | /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET. */ | |
5436 | addr = loongarch_add_offset (temp2, temp1, vcall_offset); | |
5437 | ||
5438 | /* Load the offset and add it to THIS_RTX. */ | |
5439 | loongarch_emit_move (temp1, gen_rtx_MEM (Pmode, addr)); | |
5440 | emit_insn (gen_add3_insn (this_rtx, this_rtx, temp1)); | |
5441 | } | |
5442 | ||
5443 | /* Jump to the target function. Use a sibcall if direct jumps are | |
5444 | allowed, otherwise load the address into a register first. */ | |
5445 | if (use_sibcall_p) | |
5446 | { | |
5447 | insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx)); | |
5448 | SIBLING_CALL_P (insn) = 1; | |
5449 | } | |
5450 | else | |
5451 | { | |
5452 | loongarch_emit_move (temp1, fnaddr); | |
5453 | emit_jump_insn (gen_indirect_jump (temp1)); | |
5454 | } | |
5455 | ||
5456 | /* Run just enough of rest_of_compilation. This sequence was | |
5457 | "borrowed" from alpha.c. */ | |
5458 | insn = get_insns (); | |
5459 | split_all_insns_noflow (); | |
5460 | shorten_branches (insn); | |
5461 | assemble_start_function (thunk_fndecl, fnname); | |
5462 | final_start_function (insn, file, 1); | |
5463 | final (insn, file, 1); | |
5464 | final_end_function (); | |
5465 | assemble_end_function (thunk_fndecl, fnname); | |
5466 | ||
5467 | /* Stop pretending to be a post-reload pass. */ | |
5468 | reload_completed = 0; | |
5469 | } | |
5470 | ||
5471 | /* Allocate a chunk of memory for per-function machine-dependent data. */ | |
5472 | ||
5473 | static struct machine_function * | |
5474 | loongarch_init_machine_status (void) | |
5475 | { | |
5476 | return ggc_cleared_alloc<machine_function> (); | |
5477 | } | |
5478 | ||
5479 | static void | |
5480 | loongarch_option_override_internal (struct gcc_options *opts) | |
5481 | { | |
5482 | int i, regno, mode; | |
5483 | ||
5484 | if (flag_pic) | |
5485 | g_switch_value = 0; | |
5486 | ||
5487 | /* Handle target-specific options: compute defaults/conflicts etc. */ | |
5488 | loongarch_config_target (&la_target, la_opt_switches, | |
5489 | la_opt_cpu_arch, la_opt_cpu_tune, la_opt_fpu, | |
5490 | la_opt_abi_base, la_opt_abi_ext, la_opt_cmodel, 0); | |
5491 | ||
5492 | if (TARGET_ABI_LP64) | |
5493 | flag_pcc_struct_return = 0; | |
5494 | ||
5495 | /* Decide which rtx_costs structure to use. */ | |
5496 | if (optimize_size) | |
5497 | loongarch_cost = &loongarch_rtx_cost_optimize_size; | |
5498 | else | |
5499 | loongarch_cost = &loongarch_cpu_rtx_cost_data[LARCH_ACTUAL_TUNE]; | |
5500 | ||
5501 | /* If the user hasn't specified a branch cost, use the processor's | |
5502 | default. */ | |
5503 | if (loongarch_branch_cost == 0) | |
5504 | loongarch_branch_cost = loongarch_cost->branch_cost; | |
5505 | ||
5506 | ||
5507 | switch (la_target.cmodel) | |
5508 | { | |
5509 | case CMODEL_TINY_STATIC: | |
5510 | case CMODEL_EXTREME: | |
5511 | if (opts->x_flag_plt) | |
5512 | error ("code model %qs and %qs not support %s mode", | |
5513 | "tiny-static", "extreme", "plt"); | |
5514 | break; | |
5515 | ||
5516 | case CMODEL_NORMAL: | |
5517 | case CMODEL_TINY: | |
5518 | case CMODEL_LARGE: | |
5519 | break; | |
5520 | ||
5521 | default: | |
5522 | gcc_unreachable (); | |
5523 | } | |
5524 | ||
5525 | loongarch_init_print_operand_punct (); | |
5526 | ||
5527 | /* Set up array to map GCC register number to debug register number. | |
5528 | Ignore the special purpose register numbers. */ | |
5529 | ||
5530 | for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) | |
5531 | { | |
5532 | if (GP_REG_P (i) || FP_REG_P (i)) | |
5533 | loongarch_dwarf_regno[i] = i; | |
5534 | else | |
5535 | loongarch_dwarf_regno[i] = INVALID_REGNUM; | |
5536 | } | |
5537 | ||
5538 | /* Set up loongarch_hard_regno_mode_ok. */ | |
5539 | for (mode = 0; mode < MAX_MACHINE_MODE; mode++) | |
5540 | for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) | |
5541 | loongarch_hard_regno_mode_ok_p[mode][regno] | |
5542 | = loongarch_hard_regno_mode_ok_uncached (regno, (machine_mode) mode); | |
5543 | ||
5544 | /* Function to allocate machine-dependent function status. */ | |
5545 | init_machine_status = &loongarch_init_machine_status; | |
5546 | } | |
5547 | ||
5548 | ||
5549 | /* Implement TARGET_OPTION_OVERRIDE. */ | |
5550 | ||
5551 | static void | |
5552 | loongarch_option_override (void) | |
5553 | { | |
5554 | loongarch_option_override_internal (&global_options); | |
5555 | } | |
5556 | ||
5557 | /* Implement TARGET_CONDITIONAL_REGISTER_USAGE. */ | |
5558 | ||
5559 | static void | |
5560 | loongarch_conditional_register_usage (void) | |
5561 | { | |
5562 | if (!TARGET_HARD_FLOAT) | |
5563 | accessible_reg_set &= ~(reg_class_contents[FP_REGS] | |
5564 | | reg_class_contents[FCC_REGS]); | |
5565 | } | |
5566 | ||
5567 | /* Implement EH_USES. */ | |
5568 | ||
5569 | bool | |
5570 | loongarch_eh_uses (unsigned int regno ATTRIBUTE_UNUSED) | |
5571 | { | |
5572 | return false; | |
5573 | } | |
5574 | ||
5575 | /* Implement EPILOGUE_USES. */ | |
5576 | ||
5577 | bool | |
5578 | loongarch_epilogue_uses (unsigned int regno) | |
5579 | { | |
5580 | /* Say that the epilogue uses the return address register. Note that | |
5581 | in the case of sibcalls, the values "used by the epilogue" are | |
5582 | considered live at the start of the called function. */ | |
5583 | if (regno == RETURN_ADDR_REGNUM) | |
5584 | return true; | |
5585 | ||
5586 | return false; | |
5587 | } | |
5588 | ||
5589 | bool | |
5590 | loongarch_load_store_bonding_p (rtx *operands, machine_mode mode, bool load_p) | |
5591 | { | |
5592 | rtx reg1, reg2, mem1, mem2, base1, base2; | |
5593 | enum reg_class rc1, rc2; | |
5594 | HOST_WIDE_INT offset1, offset2; | |
5595 | ||
5596 | if (load_p) | |
5597 | { | |
5598 | reg1 = operands[0]; | |
5599 | reg2 = operands[2]; | |
5600 | mem1 = operands[1]; | |
5601 | mem2 = operands[3]; | |
5602 | } | |
5603 | else | |
5604 | { | |
5605 | reg1 = operands[1]; | |
5606 | reg2 = operands[3]; | |
5607 | mem1 = operands[0]; | |
5608 | mem2 = operands[2]; | |
5609 | } | |
5610 | ||
5611 | if (loongarch_address_insns (XEXP (mem1, 0), mode, false) == 0 | |
5612 | || loongarch_address_insns (XEXP (mem2, 0), mode, false) == 0) | |
5613 | return false; | |
5614 | ||
5615 | loongarch_split_plus (XEXP (mem1, 0), &base1, &offset1); | |
5616 | loongarch_split_plus (XEXP (mem2, 0), &base2, &offset2); | |
5617 | ||
5618 | /* Base regs do not match. */ | |
5619 | if (!REG_P (base1) || !rtx_equal_p (base1, base2)) | |
5620 | return false; | |
5621 | ||
5622 | /* Either of the loads is clobbering base register. It is legitimate to bond | |
5623 | loads if second load clobbers base register. However, hardware does not | |
5624 | support such bonding. */ | |
5625 | if (load_p | |
5626 | && (REGNO (reg1) == REGNO (base1) || (REGNO (reg2) == REGNO (base1)))) | |
5627 | return false; | |
5628 | ||
5629 | /* Loading in same registers. */ | |
5630 | if (load_p && REGNO (reg1) == REGNO (reg2)) | |
5631 | return false; | |
5632 | ||
5633 | /* The loads/stores are not of same type. */ | |
5634 | rc1 = REGNO_REG_CLASS (REGNO (reg1)); | |
5635 | rc2 = REGNO_REG_CLASS (REGNO (reg2)); | |
5636 | if (rc1 != rc2 && !reg_class_subset_p (rc1, rc2) | |
5637 | && !reg_class_subset_p (rc2, rc1)) | |
5638 | return false; | |
5639 | ||
5640 | if (abs (offset1 - offset2) != GET_MODE_SIZE (mode)) | |
5641 | return false; | |
5642 | ||
5643 | return true; | |
5644 | } | |
5645 | ||
5646 | /* Implement TARGET_TRAMPOLINE_INIT. */ | |
5647 | ||
5648 | static void | |
5649 | loongarch_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) | |
5650 | { | |
5651 | rtx addr, end_addr, mem; | |
5652 | rtx trampoline[8]; | |
5653 | unsigned int i, j; | |
5654 | HOST_WIDE_INT end_addr_offset, static_chain_offset, target_function_offset; | |
5655 | ||
5656 | /* Work out the offsets of the pointers from the start of the | |
5657 | trampoline code. */ | |
5658 | end_addr_offset = TRAMPOLINE_CODE_SIZE; | |
5659 | static_chain_offset = end_addr_offset; | |
5660 | target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode); | |
5661 | ||
5662 | /* Get pointers to the beginning and end of the code block. */ | |
5663 | addr = force_reg (Pmode, XEXP (m_tramp, 0)); | |
5664 | end_addr | |
5665 | = loongarch_force_binary (Pmode, PLUS, addr, GEN_INT (end_addr_offset)); | |
5666 | ||
5667 | #define OP(X) gen_int_mode (X, SImode) | |
5668 | ||
5669 | /* Build up the code in TRAMPOLINE. */ | |
5670 | i = 0; | |
5671 | /*pcaddi $static_chain,0 | |
5672 | ld.[dw] $tmp,$static_chain,target_function_offset | |
5673 | ld.[dw] $static_chain,$static_chain,static_chain_offset | |
5674 | jirl $r0,$tmp,0 */ | |
5675 | trampoline[i++] = OP (0x18000000 | (STATIC_CHAIN_REGNUM - GP_REG_FIRST)); | |
5676 | trampoline[i++] = OP ((ptr_mode == DImode ? 0x28c00000 : 0x28800000) | |
5677 | | 19 /* $t7 */ | |
5678 | | ((STATIC_CHAIN_REGNUM - GP_REG_FIRST) << 5) | |
5679 | | ((target_function_offset & 0xfff) << 10)); | |
5680 | trampoline[i++] = OP ((ptr_mode == DImode ? 0x28c00000 : 0x28800000) | |
5681 | | (STATIC_CHAIN_REGNUM - GP_REG_FIRST) | |
5682 | | ((STATIC_CHAIN_REGNUM - GP_REG_FIRST) << 5) | |
5683 | | ((static_chain_offset & 0xfff) << 10)); | |
5684 | trampoline[i++] = OP (0x4c000000 | (19 << 5)); | |
5685 | #undef OP | |
5686 | ||
5687 | for (j = 0; j < i; j++) | |
5688 | { | |
5689 | mem = adjust_address (m_tramp, SImode, j * GET_MODE_SIZE (SImode)); | |
5690 | loongarch_emit_move (mem, trampoline[j]); | |
5691 | } | |
5692 | ||
5693 | /* Set up the static chain pointer field. */ | |
5694 | mem = adjust_address (m_tramp, ptr_mode, static_chain_offset); | |
5695 | loongarch_emit_move (mem, chain_value); | |
5696 | ||
5697 | /* Set up the target function field. */ | |
5698 | mem = adjust_address (m_tramp, ptr_mode, target_function_offset); | |
5699 | loongarch_emit_move (mem, XEXP (DECL_RTL (fndecl), 0)); | |
5700 | ||
5701 | /* Flush the code part of the trampoline. */ | |
5702 | emit_insn (gen_add3_insn (end_addr, addr, GEN_INT (TRAMPOLINE_SIZE))); | |
5703 | emit_insn (gen_clear_cache (addr, end_addr)); | |
5704 | } | |
5705 | ||
5706 | /* Implement HARD_REGNO_CALLER_SAVE_MODE. */ | |
5707 | ||
5708 | machine_mode | |
5709 | loongarch_hard_regno_caller_save_mode (unsigned int regno, unsigned int nregs, | |
5710 | machine_mode mode) | |
5711 | { | |
5712 | /* For performance, avoid saving/restoring upper parts of a register | |
5713 | by returning MODE as save mode when the mode is known. */ | |
5714 | if (mode == VOIDmode) | |
5715 | return choose_hard_reg_mode (regno, nregs, NULL); | |
5716 | else | |
5717 | return mode; | |
5718 | } | |
5719 | ||
5720 | /* Implement TARGET_SPILL_CLASS. */ | |
5721 | ||
5722 | static reg_class_t | |
5723 | loongarch_spill_class (reg_class_t rclass ATTRIBUTE_UNUSED, | |
5724 | machine_mode mode ATTRIBUTE_UNUSED) | |
5725 | { | |
5726 | return NO_REGS; | |
5727 | } | |
5728 | ||
5729 | /* Implement TARGET_PROMOTE_FUNCTION_MODE. */ | |
5730 | ||
5731 | /* This function is equivalent to default_promote_function_mode_always_promote | |
5732 | except that it returns a promoted mode even if type is NULL_TREE. This is | |
5733 | needed by libcalls which have no type (only a mode) such as fixed conversion | |
5734 | routines that take a signed or unsigned char/short argument and convert it | |
5735 | to a fixed type. */ | |
5736 | ||
5737 | static machine_mode | |
5738 | loongarch_promote_function_mode (const_tree type ATTRIBUTE_UNUSED, | |
5739 | machine_mode mode, | |
5740 | int *punsignedp ATTRIBUTE_UNUSED, | |
5741 | const_tree fntype ATTRIBUTE_UNUSED, | |
5742 | int for_return ATTRIBUTE_UNUSED) | |
5743 | { | |
5744 | int unsignedp; | |
5745 | ||
5746 | if (type != NULL_TREE) | |
5747 | return promote_mode (type, mode, punsignedp); | |
5748 | ||
5749 | unsignedp = *punsignedp; | |
5750 | PROMOTE_MODE (mode, unsignedp, type); | |
5751 | *punsignedp = unsignedp; | |
5752 | return mode; | |
5753 | } | |
5754 | ||
5755 | /* Implement TARGET_STARTING_FRAME_OFFSET. See loongarch_compute_frame_info | |
5756 | for details about the frame layout. */ | |
5757 | ||
5758 | static HOST_WIDE_INT | |
5759 | loongarch_starting_frame_offset (void) | |
5760 | { | |
5761 | if (FRAME_GROWS_DOWNWARD) | |
5762 | return 0; | |
5763 | return crtl->outgoing_args_size; | |
5764 | } | |
5765 | ||
5766 | /* Initialize the GCC target structure. */ | |
5767 | #undef TARGET_ASM_ALIGNED_HI_OP | |
5768 | #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t" | |
5769 | #undef TARGET_ASM_ALIGNED_SI_OP | |
5770 | #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" | |
5771 | #undef TARGET_ASM_ALIGNED_DI_OP | |
5772 | #define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t" | |
5773 | ||
5774 | #undef TARGET_OPTION_OVERRIDE | |
5775 | #define TARGET_OPTION_OVERRIDE loongarch_option_override | |
5776 | ||
5777 | #undef TARGET_LEGITIMIZE_ADDRESS | |
5778 | #define TARGET_LEGITIMIZE_ADDRESS loongarch_legitimize_address | |
5779 | ||
5780 | #undef TARGET_ASM_SELECT_RTX_SECTION | |
5781 | #define TARGET_ASM_SELECT_RTX_SECTION loongarch_select_rtx_section | |
5782 | #undef TARGET_ASM_FUNCTION_RODATA_SECTION | |
5783 | #define TARGET_ASM_FUNCTION_RODATA_SECTION loongarch_function_rodata_section | |
5784 | ||
5785 | #undef TARGET_SCHED_INIT | |
5786 | #define TARGET_SCHED_INIT loongarch_sched_init | |
5787 | #undef TARGET_SCHED_REORDER | |
5788 | #define TARGET_SCHED_REORDER loongarch_sched_reorder | |
5789 | #undef TARGET_SCHED_REORDER2 | |
5790 | #define TARGET_SCHED_REORDER2 loongarch_sched_reorder2 | |
5791 | #undef TARGET_SCHED_VARIABLE_ISSUE | |
5792 | #define TARGET_SCHED_VARIABLE_ISSUE loongarch_variable_issue | |
5793 | #undef TARGET_SCHED_ADJUST_COST | |
5794 | #define TARGET_SCHED_ADJUST_COST loongarch_adjust_cost | |
5795 | #undef TARGET_SCHED_ISSUE_RATE | |
5796 | #define TARGET_SCHED_ISSUE_RATE loongarch_issue_rate | |
5797 | #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD | |
5798 | #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \ | |
5799 | loongarch_multipass_dfa_lookahead | |
5800 | ||
5801 | #undef TARGET_FUNCTION_OK_FOR_SIBCALL | |
5802 | #define TARGET_FUNCTION_OK_FOR_SIBCALL loongarch_function_ok_for_sibcall | |
5803 | ||
5804 | #undef TARGET_VALID_POINTER_MODE | |
5805 | #define TARGET_VALID_POINTER_MODE loongarch_valid_pointer_mode | |
5806 | #undef TARGET_REGISTER_MOVE_COST | |
5807 | #define TARGET_REGISTER_MOVE_COST loongarch_register_move_cost | |
5808 | #undef TARGET_MEMORY_MOVE_COST | |
5809 | #define TARGET_MEMORY_MOVE_COST loongarch_memory_move_cost | |
5810 | #undef TARGET_RTX_COSTS | |
5811 | #define TARGET_RTX_COSTS loongarch_rtx_costs | |
5812 | #undef TARGET_ADDRESS_COST | |
5813 | #define TARGET_ADDRESS_COST loongarch_address_cost | |
5814 | ||
5815 | #undef TARGET_IN_SMALL_DATA_P | |
5816 | #define TARGET_IN_SMALL_DATA_P loongarch_in_small_data_p | |
5817 | ||
5818 | #undef TARGET_PREFERRED_RELOAD_CLASS | |
5819 | #define TARGET_PREFERRED_RELOAD_CLASS loongarch_preferred_reload_class | |
5820 | ||
5821 | #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE | |
5822 | #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true | |
5823 | ||
5824 | #undef TARGET_EXPAND_BUILTIN_VA_START | |
5825 | #define TARGET_EXPAND_BUILTIN_VA_START loongarch_va_start | |
5826 | ||
5827 | #undef TARGET_PROMOTE_FUNCTION_MODE | |
5828 | #define TARGET_PROMOTE_FUNCTION_MODE loongarch_promote_function_mode | |
5829 | #undef TARGET_RETURN_IN_MEMORY | |
5830 | #define TARGET_RETURN_IN_MEMORY loongarch_return_in_memory | |
5831 | ||
5832 | #undef TARGET_FUNCTION_VALUE | |
5833 | #define TARGET_FUNCTION_VALUE loongarch_function_value | |
5834 | #undef TARGET_LIBCALL_VALUE | |
5835 | #define TARGET_LIBCALL_VALUE loongarch_libcall_value | |
5836 | ||
5837 | #undef TARGET_ASM_OUTPUT_MI_THUNK | |
5838 | #define TARGET_ASM_OUTPUT_MI_THUNK loongarch_output_mi_thunk | |
5839 | #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK | |
5840 | #define TARGET_ASM_CAN_OUTPUT_MI_THUNK \ | |
5841 | hook_bool_const_tree_hwi_hwi_const_tree_true | |
5842 | ||
5843 | #undef TARGET_PRINT_OPERAND | |
5844 | #define TARGET_PRINT_OPERAND loongarch_print_operand | |
5845 | #undef TARGET_PRINT_OPERAND_ADDRESS | |
5846 | #define TARGET_PRINT_OPERAND_ADDRESS loongarch_print_operand_address | |
5847 | #undef TARGET_PRINT_OPERAND_PUNCT_VALID_P | |
5848 | #define TARGET_PRINT_OPERAND_PUNCT_VALID_P \ | |
5849 | loongarch_print_operand_punct_valid_p | |
5850 | ||
5851 | #undef TARGET_SETUP_INCOMING_VARARGS | |
5852 | #define TARGET_SETUP_INCOMING_VARARGS loongarch_setup_incoming_varargs | |
5853 | #undef TARGET_STRICT_ARGUMENT_NAMING | |
5854 | #define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true | |
5855 | #undef TARGET_MUST_PASS_IN_STACK | |
5856 | #define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size | |
5857 | #undef TARGET_PASS_BY_REFERENCE | |
5858 | #define TARGET_PASS_BY_REFERENCE loongarch_pass_by_reference | |
5859 | #undef TARGET_ARG_PARTIAL_BYTES | |
5860 | #define TARGET_ARG_PARTIAL_BYTES loongarch_arg_partial_bytes | |
5861 | #undef TARGET_FUNCTION_ARG | |
5862 | #define TARGET_FUNCTION_ARG loongarch_function_arg | |
5863 | #undef TARGET_FUNCTION_ARG_ADVANCE | |
5864 | #define TARGET_FUNCTION_ARG_ADVANCE loongarch_function_arg_advance | |
5865 | #undef TARGET_FUNCTION_ARG_BOUNDARY | |
5866 | #define TARGET_FUNCTION_ARG_BOUNDARY loongarch_function_arg_boundary | |
5867 | ||
5868 | #undef TARGET_SCALAR_MODE_SUPPORTED_P | |
5869 | #define TARGET_SCALAR_MODE_SUPPORTED_P loongarch_scalar_mode_supported_p | |
5870 | ||
5871 | #undef TARGET_INIT_BUILTINS | |
5872 | #define TARGET_INIT_BUILTINS loongarch_init_builtins | |
5873 | #undef TARGET_BUILTIN_DECL | |
5874 | #define TARGET_BUILTIN_DECL loongarch_builtin_decl | |
5875 | #undef TARGET_EXPAND_BUILTIN | |
5876 | #define TARGET_EXPAND_BUILTIN loongarch_expand_builtin | |
5877 | ||
5878 | /* The generic ELF target does not always have TLS support. */ | |
5879 | #ifdef HAVE_AS_TLS | |
5880 | #undef TARGET_HAVE_TLS | |
5881 | #define TARGET_HAVE_TLS HAVE_AS_TLS | |
5882 | #endif | |
5883 | ||
5884 | #undef TARGET_CANNOT_FORCE_CONST_MEM | |
5885 | #define TARGET_CANNOT_FORCE_CONST_MEM loongarch_cannot_force_const_mem | |
5886 | ||
5887 | #undef TARGET_LEGITIMATE_CONSTANT_P | |
5888 | #define TARGET_LEGITIMATE_CONSTANT_P loongarch_legitimate_constant_p | |
5889 | ||
5890 | #undef TARGET_USE_BLOCKS_FOR_CONSTANT_P | |
5891 | #define TARGET_USE_BLOCKS_FOR_CONSTANT_P hook_bool_mode_const_rtx_true | |
5892 | ||
5893 | #ifdef HAVE_AS_DTPRELWORD | |
5894 | #undef TARGET_ASM_OUTPUT_DWARF_DTPREL | |
5895 | #define TARGET_ASM_OUTPUT_DWARF_DTPREL loongarch_output_dwarf_dtprel | |
5896 | #endif | |
5897 | ||
5898 | #undef TARGET_LEGITIMATE_ADDRESS_P | |
5899 | #define TARGET_LEGITIMATE_ADDRESS_P loongarch_legitimate_address_p | |
5900 | ||
5901 | #undef TARGET_FRAME_POINTER_REQUIRED | |
5902 | #define TARGET_FRAME_POINTER_REQUIRED loongarch_frame_pointer_required | |
5903 | ||
5904 | #undef TARGET_CAN_ELIMINATE | |
5905 | #define TARGET_CAN_ELIMINATE loongarch_can_eliminate | |
5906 | ||
5907 | #undef TARGET_CONDITIONAL_REGISTER_USAGE | |
5908 | #define TARGET_CONDITIONAL_REGISTER_USAGE loongarch_conditional_register_usage | |
5909 | ||
5910 | #undef TARGET_TRAMPOLINE_INIT | |
5911 | #define TARGET_TRAMPOLINE_INIT loongarch_trampoline_init | |
5912 | ||
5913 | #undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV | |
5914 | #define TARGET_ATOMIC_ASSIGN_EXPAND_FENV loongarch_atomic_assign_expand_fenv | |
5915 | ||
5916 | #undef TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS | |
5917 | #define TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS true | |
5918 | ||
5919 | #undef TARGET_SPILL_CLASS | |
5920 | #define TARGET_SPILL_CLASS loongarch_spill_class | |
5921 | ||
5922 | #undef TARGET_HARD_REGNO_NREGS | |
5923 | #define TARGET_HARD_REGNO_NREGS loongarch_hard_regno_nregs | |
5924 | #undef TARGET_HARD_REGNO_MODE_OK | |
5925 | #define TARGET_HARD_REGNO_MODE_OK loongarch_hard_regno_mode_ok | |
5926 | ||
5927 | #undef TARGET_MODES_TIEABLE_P | |
5928 | #define TARGET_MODES_TIEABLE_P loongarch_modes_tieable_p | |
5929 | ||
5930 | #undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS | |
5931 | #define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 2 | |
5932 | ||
5933 | #undef TARGET_CAN_CHANGE_MODE_CLASS | |
5934 | #define TARGET_CAN_CHANGE_MODE_CLASS loongarch_can_change_mode_class | |
5935 | ||
5936 | #undef TARGET_CONSTANT_ALIGNMENT | |
5937 | #define TARGET_CONSTANT_ALIGNMENT loongarch_constant_alignment | |
5938 | ||
5939 | #undef TARGET_STARTING_FRAME_OFFSET | |
5940 | #define TARGET_STARTING_FRAME_OFFSET loongarch_starting_frame_offset | |
5941 | ||
5942 | #undef TARGET_SECONDARY_RELOAD | |
5943 | #define TARGET_SECONDARY_RELOAD loongarch_secondary_reload | |
5944 | ||
5945 | #undef TARGET_HAVE_SPECULATION_SAFE_VALUE | |
5946 | #define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed | |
5947 | ||
5948 | struct gcc_target targetm = TARGET_INITIALIZER; | |
5949 | ||
5950 | #include "gt-loongarch.h" |