1 /* mips16 floating point support code
2 Copyright (C) 1996-2024 Free Software Foundation, Inc.
3 Contributed by Cygnus Support
5 This file is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 3, or (at your option) any
10 This file is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 Under Section 7 of GPL version 3, you are granted additional
16 permissions described in the GCC Runtime Library Exception, version
17 3.1, as published by the Free Software Foundation.
19 You should have received a copy of the GNU General Public License and
20 a copy of the GCC Runtime Library Exception along with this program;
21 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
22 <http://www.gnu.org/licenses/>. */
24 /* An executable stack is *not* required for these functions. */
27 #include "auto-host.h"
29 #if defined(__mips_micromips) || defined(__mips_soft_float) \
30 || __mips_isa_rev >= 6
31 /* Do nothing because this code is only needed when linking
32 against mips16 hard-float objects. Neither micromips code
33 nor soft-float nor MIPS R6 code can be linked against mips16
34 hard-float objects so we do not need these routines when
35 building libgcc for those cases. */
38 #if defined(HAVE_AS_MODULE)
43 #elif __mips_fpr == 64
48 /* This file contains mips16 floating point support functions. These
49 functions are called by mips16 code to handle floating point when
50 -msoft-float is not used. They accept the arguments and return
51 values using the soft-float calling convention, but do the actual
52 operation using the hard floating point instructions. */
54 #if defined _MIPS_SIM && (_MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIO64)
56 /* This file contains 32-bit assembly code. */
59 /* Start a function. */
61 #define STARTFN(NAME) .globl NAME; .ent NAME; NAME:
63 /* Finish a function. */
65 #define ENDFN(NAME) .end NAME
68 The FPR that holds the first floating-point argument.
71 The FPR that holds the second floating-point argument.
74 The FPR that holds a floating-point return value. */
84 /* Set 64-bit register GPR so that its high 32 bits contain HIGH_FPR
85 and so that its low 32 bits contain LOW_FPR. */
86 #define MERGE_GPRf(GPR, HIGH_FPR, LOW_FPR) \
96 /* Move the high 32 bits of GPR to HIGH_FPR and the low 32 bits of
98 #define MERGE_GPRt(GPR, HIGH_FPR, LOW_FPR) \
105 /* Jump to T, and use "OPCODE, OP2" to implement a delayed move. */
106 #define DELAYt(T, OPCODE, OP2) \
113 /* Coprocessor moves are interlocked from the MIPS IV ISA up. */
114 #define DELAYf(T, OPCODE, OP2) DELAYt (T, OPCODE, OP2)
116 /* Use "OPCODE. OP2" and jump to T. */
117 #define DELAYf(T, OPCODE, OP2) OPCODE, OP2; jr T
121 Move the first single-precision floating-point argument between
125 Likewise the first single-precision integer argument.
128 Move the second single-precision floating-point argument between
129 GPRs and FPRs, given that the first argument occupies 4 bytes.
132 Move the second single-precision floating-point argument between
133 GPRs and FPRs, given that the first argument occupies 8 bytes.
136 Move the first double-precision floating-point argument between
140 Likewise the second double-precision floating-point argument.
143 Likewise a single-precision floating-point return value,
147 Likewise a complex single-precision floating-point return value.
150 Likewise a double-precision floating-point return value.
153 Likewise a complex double-precision floating-point return value.
156 Likewise a single-precision integer return value.
158 The D argument is "t" to move to FPRs and "f" to move from FPRs.
159 The return macros may assume that the target of the jump does not
160 use a floating-point register. */
162 #define MOVE_SF_RET(D, T) DELAY##D (T, m##D##c1 $2,$f0)
163 #define MOVE_SI_RET(D, T) DELAY##D (T, m##D##c1 $2,$f0)
165 #if defined(__mips64) && defined(__MIPSEB__)
166 #define MOVE_SC_RET(D, T) MERGE_GPR##D ($2, $f0, $f1); jr T
167 #elif defined(__mips64)
168 /* The high 32 bits of $2 correspond to the second word in memory;
169 i.e. the imaginary part. */
170 #define MOVE_SC_RET(D, T) MERGE_GPR##D ($2, $f1, $f0); jr T
172 #define MOVE_SC_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##c1 $3,$f2)
175 #if defined(__mips64)
176 #define MOVE_SF_BYTE0(D) m##D##c1 $4,$f12
177 #define MOVE_SF_BYTE4(D) m##D##c1 $5,$f13
178 #define MOVE_SF_BYTE8(D) m##D##c1 $5,$f13
180 #define MOVE_SF_BYTE0(D) m##D##c1 $4,$f12
181 #define MOVE_SF_BYTE4(D) m##D##c1 $5,$f14
182 #define MOVE_SF_BYTE8(D) m##D##c1 $6,$f14
184 #define MOVE_SI_BYTE0(D) MOVE_SF_BYTE0(D)
186 #if defined(__mips64)
187 #define MOVE_DF_BYTE0(D) dm##D##c1 $4,$f12
188 #define MOVE_DF_BYTE8(D) dm##D##c1 $5,$f13
189 #define MOVE_DF_RET(D, T) DELAY##D (T, dm##D##c1 $2,$f0)
190 #define MOVE_DC_RET(D, T) dm##D##c1 $3,$f1; MOVE_DF_RET (D, T)
191 #elif __mips_fpr != 32 && __mips_isa_rev >= 2 && defined(__MIPSEB__)
192 #define MOVE_DF_BYTE0(D) m##D##c1 $5,$f12; m##D##hc1 $4,$f12
193 #define MOVE_DF_BYTE8(D) m##D##c1 $7,$f14; m##D##hc1 $6,$f14
194 #define MOVE_DF_RET(D, T) m##D##c1 $3,$f0; DELAY##D (T, m##D##hc1 $2,$f0)
195 #define MOVE_DC_RET(D, T) m##D##c1 $5,$f2; m##D##hc1 $4,$f2; MOVE_DF_RET (D, T)
196 #elif __mips_fpr != 32 && __mips_isa_rev >= 2
197 #define MOVE_DF_BYTE0(D) m##D##c1 $4,$f12; m##D##hc1 $5,$f12
198 #define MOVE_DF_BYTE8(D) m##D##c1 $6,$f14; m##D##hc1 $7,$f14
199 #define MOVE_DF_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##hc1 $3,$f0)
200 #define MOVE_DC_RET(D, T) m##D##c1 $4,$f2; m##D##hc1 $5,$f2; MOVE_DF_RET (D, T)
201 #elif __mips_fpr == 0
202 #define MOVE_DF_BYTE0t sw $4, 0($29); sw $5, 4($29); ldc1 $f12, 0($29)
203 #define MOVE_DF_BYTE0f sdc1 $f12, 0($29); lw $4, 0($29); lw $5, 4($29)
204 #define MOVE_DF_BYTE0(D) MOVE_DF_BYTE0##D
205 #define MOVE_DF_BYTE8t sw $6, 8($29); sw $7, 12($29); ldc1 $f14, 8($29)
206 #define MOVE_DF_BYTE8f sdc1 $f14, 8($29); lw $6, 8($29); lw $7, 12($29)
207 #define MOVE_DF_BYTE8(D) MOVE_DF_BYTE8##D
208 #define MOVE_DF_RETt(T) sw $2, 0($29); sw $3, 4($29); DELAYt (T, ldc1 $f0, 0($29))
209 #define MOVE_DF_RETf(T) sdc1 $f0, 0($29); lw $2, 0($29); DELAYf (T, lw $3, 4($29))
210 #define MOVE_DF_RET(D, T) MOVE_DF_RET##D(T)
211 #define MOVE_DC_RETt(T) sw $4, 8($29); sw $5, 12($29); ldc1 $f2, 8($29); MOVE_DF_RETt(T)
212 #define MOVE_DC_RETf(T) sdc1 $f2, 8($29); lw $4, 8($29); lw $5, 12($29); MOVE_DF_RETf(T)
213 #define MOVE_DC_RET(D, T) MOVE_DF_RET##D(T)
214 #elif defined(__MIPSEB__)
215 /* FPRs are little-endian. */
216 #define MOVE_DF_BYTE0(D) m##D##c1 $4,$f13; m##D##c1 $5,$f12
217 #define MOVE_DF_BYTE8(D) m##D##c1 $6,$f15; m##D##c1 $7,$f14
218 #define MOVE_DF_RET(D, T) m##D##c1 $2,$f1; DELAY##D (T, m##D##c1 $3,$f0)
219 #define MOVE_DC_RET(D, T) m##D##c1 $4,$f3; m##D##c1 $5,$f2; MOVE_DF_RET (D, T)
221 #define MOVE_DF_BYTE0(D) m##D##c1 $4,$f12; m##D##c1 $5,$f13
222 #define MOVE_DF_BYTE8(D) m##D##c1 $6,$f14; m##D##c1 $7,$f15
223 #define MOVE_DF_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##c1 $3,$f1)
224 #define MOVE_DC_RET(D, T) m##D##c1 $4,$f2; m##D##c1 $5,$f3; MOVE_DF_RET (D, T)
227 /* Single-precision math. */
229 /* Define a function NAME that loads two single-precision values,
230 performs FPU operation OPCODE on them, and returns the single-
233 #define OPSF3(NAME, OPCODE) \
237 OPCODE RET,ARG1,ARG2; \
238 MOVE_SF_RET (f, $31); \
242 OPSF3 (__mips16_addsf3, add.s)
245 OPSF3 (__mips16_subsf3, sub.s)
248 OPSF3 (__mips16_mulsf3, mul.s)
251 OPSF3 (__mips16_divsf3, div.s)
254 /* Define a function NAME that loads a single-precision value,
255 performs FPU operation OPCODE on it, and returns the single-
258 #define OPSF2(NAME, OPCODE) \
262 MOVE_SF_RET (f, $31); \
266 OPSF2 (__mips16_negsf2, neg.s)
269 OPSF2 (__mips16_abssf2, abs.s)
272 /* Single-precision comparisons. */
274 /* Define a function NAME that loads two single-precision values,
275 performs floating point comparison OPCODE, and returns TRUE or
276 FALSE depending on the result. */
278 #define CMPSF(NAME, OPCODE, TRUE, FALSE) \
290 /* Like CMPSF, but reverse the comparison operands. */
292 #define REVCMPSF(NAME, OPCODE, TRUE, FALSE) \
305 CMPSF (__mips16_eqsf2, c.eq.s, 0, 1)
308 CMPSF (__mips16_nesf2, c.eq.s, 0, 1)
311 REVCMPSF (__mips16_gtsf2, c.lt.s, 1, 0)
314 REVCMPSF (__mips16_gesf2, c.le.s, 0, -1)
317 CMPSF (__mips16_lesf2, c.le.s, 0, 1)
320 CMPSF (__mips16_ltsf2, c.lt.s, -1, 0)
323 CMPSF(__mips16_unordsf2, c.un.s, 1, 0)
327 /* Single-precision conversions. */
330 STARTFN (__mips16_floatsisf)
334 ENDFN (__mips16_floatsisf)
337 #ifdef L_m16fltunsisf
338 STARTFN (__mips16_floatunsisf)
353 ENDFN (__mips16_floatunsisf)
356 #ifdef L_m16fix_truncsfsi
357 STARTFN (__mips16_fix_truncsfsi)
359 trunc.w.s RET,ARG1,$4
361 ENDFN (__mips16_fix_truncsfsi)
364 #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
366 /* Double-precision math. */
368 /* Define a function NAME that loads two double-precision values,
369 performs FPU operation OPCODE on them, and returns the double-
372 #define OPDF3(NAME, OPCODE) \
376 OPCODE RET,ARG1,ARG2; \
377 MOVE_DF_RET (f, $31); \
381 OPDF3 (__mips16_adddf3, add.d)
384 OPDF3 (__mips16_subdf3, sub.d)
387 OPDF3 (__mips16_muldf3, mul.d)
390 OPDF3 (__mips16_divdf3, div.d)
393 /* Define a function NAME that loads a double-precision value,
394 performs FPU operation OPCODE on it, and returns the double-
397 #define OPDF2(NAME, OPCODE) \
401 MOVE_DF_RET (f, $31); \
405 OPDF2 (__mips16_negdf2, neg.d)
408 OPDF2 (__mips16_absdf2, abs.d)
411 /* Conversions between single and double precision. */
414 STARTFN (__mips16_extendsfdf2)
418 ENDFN (__mips16_extendsfdf2)
422 STARTFN (__mips16_truncdfsf2)
426 ENDFN (__mips16_truncdfsf2)
429 /* Double-precision comparisons. */
431 /* Define a function NAME that loads two double-precision values,
432 performs floating point comparison OPCODE, and returns TRUE or
433 FALSE depending on the result. */
435 #define CMPDF(NAME, OPCODE, TRUE, FALSE) \
447 /* Like CMPDF, but reverse the comparison operands. */
449 #define REVCMPDF(NAME, OPCODE, TRUE, FALSE) \
462 CMPDF (__mips16_eqdf2, c.eq.d, 0, 1)
465 CMPDF (__mips16_nedf2, c.eq.d, 0, 1)
468 REVCMPDF (__mips16_gtdf2, c.lt.d, 1, 0)
471 REVCMPDF (__mips16_gedf2, c.le.d, 0, -1)
474 CMPDF (__mips16_ledf2, c.le.d, 0, 1)
477 CMPDF (__mips16_ltdf2, c.lt.d, -1, 0)
480 CMPDF(__mips16_unorddf2, c.un.d, 1, 0)
483 /* Double-precision conversions. */
486 STARTFN (__mips16_floatsidf)
490 ENDFN (__mips16_floatsidf)
493 #ifdef L_m16fltunsidf
494 STARTFN (__mips16_floatunsidf)
498 li.d ARG1, 4.294967296e+9
500 1: MOVE_DF_RET (f, $31)
501 ENDFN (__mips16_floatunsidf)
504 #ifdef L_m16fix_truncdfsi
505 STARTFN (__mips16_fix_truncdfsi)
507 trunc.w.d RET,ARG1,$4
509 ENDFN (__mips16_fix_truncdfsi)
511 #endif /* !__mips_single_float */
513 /* We don't export stubs from libgcc_s.so and always require static
514 versions to be pulled from libgcc.a as needed because they use $2
515 and possibly $3 as arguments, diverging from the standard SysV ABI,
516 and as such would require severe pessimisation of MIPS16 PLT entries
517 just for this single special case.
519 For compatibility with old binaries that used safe standard MIPS PLT
520 entries and referred to these functions we still export them at
521 version GCC_4.4.0 for run-time loading only. */
524 #define CE_STARTFN(NAME) \
525 STARTFN (NAME##_compat); \
526 .symver NAME##_compat, NAME@GCC_4.4.0
527 #define CE_ENDFN(NAME) ENDFN (NAME##_compat)
529 #define CE_STARTFN(NAME) \
532 #define CE_ENDFN(NAME) ENDFN (NAME)
535 /* Define a function NAME that moves a return value of mode MODE from
538 #define RET_FUNCTION(NAME, MODE) \
540 MOVE_##MODE##_RET (t, $31); \
544 RET_FUNCTION (__mips16_ret_sf, SF)
548 RET_FUNCTION (__mips16_ret_sc, SC)
551 #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
553 RET_FUNCTION (__mips16_ret_df, DF)
557 RET_FUNCTION (__mips16_ret_dc, DC)
559 #endif /* !__mips_single_float */
561 /* STUB_ARGS_X copies the arguments from GPRs to FPRs for argument
562 code X. X is calculated as ARG1 + ARG2 * 4, where ARG1 and ARG2
563 classify the first and second arguments as follows:
565 1: a single-precision argument
566 2: a double-precision argument
567 0: no argument, or not one of the above. */
569 #define STUB_ARGS_0 /* () */
570 #define STUB_ARGS_1 MOVE_SF_BYTE0 (t) /* (sf) */
571 #define STUB_ARGS_5 MOVE_SF_BYTE0 (t); MOVE_SF_BYTE4 (t) /* (sf, sf) */
572 #define STUB_ARGS_9 MOVE_SF_BYTE0 (t); MOVE_DF_BYTE8 (t) /* (sf, df) */
573 #define STUB_ARGS_2 MOVE_DF_BYTE0 (t) /* (df) */
574 #define STUB_ARGS_6 MOVE_DF_BYTE0 (t); MOVE_SF_BYTE8 (t) /* (df, sf) */
575 #define STUB_ARGS_10 MOVE_DF_BYTE0 (t); MOVE_DF_BYTE8 (t) /* (df, df) */
577 /* These functions are used by 16-bit code when calling via a function
578 pointer. They must copy the floating point arguments from the GPRs
579 to FPRs and then call function $2. */
581 #define CALL_STUB_NO_RET(NAME, CODE) \
591 CALL_STUB_NO_RET (__mips16_call_stub_1, 1)
595 CALL_STUB_NO_RET (__mips16_call_stub_5, 5)
598 #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
601 CALL_STUB_NO_RET (__mips16_call_stub_2, 2)
605 CALL_STUB_NO_RET (__mips16_call_stub_6, 6)
609 CALL_STUB_NO_RET (__mips16_call_stub_9, 9)
613 CALL_STUB_NO_RET (__mips16_call_stub_10, 10)
615 #endif /* !__mips_single_float */
617 /* Now we have the same set of functions, except that this time the
618 function being called returns an SFmode, SCmode, DFmode or DCmode
619 value; we need to instantiate a set for each case. The calling
620 function will arrange to preserve $18, so these functions are free
621 to use it to hold the return address.
623 Note that we do not know whether the function we are calling is 16
624 bit or 32 bit. However, it does not matter, because 16-bit
625 functions always return floating point values in both the gp and
626 the fp regs. It would be possible to check whether the function
627 being called is 16 bits, in which case the copy is unnecessary;
628 however, it's faster to always do the copy. */
630 #define CALL_STUB_RET(NAME, CODE, MODE) \
633 /* Create a fake CFA 4 bytes below the stack pointer. */ \
634 .cfi_def_cfa 29,-4; \
635 /* "Save" $sp in itself so we don't use the fake CFA. \
636 This is: DW_CFA_val_expression r29, { DW_OP_reg29 }. */ \
637 .cfi_escape 0x16,29,1,0x6d; \
639 .cfi_register 31,18; \
645 MOVE_##MODE##_RET (f, $18); \
649 /* First, instantiate the single-float set. */
652 CALL_STUB_RET (__mips16_call_stub_sf_0, 0, SF)
656 CALL_STUB_RET (__mips16_call_stub_sf_1, 1, SF)
660 CALL_STUB_RET (__mips16_call_stub_sf_5, 5, SF)
663 #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
665 CALL_STUB_RET (__mips16_call_stub_sf_2, 2, SF)
669 CALL_STUB_RET (__mips16_call_stub_sf_6, 6, SF)
673 CALL_STUB_RET (__mips16_call_stub_sf_9, 9, SF)
677 CALL_STUB_RET (__mips16_call_stub_sf_10, 10, SF)
679 #endif /* !__mips_single_float */
682 /* Now we have the same set of functions again, except that this time
683 the function being called returns an DFmode value. */
685 #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
687 CALL_STUB_RET (__mips16_call_stub_df_0, 0, DF)
691 CALL_STUB_RET (__mips16_call_stub_df_1, 1, DF)
695 CALL_STUB_RET (__mips16_call_stub_df_5, 5, DF)
699 CALL_STUB_RET (__mips16_call_stub_df_2, 2, DF)
703 CALL_STUB_RET (__mips16_call_stub_df_6, 6, DF)
707 CALL_STUB_RET (__mips16_call_stub_df_9, 9, DF)
711 CALL_STUB_RET (__mips16_call_stub_df_10, 10, DF)
713 #endif /* !__mips_single_float */
716 /* Ho hum. Here we have the same set of functions again, this time
717 for when the function being called returns an SCmode value. */
720 CALL_STUB_RET (__mips16_call_stub_sc_0, 0, SC)
724 CALL_STUB_RET (__mips16_call_stub_sc_1, 1, SC)
728 CALL_STUB_RET (__mips16_call_stub_sc_5, 5, SC)
731 #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
733 CALL_STUB_RET (__mips16_call_stub_sc_2, 2, SC)
737 CALL_STUB_RET (__mips16_call_stub_sc_6, 6, SC)
741 CALL_STUB_RET (__mips16_call_stub_sc_9, 9, SC)
745 CALL_STUB_RET (__mips16_call_stub_sc_10, 10, SC)
747 #endif /* !__mips_single_float */
750 /* Finally, another set of functions for DCmode. */
752 #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
754 CALL_STUB_RET (__mips16_call_stub_dc_0, 0, DC)
758 CALL_STUB_RET (__mips16_call_stub_dc_1, 1, DC)
762 CALL_STUB_RET (__mips16_call_stub_dc_5, 5, DC)
766 CALL_STUB_RET (__mips16_call_stub_dc_2, 2, DC)
770 CALL_STUB_RET (__mips16_call_stub_dc_6, 6, DC)
774 CALL_STUB_RET (__mips16_call_stub_dc_9, 9, DC)
778 CALL_STUB_RET (__mips16_call_stub_dc_10, 10, DC)
780 #endif /* !__mips_single_float */
783 #endif /* defined(__mips_micromips) || defined(__mips_soft_float) */