1 // llmath.d
2 // Copyright (C) 1993-2008 by Digital Mars, http://www.digitalmars.com
3 // All Rights Reserved
4 // Written by Walter Bright
5 
6 // Compiler runtime support for 64 bit longs
7 
8 module rt.compiler.dmd.posix.llmath;
9 
10 extern (C):
11 
12 
13 /***************************************
14  * Unsigned long divide.
15  * Input:
16  *      [EDX,EAX],[ECX,EBX]
17  * Output:
18  *      [EDX,EAX] = [EDX,EAX] / [ECX,EBX]
19  *      [ECX,EBX] = [EDX,EAX] % [ECX,EBX]
20  */
21 
22 
23 void __ULDIV__()
24 {
25   version (D_InlineAsm_X86)
26     asm
27     {
28         naked                   ;
29         test    ECX,ECX         ;
30         jz      uldiv           ;
31 
32         // if ECX > EDX, then quotient is 0 and remainder is [EDX,EAX]
33         cmp     ECX,EDX         ;
34         ja      quo0            ;
35 
36         test    ECX,ECX         ;
37         js      Lleft           ;
38 
39         /* We have n>d, and know that n/d will fit in 32 bits.
40          * d will be left justified if we shift it left s bits.
41          * [d1,d0] <<= s
42          * [n2,n1,n0] = [n1,n0] << s
43          *
44          * Use one divide, by this reasoning:
45          * ([n2,n1]<<32 + n0)/(d1<<32 + d0)
46          * becomes:
47          * ([n2,n1]<<32)/(d1<<32 + d0) + n0/(d1<<32 + d0)
48          * The second divide is always 0.
49          * Ignore the d0 in the first divide, which will yield a quotient
50          * that might be too high by 1 (because d1 is left justified).
51          * We can tell if it's too big if:
52          *  q*[d1,d0] > [n2,n1,n0]
53          * which is:
54          *  q*[d1,d0] > [[q*[d1,0]+q%[d1,0],n1,n0]
55          * If we subtract q*[d1,0] from both sides, we get:
56          *  q*d0 > [[n2,n1]%d1,n0]
57          * So if it is too big by one, reduce q by one to q'=q-one.
58          * Compute remainder as:
59          *  r = ([n1,n0] - q'*[d1,d0]) >> s
60          * Again, we can subtract q*[d1,0]:
61          *  r = ([n1,n0] - q*[d1,0] - (q'*[d1,d0] - q*[d1,0])) >> s
62          *  r = ([[n2,n1]%d1,n0] + (q*[d1,0] - (q - one)*[d1,d0])) >> s
63          *  r = ([[n2,n1]%d1,n0] + (q*[d1,0] - [d1 *(q-one),d0*(1-q)])) >> s
64          *  r = ([[n2,n1]%d1,n0] + [d1 *one,d0*(one-q)])) >> s
65          */
66 
67         push    EBP             ;
68         push    ESI             ;
69         push    EDI             ;
70 
71         mov     ESI,EDX         ;
72         mov     EDI,EAX         ;
73         mov     EBP,ECX         ;
74 
75         bsr     EAX,ECX         ;       // EAX is now 30..0
76         xor     EAX,0x1F        ;       // EAX is now 1..31
77         mov     CH,AL           ;
78         neg     EAX             ;
79         add     EAX,32          ;
80         mov     CL,AL           ;
81 
82         mov     EAX,EBX         ;
83         shr     EAX,CL          ;
84         xchg    CH,CL           ;
85         shl     EBP,CL          ;
86         or      EBP,EAX         ;
87         shl     EBX,CL          ;
88 
89         mov     EDX,ESI         ;
90         xchg    CH,CL           ;
91         shr     EDX,CL          ;
92 
93         mov     EAX,EDI         ;
94         shr     EAX,CL          ;
95         xchg    CH,CL           ;
96         shl     EDI,CL          ;
97         shl     ESI,CL          ;
98         or      EAX,ESI         ;
99 
100         div     EBP             ;
101         push    EBP             ;
102         mov     EBP,EAX         ;
103         mov     ESI,EDX         ;
104 
105         mul     EBX             ;
106         cmp     EDX,ESI         ;
107         ja      L1              ;
108         jb      L2              ;
109         cmp     EAX,EDI         ;
110         jbe     L2              ;
111 L1:     dec     EBP             ;
112         sub     EAX,EBX         ;
113         sbb     EDX,0[ESP]      ;
114 L2:
115         add     ESP,4           ;
116         sub     EDI,EAX         ;
117         sbb     ESI,EDX         ;
118         mov     EAX,ESI         ;
119         xchg    CH,CL           ;
120         shl     EAX,CL          ;
121         xchg    CH,CL           ;
122         shr     EDI,CL          ;
123         or      EDI,EAX         ;
124         shr     ESI,CL          ;
125         mov     EBX,EDI         ;
126         mov     ECX,ESI         ;
127         mov     EAX,EBP         ;
128         xor     EDX,EDX         ;
129 
130         pop     EDI             ;
131         pop     ESI             ;
132         pop     EBP             ;
133         ret                     ;
134 
135 uldiv:  test    EDX,EDX         ;
136         jnz     D3              ;
137         // Both high words are 0, we can use the DIV instruction
138         div     EBX             ;
139         mov     EBX,EDX         ;
140         mov     EDX,ECX         ;       // EDX = ECX = 0
141         ret                     ;
142 
143         even                    ;
144 D3:     // Divide [EDX,EAX] by EBX
145         mov     ECX,EAX         ;
146         mov     EAX,EDX         ;
147         xor     EDX,EDX         ;
148         div     EBX             ;
149         xchg    ECX,EAX         ;
150         div     EBX             ;
151         // ECX,EAX = result
152         // EDX = remainder
153         mov     EBX,EDX         ;
154         mov     EDX,ECX         ;
155         xor     ECX,ECX         ;
156         ret                     ;
157 
158 quo0:   // Quotient is 0
159         // Remainder is [EDX,EAX]
160         mov     EBX,EAX         ;
161         mov     ECX,EDX         ;
162         xor     EAX,EAX         ;
163         xor     EDX,EDX         ;
164         ret                     ;
165 
166 Lleft:  // The quotient is 0 or 1 and EDX >= ECX
167         cmp     EDX,ECX         ;
168         ja      quo1            ;       // [EDX,EAX] > [ECX,EBX]
169         // EDX == ECX
170         cmp     EAX,EBX         ;
171         jb      quo0            ;
172 
173 quo1:   // Quotient is 1
174         // Remainder is [EDX,EAX] - [ECX,EBX]
175         sub     EAX,EBX         ;
176         sbb     EDX,ECX         ;
177         mov     EBX,EAX         ;
178         mov     ECX,EDX         ;
179         mov     EAX,1           ;
180         xor     EDX,EDX         ;
181         ret                     ;
182     }
183   else version (D_InlineAsm_X86_64)
184         assert(0);
185   else
186         static assert(0);
187 }
188 
189 
190 /***************************************
191  * Signed long divide.
192  * Input:
193  *      [EDX,EAX],[ECX,EBX]
194  * Output:
195  *      [EDX,EAX] = [EDX,EAX] / [ECX,EBX]
196  *      [ECX,EBX] = [EDX,EAX] % [ECX,EBX]
197  *      ESI,EDI destroyed
198  */
199 
200 void __LDIV__()
201 {
202   version (D_InlineAsm_X86)
203     asm
204     {
205         naked                   ;
206         test    EDX,EDX         ;       // [EDX,EAX] negative?
207         jns     L10             ;       // no
208         //neg64 EDX,EAX         ;       // [EDX,EAX] = -[EDX,EAX]
209           neg   EDX             ;
210           neg   EAX             ;
211           sbb   EDX,0           ;
212         test    ECX,ECX         ;       // [ECX,EBX] negative?
213         jns     L11             ;       // no
214         //neg64 ECX,EBX         ;
215           neg   ECX             ;
216           neg   EBX             ;
217           sbb   ECX,0           ;
218         call    __ULDIV__       ;
219         //neg64 ECX,EBX         ;       // remainder same sign as dividend
220           neg   ECX             ;
221           neg   EBX             ;
222           sbb   ECX,0           ;
223         ret                     ;
224 
225 L11:    call    __ULDIV__       ;
226         //neg64 ECX,EBX         ;       // remainder same sign as dividend
227           neg   ECX             ;
228           neg   EBX             ;
229           sbb   ECX,0           ;
230         //neg64 EDX,EAX         ;       // quotient is negative
231           neg   EDX             ;
232           neg   EAX             ;
233           sbb   EDX,0           ;
234         ret                     ;
235 
236 L10:    test    ECX,ECX         ;       // [ECX,EBX] negative?
237         jns     L12             ;       // no (all is positive)
238         //neg64 ECX,EBX         ;
239           neg   ECX             ;
240           neg   EBX             ;
241           sbb   ECX,0           ;
242         call    __ULDIV__       ;
243         //neg64 EDX,EAX         ;       // quotient is negative
244           neg   EDX             ;
245           neg   EAX             ;
246           sbb   EDX,0           ;
247         ret                     ;
248 
249 L12:    jmp     __ULDIV__       ;
250     }
251   else version (D_InlineAsm_X86_64)
252         assert(0);
253   else
254         static assert(0);
255 }
256 
257 /***************************************
258  * Compare [EDX,EAX] with [ECX,EBX]
259  * Signed
260  * Returns result in flags
261  */
262 
263 void __LCMP__()
264 {
265   version (D_InlineAsm_X86)
266     asm
267     {
268         naked                   ;
269         cmp     EDX,ECX         ;
270         jne     C1              ;
271         push    EDX             ;
272         xor     EDX,EDX         ;
273         cmp     EAX,EBX         ;
274         jz      C2              ;
275         ja      C3              ;
276         dec     EDX             ;
277         pop     EDX             ;
278         ret                     ;
279 
280 C3:     inc     EDX             ;
281 C2:     pop     EDX             ;
282 C1:     ret                     ;
283     }
284   else version (D_InlineAsm_X86_64)
285         assert(0);
286   else
287         static assert(0);
288 }
289 
290 // Convert ulong to real
291 
292 private real adjust = cast(real)0x800_0000_0000_0000 * 0x10;
293 
294 real __U64_LDBL()
295 {
296     version (OSX)
297     {
298       version (D_InlineAsm_X86)
299         asm
300         {   naked                               ;
301             push        EDX                     ;
302             push        EAX                     ;
303             and         dword ptr 4[ESP], 0x7FFFFFFF    ;
304             fild        qword ptr [ESP]         ;
305             test        EDX,EDX                 ;
306             jns         L1                      ;
307             push        0x0000403e              ;
308             push        0x80000000              ;
309             push        0                       ;
310             fld         real ptr [ESP]          ; // adjust
311             add         ESP,12                  ;
312             faddp       ST(1), ST               ;
313         L1:                                     ;
314             add         ESP, 8                  ;
315             ret                                 ;
316         }
317       else version (D_InlineAsm_X86_64)
318             static assert(0);
319       else
320             static assert(0);
321     }
322     else
323     {
324       version (D_InlineAsm_X86)
325         asm
326         {   naked                               ;
327             push        EDX                     ;
328             push        EAX                     ;
329             and         dword ptr 4[ESP], 0x7FFFFFFF    ;
330             fild        qword ptr [ESP]         ;
331             test        EDX,EDX                 ;
332             jns         L1                      ;
333             fld         real ptr adjust         ;
334             faddp       ST(1), ST               ;
335         L1:                                     ;
336             add         ESP, 8                  ;
337             ret                                 ;
338         }
339       else version (D_InlineAsm_X86_64)
340         asm
341         {   naked                               ;
342             push        RAX                     ;
343             and         dword ptr 4[RSP], 0x7FFFFFFF    ;
344             fild        qword ptr [RSP]         ;
345             test        RAX,RAX                 ;
346             jns         L1                      ;
347             fld         real ptr adjust         ;
348             faddp       ST(1), ST               ;
349         L1:                                     ;
350             add         RSP, 8                  ;
351             ret                                 ;
352         }
353       else
354             static assert(0);
355     }
356 }
357 
358 // Same as __U64_LDBL, but return result as double in [EDX,EAX]
359 ulong __ULLNGDBL()
360 {
361   version (D_InlineAsm_X86)
362     asm
363     {   naked                                   ;
364         call __U64_LDBL                         ;
365         sub  ESP,8                              ;
366         fstp double ptr [ESP]                   ;
367         pop  EAX                                ;
368         pop  EDX                                ;
369         ret                                     ;
370     }
371   else version (D_InlineAsm_X86_64)
372     asm
373     {   naked                                   ;
374         call __U64_LDBL                         ;
375         sub  RSP,8                              ;
376         fstp double ptr [RSP]                   ;
377         pop  RAX                                ;
378         ret                                     ;
379     }
380   else
381         static assert(0);
382 }
383 
384 
385 // Convert double to ulong
386 
387 private short roundTo0 = 0xFBF;
388 
389 ulong __DBLULLNG()
390 {
391     // BUG: should handle NAN's and overflows
392     version (OSX)
393     {
394       version (D_InlineAsm_X86)
395         asm
396         {   naked                               ;
397             push        0xFBF                   ; // roundTo0
398             push        0x0000403e              ;
399             push        0x80000000              ;
400             push        0                       ; // adjust
401             push        EDX                     ;
402             push        EAX                     ;
403             fld         double ptr [ESP]        ;
404             sub         ESP,8                   ;
405             fld         real ptr 16[ESP]        ; // adjust
406             fcomp                               ;
407             fstsw       AX                      ;
408             fstcw       8[ESP]                  ;
409             fldcw       28[ESP]                 ; // roundTo0
410             sahf                                ;
411             jae         L1                      ;
412             fld         real ptr 16[ESP]        ; // adjust
413             fsubp       ST(1), ST               ;
414             fistp       qword ptr [ESP]         ;
415             pop         EAX                     ;
416             pop         EDX                     ;
417             fldcw       [ESP]                   ;
418             add         ESP,24                  ;
419             add         EDX,0x8000_0000         ;
420             ret                                 ;
421         L1:                                     ;
422             fistp       qword ptr [ESP]         ;
423             pop         EAX                     ;
424             pop         EDX                     ;
425             fldcw       [ESP]                   ;
426             add         ESP,24                  ;
427             ret                                 ;
428         }
429       else version (D_InlineAsm_X86_64)
430             static assert(0);
431       else
432             static assert(0);
433     }
434     else
435     {
436       version (D_InlineAsm_X86)
437         asm
438         {   naked                               ;
439             push        EDX                     ;
440             push        EAX                     ;
441             fld         double ptr [ESP]        ;
442             sub         ESP,8                   ;
443             fld         real ptr adjust         ;
444             fcomp                               ;
445             fstsw       AX                      ;
446             fstcw       8[ESP]                  ;
447             fldcw       roundTo0                ;
448             sahf                                ;
449             jae         L1                      ;
450             fld         real ptr adjust         ;
451             fsubp       ST(1), ST               ;
452             fistp       qword ptr [ESP]         ;
453             pop         EAX                     ;
454             pop         EDX                     ;
455             fldcw       [ESP]                   ;
456             add         ESP,8                   ;
457             add         EDX,0x8000_0000         ;
458             ret                                 ;
459         L1:                                     ;
460             fistp       qword ptr [ESP]         ;
461             pop         EAX                     ;
462             pop         EDX                     ;
463             fldcw       [ESP]                   ;
464             add         ESP,8                   ;
465             ret                                 ;
466         }
467       else version (D_InlineAsm_X86_64)
468         asm
469         {   naked                               ;
470             push        RAX                     ;
471             fld         double ptr [RSP]        ;
472             sub         RSP,8                   ;
473             fld         real ptr adjust         ;
474             fcomp                               ;
475             fstsw       AX                      ;
476             fstcw       8[RSP]                  ;
477             fldcw       roundTo0                ;
478             sahf                                ;
479             jae         L1                      ;
480             fld         real ptr adjust         ;
481             fsubp       ST(1), ST               ;
482             fistp       qword ptr [RSP]         ;
483             pop         RAX                     ;
484             fldcw       [RSP]                   ;
485             add         RSP,8                   ;
486             mov         EDX,0x8000_0000         ;
487             shl         RDX,32                  ;
488             add         RAX,RDX                 ;
489             ret                                 ;
490         L1:                                     ;
491             fistp       qword ptr [RSP]         ;
492             pop         RAX                     ;
493             fldcw       [RSP]                   ;
494             add         RSP,8                   ;
495             ret                                 ;
496         }
497       else
498             static assert(0);
499     }
500 }
501 
502 // Convert double in ST0 to uint
503 
504 uint __DBLULNG()
505 {
506     // BUG: should handle NAN's and overflows
507     version (OSX)
508     {
509       version (D_InlineAsm_X86)
510         asm
511         {   naked                               ;
512             push        0xFBF                   ; // roundTo0
513             sub         ESP,12                  ;
514             fstcw       8[ESP]                  ;
515             fldcw       12[ESP]                 ; // roundTo0
516             fistp       qword ptr [ESP]         ;
517             fldcw       8[ESP]                  ;
518             pop         EAX                     ;
519             add         ESP,12                  ;
520             ret                                 ;
521         }
522       else version (D_InlineAsm_X86_64)
523             assert(0);
524       else
525             static assert(0);
526     }
527     else
528     {
529       version (D_InlineAsm_X86)
530         asm
531         {   naked                               ;
532             sub         ESP,16                  ;
533             fstcw       8[ESP]                  ;
534             fldcw       roundTo0                ;
535             fistp       qword ptr [ESP]         ;
536             fldcw       8[ESP]                  ;
537             pop         EAX                     ;
538             add         ESP,12                  ;
539             ret                                 ;
540         }
541       else version (D_InlineAsm_X86_64)
542         asm
543         {   naked                               ;
544             sub         RSP,16                  ;
545             fstcw       8[RSP]                  ;
546             fldcw       roundTo0                ;
547             fistp       qword ptr [RSP]         ;
548             fldcw       8[RSP]                  ;
549             pop         RAX                     ;
550             add         RSP,8                   ;
551             ret                                 ;
552         }
553       else
554             static assert(0);
555     }
556 }
557 
558 // Convert real in ST0 to ulong
559 
560 ulong __LDBLULLNG()
561 {
562     version (OSX)
563     {
564       version (D_InlineAsm_X86)
565         asm
566         {   naked                               ;
567             push        0xFBF                   ; // roundTo0
568             push        0x0000403e              ;
569             push        0x80000000              ;
570             push        0                       ; // adjust
571             sub         ESP,16                  ;
572             fld         real ptr 16[ESP]        ; // adjust
573             fcomp                               ;
574             fstsw       AX                      ;
575             fstcw       8[ESP]                  ;
576             fldcw       28[ESP]                 ; // roundTo0
577             sahf                                ;
578             jae         L1                      ;
579             fld         real ptr 16[ESP]        ; // adjust
580             fsubp       ST(1), ST               ;
581             fistp       qword ptr [ESP]         ;
582             pop         EAX                     ;
583             pop         EDX                     ;
584             fldcw       [ESP]                   ;
585             add         ESP,24                  ;
586             add         EDX,0x8000_0000         ;
587             ret                                 ;
588         L1:                                     ;
589             fistp       qword ptr [ESP]         ;
590             pop         EAX                     ;
591             pop         EDX                     ;
592             fldcw       [ESP]                   ;
593             add         ESP,24                  ;
594             ret                                 ;
595         }
596       else version (D_InlineAsm_X86_64)
597             static assert(0);
598       else
599             static assert(0);
600     }
601     else
602     {
603       version (D_InlineAsm_X86)
604         asm
605         {   naked                               ;
606             sub         ESP,16                  ;
607             fld         real ptr adjust         ;
608             fcomp                               ;
609             fstsw       AX                      ;
610             fstcw       8[ESP]                  ;
611             fldcw       roundTo0                ;
612             sahf                                ;
613             jae         L1                      ;
614             fld         real ptr adjust         ;
615             fsubp       ST(1), ST               ;
616             fistp       qword ptr [ESP]         ;
617             pop         EAX                     ;
618             pop         EDX                     ;
619             fldcw       [ESP]                   ;
620             add         ESP,8                   ;
621             add         EDX,0x8000_0000         ;
622             ret                                 ;
623         L1:                                     ;
624             fistp       qword ptr [ESP]         ;
625             pop         EAX                     ;
626             pop         EDX                     ;
627             fldcw       [ESP]                   ;
628             add         ESP,8                   ;
629             ret                                 ;
630         }
631       else version (D_InlineAsm_X86_64)
632         asm
633         {   naked                               ;
634             sub         RSP,16                  ;
635             fld         real ptr adjust         ;
636             fcomp                               ;
637             fstsw       AX                      ;
638             fstcw       8[RSP]                  ;
639             fldcw       roundTo0                ;
640             sahf                                ;
641             jae         L1                      ;
642             fld         real ptr adjust         ;
643             fsubp       ST(1), ST               ;
644             fistp       qword ptr [RSP]         ;
645             pop         RAX                     ;
646             fldcw       [RSP]                   ;
647             add         RSP,8                   ;
648             mov         RCX,0x8000_0000         ;
649             shl         RCX,32                  ;
650             add         RAX,RCX                 ;
651             ret                                 ;
652         L1:                                     ;
653             fistp       qword ptr [RSP]         ;
654             pop         RAX                     ;
655             fldcw       [RSP]                   ;
656             add         RSP,8                   ;
657             ret                                 ;
658         }
659       else
660             static assert(0);
661     }
662 }