mips16.S revision 302408
1/* mips16 floating point support code
2   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
3   Contributed by Cygnus Support
4
5This file is free software; you can redistribute it and/or modify it
6under the terms of the GNU General Public License as published by the
7Free Software Foundation; either version 2, or (at your option) any
8later version.
9
10In addition to the permissions in the GNU General Public License, the
11Free Software Foundation gives you unlimited permission to link the
12compiled version of this file with other programs, and to distribute
13those programs without any restriction coming from the use of this
14file.  (The General Public License restrictions do apply in other
15respects; for example, they cover modification of the file, and
16distribution when not linked into another program.)
17
18This file is distributed in the hope that it will be useful, but
19WITHOUT ANY WARRANTY; without even the implied warranty of
20MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with this program; see the file COPYING.  If not, write to
25the Free Software Foundation, 51 Franklin Street, Fifth Floor,
26Boston, MA 02110-1301, USA.  */
27
28/* As a special exception, if you link this library with other files,
29   some of which are compiled with GCC, to produce an executable,
30   this library does not by itself cause the resulting executable
31   to be covered by the GNU General Public License.
32   This exception does not however invalidate any other reasons why
33   the executable file might be covered by the GNU General Public License.  */
34
35/* This file contains mips16 floating point support functions.  These
36   functions are called by mips16 code to handle floating point when
37   -msoft-float is not used.  They accept the arguments and return
38   values using the soft-float calling convention, but do the actual
39   operation using the hard floating point instructions.  */
40
41/* This file contains 32 bit assembly code.  */
42	.set nomips16
43
44/* Start a function.  */
45
46#define STARTFN(NAME) .globl NAME; .ent NAME; NAME:
47
48/* Finish a function.  */
49
50#define ENDFN(NAME) .end NAME
51
52/* Single precision math.  */
53
54/* This macro defines a function which loads two single precision
55   values, performs an operation, and returns the single precision
56   result.  */
57
58#define SFOP(NAME, OPCODE)	\
59STARTFN (NAME);			\
60	.set	noreorder;	\
61	mtc1	$4,$f0;		\
62	mtc1	$5,$f2;		\
63	nop;			\
64	OPCODE	$f0,$f0,$f2;	\
65	mfc1	$2,$f0;		\
66	j	$31;		\
67	nop;			\
68	.set	reorder;	\
69	ENDFN (NAME)
70
71#ifdef L_m16addsf3
72SFOP(__mips16_addsf3, add.s)
73#endif
74#ifdef L_m16subsf3
75SFOP(__mips16_subsf3, sub.s)
76#endif
77#ifdef L_m16mulsf3
78SFOP(__mips16_mulsf3, mul.s)
79#endif
80#ifdef L_m16divsf3
81SFOP(__mips16_divsf3, div.s)
82#endif
83
84#define SFOP2(NAME, OPCODE)	\
85STARTFN (NAME);			\
86	.set	noreorder;	\
87	mtc1	$4,$f0;		\
88	nop;			\
89	OPCODE	$f0,$f0;	\
90	mfc1	$2,$f0;		\
91	j	$31;		\
92	nop;			\
93	.set	reorder;	\
94	ENDFN (NAME)
95
96#ifdef L_m16negsf2
97SFOP2(__mips16_negsf2, neg.s)
98#endif
99#ifdef L_m16abssf2
100SFOP2(__mips16_abssf2, abs.s)
101#endif
102
103/* Single precision comparisons.  */
104
105/* This macro defines a function which loads two single precision
106   values, performs a floating point comparison, and returns the
107   specified values according to whether the comparison is true or
108   false.  */
109
110#define SFCMP(NAME, OPCODE, TRUE, FALSE)	\
111STARTFN (NAME);					\
112	mtc1	$4,$f0;				\
113	mtc1	$5,$f2;				\
114	OPCODE	$f0,$f2;			\
115	li	$2,TRUE;			\
116	bc1t	1f;				\
117	li	$2,FALSE;			\
1181:;						\
119	j	$31;				\
120	ENDFN (NAME)
121
122/* This macro is like SFCMP, but it reverses the comparison.  */
123
124#define SFREVCMP(NAME, OPCODE, TRUE, FALSE)	\
125STARTFN (NAME);					\
126	mtc1	$4,$f0;				\
127	mtc1	$5,$f2;				\
128	OPCODE	$f2,$f0;			\
129	li	$2,TRUE;			\
130	bc1t	1f;				\
131	li	$2,FALSE;			\
1321:;						\
133	j	$31;				\
134	ENDFN (NAME)
135
136#ifdef L_m16eqsf2
137SFCMP(__mips16_eqsf2, c.eq.s, 0, 1)
138#endif
139#ifdef L_m16nesf2
140SFCMP(__mips16_nesf2, c.eq.s, 0, 1)
141#endif
142#ifdef L_m16gtsf2
143SFREVCMP(__mips16_gtsf2, c.lt.s, 1, 0)
144#endif
145#ifdef L_m16gesf2
146SFREVCMP(__mips16_gesf2, c.le.s, 0, -1)
147#endif
148#ifdef L_m16lesf2
149SFCMP(__mips16_lesf2, c.le.s, 0, 1)
150#endif
151#ifdef L_m16ltsf2
152SFCMP(__mips16_ltsf2, c.lt.s, -1, 0)
153#endif
154
155/* Single precision conversions.  */
156
157#ifdef L_m16fltsisf
158STARTFN (__mips16_floatsisf)
159	.set	noreorder
160	mtc1	$4,$f0
161	nop
162	cvt.s.w	$f0,$f0
163	mfc1	$2,$f0
164	j	$31
165	nop
166	.set	reorder
167	ENDFN (__mips16_floatsisf)
168#endif
169
170#ifdef L_m16fix_truncsfsi
171STARTFN (__mips16_fix_truncsfsi)
172	.set	noreorder
173	mtc1	$4,$f0
174	nop
175	trunc.w.s $f0,$f0,$4
176	mfc1	$2,$f0
177	j	$31
178	nop
179	.set	reorder
180	ENDFN (__mips16_fix_truncsfsi)
181#endif
182
183#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
184
185/* The double precision operations.  We need to use different code
186   based on the preprocessor symbol __mips64, because the way in which
187   double precision values will change.  Without __mips64, the value
188   is passed in two 32 bit registers.  With __mips64, the value is
189   passed in a single 64 bit register.  */
190
191/* Load the first double precision operand.  */
192
193#if defined(__mips64)
194#define LDDBL1 dmtc1 $4,$f12
195#elif defined(__mipsfp64)
196#define LDDBL1 sw $4,0($29); sw $5,4($29); l.d $f12,0($29)
197#elif defined(__MIPSEB__)
198#define LDDBL1 mtc1 $4,$f13; mtc1 $5,$f12
199#else
200#define LDDBL1 mtc1 $4,$f12; mtc1 $5,$f13
201#endif
202
203/* Load the second double precision operand.  */
204
205#if defined(__mips64)
206/* XXX this should be $6 for Algo arg passing model */
207#define LDDBL2 dmtc1 $5,$f14
208#elif defined(__mipsfp64)
209#define LDDBL2 sw $6,8($29); sw $7,12($29); l.d $f14,8($29)
210#elif defined(__MIPSEB__)
211#define LDDBL2 mtc1 $6,$f15; mtc1 $7,$f14
212#else
213#define LDDBL2 mtc1 $6,$f14; mtc1 $7,$f15
214#endif
215
216/* Move the double precision return value to the right place.  */
217
218#if defined(__mips64)
219#define RETDBL dmfc1 $2,$f0
220#elif defined(__mipsfp64)
221#define RETDBL s.d $f0,0($29); lw $2,0($29); lw $3,4($29)
222#elif defined(__MIPSEB__)
223#define RETDBL mfc1 $2,$f1; mfc1 $3,$f0
224#else
225#define RETDBL mfc1 $2,$f0; mfc1 $3,$f1
226#endif
227
228/* Double precision math.  */
229
230/* This macro defines a function which loads two double precision
231   values, performs an operation, and returns the double precision
232   result.  */
233
234#define DFOP(NAME, OPCODE)	\
235STARTFN (NAME);			\
236	.set	noreorder;	\
237	LDDBL1;			\
238	LDDBL2;			\
239	nop;			\
240	OPCODE	$f0,$f12,$f14;	\
241	RETDBL;			\
242	j	$31;		\
243	nop;			\
244	.set	reorder;	\
245	ENDFN (NAME)
246
247#ifdef L_m16adddf3
248DFOP(__mips16_adddf3, add.d)
249#endif
250#ifdef L_m16subdf3
251DFOP(__mips16_subdf3, sub.d)
252#endif
253#ifdef L_m16muldf3
254DFOP(__mips16_muldf3, mul.d)
255#endif
256#ifdef L_m16divdf3
257DFOP(__mips16_divdf3, div.d)
258#endif
259
260#define DFOP2(NAME, OPCODE)	\
261STARTFN (NAME);			\
262	.set	noreorder;	\
263	LDDBL1;			\
264	nop;			\
265	OPCODE	$f0,$f12;	\
266	RETDBL;			\
267	j	$31;		\
268	nop;			\
269	.set	reorder;	\
270	ENDFN (NAME)
271
272#ifdef L_m16negdf2
273DFOP2(__mips16_negdf2, neg.d)
274#endif
275#ifdef L_m16absdf2
276DFOP2(__mips16_absdf2, abs.d)
277#endif
278
279
280/* Conversions between single and double precision.  */
281
282#ifdef L_m16extsfdf2
283STARTFN (__mips16_extendsfdf2)
284	.set	noreorder
285	mtc1	$4,$f12
286	nop
287	cvt.d.s	$f0,$f12
288	RETDBL
289	j	$31
290	nop
291	.set	reorder
292	ENDFN (__mips16_extendsfdf2)
293#endif
294
295#ifdef L_m16trdfsf2
296STARTFN (__mips16_truncdfsf2)
297	.set	noreorder
298	LDDBL1
299	nop
300	cvt.s.d	$f0,$f12
301	mfc1	$2,$f0
302	j	$31
303	nop
304	.set	reorder
305	ENDFN (__mips16_truncdfsf2)
306#endif
307
308/* Double precision comparisons.  */
309
310/* This macro defines a function which loads two double precision
311   values, performs a floating point comparison, and returns the
312   specified values according to whether the comparison is true or
313   false.  */
314
315#define DFCMP(NAME, OPCODE, TRUE, FALSE)	\
316STARTFN (NAME);					\
317	LDDBL1;					\
318	LDDBL2;					\
319	OPCODE	$f12,$f14;			\
320	li	$2,TRUE;			\
321	bc1t	1f;				\
322	li	$2,FALSE;			\
3231:;						\
324	j	$31;				\
325	ENDFN (NAME)
326
327/* This macro is like DFCMP, but it reverses the comparison.  */
328
329#define DFREVCMP(NAME, OPCODE, TRUE, FALSE)	\
330STARTFN (NAME);					\
331	LDDBL1;					\
332	LDDBL2;					\
333	OPCODE	$f14,$f12;			\
334	li	$2,TRUE;			\
335	bc1t	1f;				\
336	li	$2,FALSE;			\
3371:;						\
338	j	$31;				\
339	ENDFN (NAME)
340
341#ifdef L_m16eqdf2
342DFCMP(__mips16_eqdf2, c.eq.d, 0, 1)
343#endif
344#ifdef L_m16nedf2
345DFCMP(__mips16_nedf2, c.eq.d, 0, 1)
346#endif
347#ifdef L_m16gtdf2
348DFREVCMP(__mips16_gtdf2, c.lt.d, 1, 0)
349#endif
350#ifdef L_m16gedf2
351DFREVCMP(__mips16_gedf2, c.le.d, 0, -1)
352#endif
353#ifdef L_m16ledf2
354DFCMP(__mips16_ledf2, c.le.d, 0, 1)
355#endif
356#ifdef L_m16ltdf2
357DFCMP(__mips16_ltdf2, c.lt.d, -1, 0)
358#endif
359
360/* Double precision conversions.  */
361
362#ifdef L_m16fltsidf
363STARTFN (__mips16_floatsidf)
364	.set	noreorder
365	mtc1	$4,$f12
366	nop
367	cvt.d.w	$f0,$f12
368	RETDBL
369	j	$31
370	nop
371	.set	reorder
372	ENDFN (__mips16_floatsidf)
373#endif
374
375#ifdef L_m16fix_truncdfsi
376STARTFN (__mips16_fix_truncdfsi)
377	.set	noreorder
378	LDDBL1
379	nop
380	trunc.w.d $f0,$f12,$4
381	mfc1	$2,$f0
382	j	$31
383	nop
384	.set	reorder
385	ENDFN (__mips16_fix_truncdfsi)
386#endif
387#endif /* !__mips_single_float */
388
389/* These functions are used to return floating point values from
390   mips16 functions.  In this case we can put mtc1 in a jump delay slot,
391   because we know that the next instruction will not refer to a floating
392   point register.  */
393
394#ifdef L_m16retsf
395STARTFN (__mips16_ret_sf)
396	.set	noreorder
397	j	$31
398	mtc1	$2,$f0
399	.set	reorder
400	ENDFN (__mips16_ret_sf)
401#endif
402
403#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
404#ifdef L_m16retdf
405STARTFN (__mips16_ret_df)
406	.set	noreorder
407#if defined(__mips64)
408	j	$31
409	dmtc1	$2,$f0
410#elif defined(__mipsfp64)
411	sw	$2,0($29)
412	sw	$3,4($29)
413	l.d	$f0,0($29)
414#elif defined(__MIPSEB__)
415	mtc1	$2,$f1
416	j	$31
417	mtc1	$3,$f0
418#else
419	mtc1	$2,$f0
420	j	$31
421	mtc1	$3,$f1
422#endif
423	.set	reorder
424	ENDFN (__mips16_ret_df)
425#endif
426#endif /* !__mips_single_float */
427
428/* These functions are used by 16 bit code when calling via a function
429   pointer.  They must copy the floating point arguments from the gp
430   regs into the fp regs.  The function to call will be in $2.  The
431   exact set of floating point arguments to copy is encoded in the
432   function name; the final number is an fp_code, as described in
433   mips.h in the comment about CUMULATIVE_ARGS.  */
434
435#ifdef L_m16stub1
436/* (float) */
437STARTFN (__mips16_call_stub_1)
438	.set	noreorder
439	mtc1	$4,$f12
440	j	$2
441	nop
442	.set	reorder
443	ENDFN (__mips16_call_stub_1)
444#endif
445
446#ifdef L_m16stub5
447/* (float, float) */
448STARTFN (__mips16_call_stub_5)
449	.set	noreorder
450	mtc1	$4,$f12
451	mtc1	$5,$f14
452	j	$2
453	nop
454	.set	reorder
455	ENDFN (__mips16_call_stub_5)
456#endif
457
458#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
459
460#ifdef L_m16stub2
461/* (double) */
462STARTFN (__mips16_call_stub_2)
463	.set	noreorder
464	LDDBL1
465	j	$2
466	nop
467	.set	reorder
468	ENDFN (__mips16_call_stub_2)
469#endif
470
471#ifdef L_m16stub6
472/* (double, float) */
473STARTFN (__mips16_call_stub_6)
474	.set	noreorder
475	LDDBL1
476	mtc1	$6,$f14
477	j	$2
478	nop
479	.set	reorder
480	ENDFN (__mips16_call_stub_6)
481#endif
482
483#ifdef L_m16stub9
484/* (float, double) */
485STARTFN (__mips16_call_stub_9)
486	.set	noreorder
487	mtc1	$4,$f12
488	LDDBL2
489	j	$2
490	nop
491	.set	reorder
492	ENDFN (__mips16_call_stub_9)
493#endif
494
495#ifdef L_m16stub10
496/* (double, double) */
497STARTFN (__mips16_call_stub_10)
498	.set	noreorder
499	LDDBL1
500	LDDBL2
501	j	$2
502	nop
503	.set	reorder
504	ENDFN (__mips16_call_stub_10)
505#endif
506#endif /* !__mips_single_float */
507
508/* Now we have the same set of functions, except that this time the
509   function being called returns an SFmode value.  The calling
510   function will arrange to preserve $18, so these functions are free
511   to use it to hold the return address.
512
513   Note that we do not know whether the function we are calling is 16
514   bit or 32 bit.  However, it does not matter, because 16 bit
515   functions always return floating point values in both the gp and
516   the fp regs.  It would be possible to check whether the function
517   being called is 16 bits, in which case the copy is unnecessary;
518   however, it's faster to always do the copy.  */
519
520#ifdef L_m16stubsf0
521/* () */
522STARTFN (__mips16_call_stub_sf_0)
523	.set	noreorder
524	move	$18,$31
525	jal	$2
526	nop
527	mfc1	$2,$f0
528	j	$18
529	nop
530	.set	reorder
531	ENDFN (__mips16_call_stub_sf_0)
532#endif
533
534#ifdef L_m16stubsf1
535/* (float) */
536STARTFN (__mips16_call_stub_sf_1)
537	.set	noreorder
538	mtc1	$4,$f12
539	move	$18,$31
540	jal	$2
541	nop
542	mfc1	$2,$f0
543	j	$18
544	nop
545	.set	reorder
546	ENDFN (__mips16_call_stub_sf_1)
547#endif
548
549#ifdef L_m16stubsf5
550/* (float, float) */
551STARTFN (__mips16_call_stub_sf_5)
552	.set	noreorder
553	mtc1	$4,$f12
554	mtc1	$5,$f14
555	move	$18,$31
556	jal	$2
557	nop
558	mfc1	$2,$f0
559	j	$18
560	nop
561	.set	reorder
562	ENDFN (__mips16_call_stub_sf_5)
563#endif
564
565#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
566#ifdef L_m16stubsf2
567/* (double) */
568STARTFN (__mips16_call_stub_sf_2)
569	.set	noreorder
570	LDDBL1
571	move	$18,$31
572	jal	$2
573	nop
574	mfc1	$2,$f0
575	j	$18
576	nop
577	.set	reorder
578	ENDFN (__mips16_call_stub_sf_2)
579#endif
580
581#ifdef L_m16stubsf6
582/* (double, float) */
583STARTFN (__mips16_call_stub_sf_6)
584	.set	noreorder
585	LDDBL1
586	mtc1	$6,$f14
587	move	$18,$31
588	jal	$2
589	nop
590	mfc1	$2,$f0
591	j	$18
592	nop
593	.set	reorder
594	ENDFN (__mips16_call_stub_sf_6)
595#endif
596
597#ifdef L_m16stubsf9
598/* (float, double) */
599STARTFN (__mips16_call_stub_sf_9)
600	.set	noreorder
601	mtc1	$4,$f12
602	LDDBL2
603	move	$18,$31
604	jal	$2
605	nop
606	mfc1	$2,$f0
607	j	$18
608	nop
609	.set	reorder
610	ENDFN (__mips16_call_stub_sf_9)
611#endif
612
613#ifdef L_m16stubsf10
614/* (double, double) */
615STARTFN (__mips16_call_stub_sf_10)
616	.set	noreorder
617	LDDBL1
618	LDDBL2
619	move	$18,$31
620	jal	$2
621	nop
622	mfc1	$2,$f0
623	j	$18
624	nop
625	.set	reorder
626	ENDFN (__mips16_call_stub_sf_10)
627#endif
628
629/* Now we have the same set of functions again, except that this time
630   the function being called returns an DFmode value.  */
631
632#ifdef L_m16stubdf0
633/* () */
634STARTFN (__mips16_call_stub_df_0)
635	.set	noreorder
636	move	$18,$31
637	jal	$2
638	nop
639	RETDBL
640	j	$18
641	nop
642	.set	reorder
643	ENDFN (__mips16_call_stub_df_0)
644#endif
645
646#ifdef L_m16stubdf1
647/* (float) */
648STARTFN (__mips16_call_stub_df_1)
649	.set	noreorder
650	mtc1	$4,$f12
651	move	$18,$31
652	jal	$2
653	nop
654	RETDBL
655	j	$18
656	nop
657	.set	reorder
658	ENDFN (__mips16_call_stub_df_1)
659#endif
660
661#ifdef L_m16stubdf2
662/* (double) */
663STARTFN (__mips16_call_stub_df_2)
664	.set	noreorder
665	LDDBL1
666	move	$18,$31
667	jal	$2
668	nop
669	RETDBL
670	j	$18
671	nop
672	.set	reorder
673	ENDFN (__mips16_call_stub_df_2)
674#endif
675
676#ifdef L_m16stubdf5
677/* (float, float) */
678STARTFN (__mips16_call_stub_df_5)
679	.set	noreorder
680	mtc1	$4,$f12
681	mtc1	$5,$f14
682	move	$18,$31
683	jal	$2
684	nop
685	RETDBL
686	j	$18
687	nop
688	.set	reorder
689	ENDFN (__mips16_call_stub_df_5)
690#endif
691
692#ifdef L_m16stubdf6
693/* (double, float) */
694STARTFN (__mips16_call_stub_df_6)
695	.set	noreorder
696	LDDBL1
697	mtc1	$6,$f14
698	move	$18,$31
699	jal	$2
700	nop
701	RETDBL
702	j	$18
703	nop
704	.set	reorder
705	ENDFN (__mips16_call_stub_df_6)
706#endif
707
708#ifdef L_m16stubdf9
709/* (float, double) */
710STARTFN (__mips16_call_stub_df_9)
711	.set	noreorder
712	mtc1	$4,$f12
713	LDDBL2
714	move	$18,$31
715	jal	$2
716	nop
717	RETDBL
718	j	$18
719	nop
720	.set	reorder
721	ENDFN (__mips16_call_stub_df_9)
722#endif
723
724#ifdef L_m16stubdf10
725/* (double, double) */
726STARTFN (__mips16_call_stub_df_10)
727	.set	noreorder
728	LDDBL1
729	LDDBL2
730	move	$18,$31
731	jal	$2
732	nop
733	RETDBL
734	j	$18
735	nop
736	.set	reorder
737	ENDFN (__mips16_call_stub_df_10)
738#endif
739#endif /* !__mips_single_float */
740