1/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include "pico/asm_helper.S"
8#include "pico/runtime_init.h"
9#include "pico/bootrom/sf_table.h"
10#include "hardware/divider_helper.S"
11
12PICO_RUNTIME_INIT_FUNC_RUNTIME(__aeabi_float_init, PICO_RUNTIME_INIT_AEABI_FLOAT)
13
14pico_default_asm_setup
15
16.macro float_section name
17#if PICO_FLOAT_IN_RAM
18.section RAM_SECTION_NAME(\name), "ax"
19#else
20.section SECTION_NAME(\name), "ax"
21#endif
22.endm
23
24.macro float_wrapper_section func
25float_section WRAPPER_FUNC_NAME(\func)
26.endm
27
28.macro _float_wrapper_func x
29    wrapper_func \x
30.endm
31
32.macro wrapper_func_f1 x
33   _float_wrapper_func \x
34#if PICO_FLOAT_PROPAGATE_NANS
35    mov ip, lr
36    bl __check_nan_f1
37    mov lr, ip
38#endif
39.endm
40
41.macro wrapper_func_f2 x
42   _float_wrapper_func \x
43#if PICO_FLOAT_PROPAGATE_NANS
44    mov ip, lr
45    bl __check_nan_f2
46    mov lr, ip
47#endif
48.endm
49
50.section .text
51
52#if PICO_FLOAT_PROPAGATE_NANS
53.thumb_func
54__check_nan_f1:
55   movs r3, #1
56   lsls r3, #24
57   lsls r2, r0, #1
58   adds r2, r3
59   bhi 1f
60   bx lr
611:
62   bx ip
63
64.thumb_func
65__check_nan_f2:
66   movs r3, #1
67   lsls r3, #24
68   lsls r2, r0, #1
69   adds r2, r3
70   bhi 1f
71   lsls r2, r1, #1
72   adds r2, r3
73   bhi 2f
74   bx lr
752:
76   mov r0, r1
771:
78   bx ip
79#endif
80
81.macro table_tail_call SF_TABLE_OFFSET
82#if PICO_FLOAT_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
83#ifndef NDEBUG
84    movs r3, #0
85    mov ip, r3
86#endif
87#endif
88    ldr r3, =sf_table
89    ldr r3, [r3, #\SF_TABLE_OFFSET]
90    bx r3
91.endm
92
93.macro shimmable_table_tail_call SF_TABLE_OFFSET shim
94    ldr r3, =sf_table
95    ldr r3, [r3, #\SF_TABLE_OFFSET]
96#if PICO_FLOAT_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
97    mov ip, pc
98#endif
99    bx r3
100#if PICO_FLOAT_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
101.byte \SF_TABLE_OFFSET, 0xdf
102.word \shim
103#endif
104.endm
105
106
107// note generally each function is in a separate section unless there is fall thru or branching between them
108// note fadd, fsub, fmul, fdiv are so tiny and just defer to rom so are lumped together so they can share constant pool
109
110// note functions are word aligned except where they are an odd number of linear instructions
111
112// float FUNC_NAME(__aeabi_fadd)(float, float)         single-precision addition
113float_wrapper_section __aeabi_farithmetic
114// float FUNC_NAME(__aeabi_frsub)(float x, float y)    single-precision reverse subtraction, y - x
115
116// frsub first because it is the only one that needs alignment
117.align 2
118wrapper_func __aeabi_frsub
119    eors r0, r1
120    eors r1, r0
121    eors r0, r1
122    // fall thru
123
124// float FUNC_NAME(__aeabi_fsub)(float x, float y)     single-precision subtraction, x - y
125wrapper_func_f2 __aeabi_fsub
126#if PICO_FLOAT_PROPAGATE_NANS
127    // we want to return nan for inf-inf or -inf - -inf, but without too much upfront cost
128    mov r2, r0
129    eors r2, r1
130    bmi 1f // different signs
131    push {r0, r1, lr}
132    bl 1f
133    b fdiv_fsub_nan_helper
1341:
135#endif
136    table_tail_call SF_TABLE_FSUB
137
138wrapper_func_f2 __aeabi_fadd
139    table_tail_call SF_TABLE_FADD
140
141// float FUNC_NAME(__aeabi_fdiv)(float n, float d)     single-precision division, n / d
142wrapper_func_f2 __aeabi_fdiv
143#if PICO_FLOAT_PROPAGATE_NANS
144    push {r0, r1, lr}
145    bl 1f
146    b fdiv_fsub_nan_helper
1471:
148#endif
149#if !PICO_DIVIDER_DISABLE_INTERRUPTS
150    // to support IRQ usage (or context switch) we must save/restore divider state around call if state is dirty
151    ldr r2, =(SIO_BASE)
152    ldr r3, [r2, #SIO_DIV_CSR_OFFSET]
153    lsrs r3, #SIO_DIV_CSR_DIRTY_SHIFT_FOR_CARRY
154    bcs fdiv_save_state
155#else
156    // to avoid worrying about IRQs (or context switches), simply disable interrupts around call
157    push {r4, lr}
158    mrs r4, PRIMASK
159    cpsid i
160    bl fdiv_shim_call
161    msr PRIMASK, r4
162    pop {r4, pc}
163#endif
164fdiv_shim_call:
165    table_tail_call SF_TABLE_FDIV
166#if !PICO_DIVIDER_DISABLE_INTERRUPTS
167fdiv_save_state:
168    save_div_state_and_lr
169    bl fdiv_shim_call
170    ldr r2, =(SIO_BASE)
171    restore_div_state_and_return
172#endif
173
174fdiv_fsub_nan_helper:
175#if PICO_FLOAT_PROPAGATE_NANS
176    pop {r1, r2}
177
178    // check for infinite op infinite (or rather check for infinite result with both
179    // operands being infinite)
180    lsls r3, r0, #1
181    asrs r3, r3, #24
182    adds r3, #1
183    beq 2f
184    pop {pc}
1852:
186    lsls r1, #1
187    asrs r1, r1, #24
188    lsls r2, #1
189    asrs r2, r2, #24
190    ands r1, r2
191    adds r1, #1
192    bne 3f
193    // infinite to nan
194    movs r1, #1
195    lsls r1, #22
196    orrs r0, r1
1973:
198    pop {pc}
199#endif
200
201// float FUNC_NAME(__aeabi_fmul)(float, float)         single-precision multiplication
202wrapper_func_f2 __aeabi_fmul
203#if PICO_FLOAT_PROPAGATE_NANS
204    push {r0, r1, lr}
205    bl 1f
206    pop {r1, r2}
207
208    // check for multiplication of infinite by zero (or rather check for infinite result with either
209    // operand 0)
210    lsls r3, r0, #1
211    asrs r3, r3, #24
212    adds r3, #1
213    beq 2f
214    pop {pc}
2152:
216    ands r1, r2
217    bne 3f
218    // infinite to nan
219    movs r1, #1
220    lsls r1, #22
221    orrs r0, r1
2223:
223    pop {pc}
2241:
225#endif
226    table_tail_call SF_TABLE_FMUL
227
228// void FUNC_NAME(__aeabi_cfrcmple)(float, float)         reversed 3-way (<, =, ?>) compare [1], result in PSR ZC flags
229float_wrapper_section __aeabi_cfcmple
230.align 2
231wrapper_func __aeabi_cfrcmple
232    push {r0-r2, lr}
233    eors r0, r1
234    eors r1, r0
235    eors r0, r1
236    b __aeabi_cfcmple_guts
237
238// NOTE these share an implementation as we have no excepting NaNs.
239// void FUNC_NAME(__aeabi_cfcmple)(float, float)         3-way (<, =, ?>) compare [1], result in PSR ZC flags
240// void FUNC_NAME(__aeabi_cfcmpeq)(float, float)         non-excepting equality comparison [1], result in PSR ZC flags
241.align 2
242wrapper_func __aeabi_cfcmple
243wrapper_func __aeabi_cfcmpeq
244    push {r0-r2, lr}
245
246__aeabi_cfcmple_guts:
247    lsls r2,r0,#1
248    lsrs r2,#24
249    beq 1f
250    cmp r2,#0xff
251    bne 2f
252    lsls r2, r0, #9
253    bhi 3f
2541:
255    lsrs r0,#23     @ clear mantissa if denormal or infinite
256    lsls r0,#23
2572:
258    lsls r2,r1,#1
259    lsrs r2,#24
260    beq 1f
261    cmp r2,#0xff
262    bne 2f
263    lsls r2, r1, #9
264    bhi 3f
2651:
266    lsrs r1,#23     @ clear mantissa if denormal or infinite
267    lsls r1,#23
2682:
269    movs r2,#1      @ initialise result
270    eors r1,r0
271    bmi 2f          @ opposite signs? then can proceed on basis of sign of x
272    eors r1,r0      @ restore y
273    bpl 1f
274    cmp r1,r0
275    pop {r0-r2, pc}
2761:
277    cmp r0,r1
278    pop {r0-r2, pc}
2792:
280    orrs r1, r0     @ handle 0/-0
281    adds r1, r1     @ note this always sets C
282    beq 3f
283    mvns r0, r0     @ carry inverse of r0 sign
284    adds r0, r0
2853:
286    pop {r0-r2, pc}
287
288
289// int FUNC_NAME(__aeabi_fcmpeq)(float, float)         result (1, 0) denotes (=, ?<>) [2], use for C == and !=
290float_wrapper_section __aeabi_fcmpeq
291.align 2
292wrapper_func __aeabi_fcmpeq
293    push {lr}
294    bl __aeabi_cfcmpeq
295    beq 1f
296    movs r0, #0
297    pop {pc}
2981:
299    movs r0, #1
300    pop {pc}
301
302// int FUNC_NAME(__aeabi_fcmplt)(float, float)         result (1, 0) denotes (<, ?>=) [2], use for C <
303float_wrapper_section __aeabi_fcmplt
304.align 2
305wrapper_func __aeabi_fcmplt
306    push {lr}
307    bl __aeabi_cfcmple
308    sbcs r0, r0
309    pop {pc}
310
311// int FUNC_NAME(__aeabi_fcmple)(float, float)         result (1, 0) denotes (<=, ?>) [2], use for C <=
312float_wrapper_section __aeabi_fcmple
313.align 2
314wrapper_func __aeabi_fcmple
315    push {lr}
316    bl __aeabi_cfcmple
317    bls 1f
318    movs r0, #0
319    pop {pc}
3201:
321    movs r0, #1
322    pop {pc}
323
324// int FUNC_NAME(__aeabi_fcmpge)(float, float)         result (1, 0) denotes (>=, ?<) [2], use for C >=
325float_wrapper_section __aeabi_fcmpge
326.align 2
327wrapper_func __aeabi_fcmpge
328    push {lr}
329    // because of NaNs it is better to reverse the args than the result
330    bl __aeabi_cfrcmple
331    bls 1f
332    movs r0, #0
333    pop {pc}
3341:
335    movs r0, #1
336    pop {pc}
337
338// int FUNC_NAME(__aeabi_fcmpgt)(float, float)         result (1, 0) denotes (>, ?<=) [2], use for C >
339float_wrapper_section __aeabi_fcmpgt
340wrapper_func __aeabi_fcmpgt
341    push {lr}
342    // because of NaNs it is better to reverse the args than the result
343    bl __aeabi_cfrcmple
344    sbcs r0, r0
345    pop {pc}
346
347// int FUNC_NAME(__aeabi_fcmpun)(float, float)         result (1, 0) denotes (?, <=>) [2], use for C99 isunordered()
348float_wrapper_section __aeabi_fcmpun
349wrapper_func __aeabi_fcmpun
350   movs r3, #1
351   lsls r3, #24
352   lsls r2, r0, #1
353   adds r2, r3
354   bhi 1f
355   lsls r2, r1, #1
356   adds r2, r3
357   bhi 1f
358   movs r0, #0
359   bx lr
3601:
361   movs r0, #1
362   bx lr
363
364
365// float FUNC_NAME(__aeabi_ui2f)(unsigned)             unsigned to float (single precision) conversion
366float_wrapper_section __aeabi_ui2f
367wrapper_func __aeabi_ui2f
368regular_func uint2float
369        subs r1, r1
370        cmp r0, #0
371        bne __aeabi_i2f_main
372        mov r0, r1
373        bx lr
374
375float_wrapper_section __aeabi_i2f
376// float FUNC_NAME(__aeabi_i2f)(int)                     integer to float (single precision) conversion
377wrapper_func __aeabi_i2f
378regular_func int2float
379        lsrs r1, r0, #31
380        lsls r1, #31
381        bpl 1f
382        negs r0, r0
3831:
384        cmp r0, #0
385        beq 7f
386__aeabi_i2f_main:
387
388        mov ip, lr
389        push {r0, r1}
390        ldr r3, =sf_clz_func
391        ldr r3, [r3]
392        blx r3
393        pop {r1, r2}
394        lsls r1, r0
395        subs r0, #158
396        negs r0, r0
397
398        adds r1,#0x80  @ rounding
399        bcs 5f         @ tripped carry? then have leading 1 in C as required (and result is even so can ignore sticky bits)
400
401        lsls r3,r1,#24 @ check bottom 8 bits of r1
402        beq 6f         @ in rounding-tie case?
403        lsls r1,#1     @ remove leading 1
4043:
405        lsrs r1,#9     @ align mantissa
406        lsls r0,#23    @ align exponent
407        orrs r0,r2     @ assemble exponent and mantissa
4084:
409        orrs r0,r1     @ apply sign
4101:
411        bx ip
4125:
413        adds r0,#1     @ correct exponent offset
414        b 3b
4156:
416        lsrs r1,#9     @ ensure even result
417        lsls r1,#10
418        b 3b
4197:
420        bx lr
421
422
423// int FUNC_NAME(__aeabi_f2iz)(float)                     float (single precision) to integer C-style conversion [3]
424float_wrapper_section __aeabi_f2iz
425wrapper_func __aeabi_f2iz
426regular_func float2int_z
427    lsls r1, r0, #1
428    lsrs r2, r1, #24
429    movs r3, #0x80
430    lsls r3, #24
431    cmp r2, #126
432    ble 1f
433    subs r2, #158
434    bge 2f
435    asrs r1, r0, #31
436    lsls r0, #9
437    lsrs r0, #1
438    orrs r0, r3
439    negs r2, r2
440    lsrs r0, r2
441    lsls r1, #1
442    adds r1, #1
443    muls r0, r1
444    bx lr
4451:
446    movs r0, #0
447    bx lr
4482:
449    lsrs r0, #31
450    adds r0, r3
451    subs r0, #1
452    bx lr
453
454    cmn r0, r0
455    bcc float2int
456    push {lr}
457    lsls r0, #1
458    lsrs r0, #1
459    movs r1, #0
460    bl __aeabi_f2uiz
461    cmp r0, #0
462    bmi 1f
463    negs r0, r0
464    pop {pc}
4651:
466    movs r0, #128
467    lsls r0, #24
468    pop {pc}
469
470float_section float2int
471regular_func float2int
472    shimmable_table_tail_call SF_TABLE_FLOAT2INT float2int_shim
473
474float_section float2fix
475regular_func float2fix
476    shimmable_table_tail_call SF_TABLE_FLOAT2FIX float2fix_shim
477
478float_section float2ufix
479regular_func float2ufix
480    table_tail_call SF_TABLE_FLOAT2UFIX
481
482// unsigned FUNC_NAME(__aeabi_f2uiz)(float)             float (single precision) to unsigned C-style conversion [3]
483float_wrapper_section __aeabi_f2uiz
484wrapper_func __aeabi_f2uiz
485regular_func float2uint_z
486    table_tail_call SF_TABLE_FLOAT2UINT
487
488float_section fix2float
489regular_func fix2float
490    table_tail_call SF_TABLE_FIX2FLOAT
491
492float_section ufix2float
493regular_func ufix2float
494    table_tail_call SF_TABLE_UFIX2FLOAT
495
496float_section fix642float
497regular_func fix642float
498    shimmable_table_tail_call SF_TABLE_FIX642FLOAT fix642float_shim
499
500float_section ufix642float
501regular_func ufix642float
502    shimmable_table_tail_call SF_TABLE_UFIX642FLOAT ufix642float_shim
503
504// float FUNC_NAME(__aeabi_l2f)(long long)             long long to float (single precision) conversion
505float_wrapper_section __aeabi_l2f
5061:
507    ldr r2, =__aeabi_i2f
508    bx r2
509wrapper_func __aeabi_l2f
510regular_func int642float
511    asrs r2, r0, #31
512    cmp r1, r2
513    beq 1b
514    shimmable_table_tail_call SF_TABLE_INT642FLOAT int642float_shim
515
516// float FUNC_NAME(__aeabi_l2f)(long long)             long long to float (single precision) conversion
517float_wrapper_section __aeabi_ul2f
5181:
519    ldr r2, =__aeabi_ui2f
520    bx r2
521wrapper_func __aeabi_ul2f
522regular_func uint642float
523    cmp r1, #0
524    beq 1b
525    shimmable_table_tail_call SF_TABLE_UINT642FLOAT uint642float_shim
526
527// long long FUNC_NAME(__aeabi_f2lz)(float)             float (single precision) to long long C-style conversion [3]
528float_wrapper_section __aeabi_f2lz
529wrapper_func __aeabi_f2lz
530regular_func float2int64_z
531    cmn r0, r0
532    bcc float2int64
533    push {lr}
534    lsls r0, #1
535    lsrs r0, #1
536    movs r1, #0
537    bl float2ufix64
538    cmp r1, #0
539    bmi 1f
540    movs r2, #0
541    negs r0, r0
542    sbcs r2, r1
543    mov r1, r2
544    pop {pc}
5451:
546    movs r1, #128
547    lsls r1, #24
548    movs r0, #0
549    pop {pc}
550
551float_section float2int64
552regular_func float2int64
553    shimmable_table_tail_call SF_TABLE_FLOAT2INT64 float2int64_shim
554
555float_section float2fix64
556regular_func float2fix64
557    shimmable_table_tail_call SF_TABLE_FLOAT2FIX64 float2fix64_shim
558
559// unsigned long long FUNC_NAME(__aeabi_f2ulz)(float)     float to unsigned long long C-style conversion [3]
560float_wrapper_section __aeabi_f2ulz
561wrapper_func __aeabi_f2ulz
562regular_func float2uint64_z
563    shimmable_table_tail_call SF_TABLE_FLOAT2UINT64 float2uint64_shim
564
565float_section float2ufix64
566regular_func float2ufix64
567    shimmable_table_tail_call SF_TABLE_FLOAT2UFIX64 float2ufix64_shim
568
569float_wrapper_section __aeabi_f2d
5701:
571#if PICO_FLOAT_PROPAGATE_NANS
572    // copy sign bit and 25 NAN id bits into sign bit and significant ID bits, also setting the high id bit
573    asrs r1, r0, #3
574    movs r2, #0xf
575    lsls r2, #27
576    orrs r1, r2
577    lsls r0, #25
578    bx lr
579#endif
580wrapper_func __aeabi_f2d
581#if PICO_FLOAT_PROPAGATE_NANS
582    movs r3, #1
583    lsls r3, #24
584    lsls r2, r0, #1
585    adds r2, r3
586    bhi 1b
587#endif
588    shimmable_table_tail_call SF_TABLE_FLOAT2DOUBLE float2double_shim
589
590float_wrapper_section sqrtf
591wrapper_func_f1 sqrtf
592#if PICO_FLOAT_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
593    // check for negative
594    asrs r1, r0, #23
595    bmi 1f
596#endif
597    table_tail_call SF_TABLE_FSQRT
598#if PICO_FLOAT_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
5991:
600    mvns r0, r1
601    cmp r0, #255
602    bne 2f
603    // -0 or -Denormal return -0 (0x80000000)
604    lsls r0, #31
605    bx lr
6062:
607    // return -Inf (0xff800000)
608    asrs r0, r1, #31
609    lsls r0, #23
610    bx lr
611#endif
612
613float_wrapper_section cosf
614// note we don't use _f1 since we do an infinity/nan check for outside of range
615wrapper_func cosf
616    // rom version only works for -128 < angle < 128
617    lsls r1, r0, #1
618    lsrs r1, #24
619    cmp r1, #127 + 7
620    bge 1f
6212:
622    table_tail_call SF_TABLE_FCOS
6231:
624#if PICO_FLOAT_PROPAGATE_NANS
625    // also check for infinites
626    cmp r1, #255
627    bne 3f
628    // infinite to nan
629    movs r1, #1
630    lsls r1, #22
631    orrs r0, r1
632    bx lr
6333:
634#endif
635    ldr r1, =0x40c90fdb // 2 * M_PI
636    push {lr}
637    bl remainderf
638    pop {r1}
639    mov lr, r1
640    b 2b
641
642float_wrapper_section sinf
643// note we don't use _f1 since we do an infinity/nan check for outside of range
644wrapper_func sinf
645    // rom version only works for -128 < angle < 128
646    lsls r1, r0, #1
647    lsrs r1, #24
648    cmp r1, #127 + 7
649    bge 1f
6502:
651    table_tail_call SF_TABLE_FSIN
6521:
653#if PICO_FLOAT_PROPAGATE_NANS
654    // also check for infinites
655    cmp r1, #255
656    bne 3f
657    // infinite to nan
658    movs r1, #1
659    lsls r1, #22
660    orrs r0, r1
661    bx lr
6623:
663#endif
664    ldr r1, =0x40c90fdb // 2 * M_PI
665    push {lr}
666    bl remainderf
667    pop {r1}
668    mov lr, r1
669    b 2b
670
671float_wrapper_section sincosf
672// note we don't use _f1 since we do an infinity/nan check for outside of range
673wrapper_func sincosf
674    push {r1, r2, lr}
675    // rom version only works for -128 < angle < 128
676    lsls r3, r0, #1
677    lsrs r3, #24
678    cmp r3, #127 + 7
679    bge 3f
6802:
681    ldr r3, =sf_table
682    ldr r3, [r3, #SF_TABLE_FSIN]
683    blx r3
684    pop {r2, r3}
685    str r0, [r2]
686    str r1, [r3]
687    pop {pc}
688#if PICO_FLOAT_PROPAGATE_NANS
689.align 2
690    pop {pc}
691#endif
6923:
693#if PICO_FLOAT_PROPAGATE_NANS
694    // also check for infinites
695    cmp r3, #255
696    bne 4f
697    // infinite to nan
698    movs r3, #1
699    lsls r3, #22
700    orrs r0, r3
701    str r0, [r1]
702    str r0, [r2]
703    add sp, #12
704    bx lr
7054:
706#endif
707    ldr r1, =0x40c90fdb // 2 * M_PI
708    push {lr}
709    bl remainderf
710    pop {r1}
711    mov lr, r1
712    b 2b
713
714float_wrapper_section tanf
715// note we don't use _f1 since we do an infinity/nan check for outside of range
716wrapper_func tanf
717    // rom version only works for -128 < angle < 128
718    lsls r1, r0, #1
719    lsrs r1, #24
720    cmp r1, #127 + 7
721    bge ftan_out_of_range
722ftan_in_range:
723#if !PICO_DIVIDER_DISABLE_INTERRUPTS
724    // to support IRQ usage (or context switch) we must save/restore divider state around call if state is dirty
725    ldr r2, =(SIO_BASE)
726    ldr r3, [r2, #SIO_DIV_CSR_OFFSET]
727    lsrs r3, #SIO_DIV_CSR_DIRTY_SHIFT_FOR_CARRY
728    bcs ftan_save_state
729#else
730    // to avoid worrying about IRQs (or context switches), simply disable interrupts around call
731    push {r4, lr}
732    mrs r4, PRIMASK
733    cpsid i
734    bl ftan_shim_call
735    msr PRIMASK, r4
736    pop {r4, pc}
737#endif
738ftan_shim_call:
739    table_tail_call SF_TABLE_FTAN
740#if !PICO_DIVIDER_DISABLE_INTERRUPTS
741ftan_save_state:
742    save_div_state_and_lr
743    bl ftan_shim_call
744    ldr r2, =(SIO_BASE)
745    restore_div_state_and_return
746#endif
747ftan_out_of_range:
748#if PICO_FLOAT_PROPAGATE_NANS
749    // also check for infinites
750    cmp r1, #255
751    bne 3f
752    // infinite to nan
753    movs r1, #1
754    lsls r1, #22
755    orrs r0, r1
756    bx lr
7573:
758#endif
759    ldr r1, =0x40c90fdb // 2 * M_PI
760    push {lr}
761    bl remainderf
762    pop {r1}
763    mov lr, r1
764    b ftan_in_range
765
766float_wrapper_section atan2f
767wrapper_func_f2 atan2f
768    shimmable_table_tail_call SF_TABLE_FATAN2 fatan2_shim
769
770float_wrapper_section expf
771wrapper_func_f1 expf
772    table_tail_call SF_TABLE_FEXP
773
774float_wrapper_section logf
775wrapper_func_f1 logf
776    table_tail_call SF_TABLE_FLN
777