diff druntime/src/compiler/dmd/llmath.d @ 1458:e0b2d67cfe7c

Added druntime (this should be removed once it works).
author Robert Clipsham <robert@octarineparrot.com>
date Tue, 02 Jun 2009 17:43:06 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/druntime/src/compiler/dmd/llmath.d	Tue Jun 02 17:43:06 2009 +0100
@@ -0,0 +1,531 @@
+/**
+ * Support for 64-bit longs.
+ *
+ * Copyright: Copyright Digital Mars 1993 - 2009.
+ * License:   <a href="http://www.boost.org/LICENSE_1_0.txt>Boost License 1.0</a>.
+ * Authors:   Walter Bright, Sean Kelly
+ *
+ *          Copyright Digital Mars 1993 - 2009.
+ * Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ */
+module rt.llmath;
+
+extern (C):
+
+
+/***************************************
+ * Unsigned long divide.
+ * Input:
+ *      [EDX,EAX],[ECX,EBX]
+ * Output:
+ *      [EDX,EAX] = [EDX,EAX] / [ECX,EBX]
+ *      [ECX,EBX] = [EDX,EAX] % [ECX,EBX]
+ */
+
+void __ULDIV__()
+{
+    asm
+    {
+        naked                   ;
+        test    ECX,ECX         ;
+        jz      uldiv           ;
+
+        // if ECX > EDX, then quotient is 0 and remainder is [EDX,EAX]
+        cmp     ECX,EDX         ;
+        ja      quo0            ;
+
+        test    ECX,ECX         ;
+        js      Lleft           ;
+
+        /* We have n>d, and know that n/d will fit in 32 bits.
+         * d will be left justified if we shift it left s bits.
+         * [d1,d0] <<= s
+         * [n2,n1,n0] = [n1,n0] << s
+         *
+         * Use one divide, by this reasoning:
+         * ([n2,n1]<<32 + n0)/(d1<<32 + d0)
+         * becomes:
+         * ([n2,n1]<<32)/(d1<<32 + d0) + n0/(d1<<32 + d0)
+         * The second divide is always 0.
+         * Ignore the d0 in the first divide, which will yield a quotient
+         * that might be too high by 1 (because d1 is left justified).
+         * We can tell if it's too big if:
+         *  q*[d1,d0] > [n2,n1,n0]
+         * which is:
+         *  q*[d1,d0] > [[q*[d1,0]+q%[d1,0],n1,n0]
+         * If we subtract q*[d1,0] from both sides, we get:
+         *  q*d0 > [[n2,n1]%d1,n0]
+         * So if it is too big by one, reduce q by one to q'=q-one.
+         * Compute remainder as:
+         *  r = ([n1,n0] - q'*[d1,d0]) >> s
+         * Again, we can subtract q*[d1,0]:
+         *  r = ([n1,n0] - q*[d1,0] - (q'*[d1,d0] - q*[d1,0])) >> s
+         *  r = ([[n2,n1]%d1,n0] + (q*[d1,0] - (q - one)*[d1,d0])) >> s
+         *  r = ([[n2,n1]%d1,n0] + (q*[d1,0] - [d1 *(q-one),d0*(1-q)])) >> s
+         *  r = ([[n2,n1]%d1,n0] + [d1 *one,d0*(one-q)])) >> s
+         */
+
+        push    EBP             ;
+        push    ESI             ;
+        push    EDI             ;
+
+        mov     ESI,EDX         ;
+        mov     EDI,EAX         ;
+        mov     EBP,ECX         ;
+
+        bsr     EAX,ECX         ;       // EAX is now 30..0
+        xor     EAX,0x1F        ;       // EAX is now 1..31
+        mov     CH,AL           ;
+        neg     EAX             ;
+        add     EAX,32          ;
+        mov     CL,AL           ;
+
+        mov     EAX,EBX         ;
+        shr     EAX,CL          ;
+        xchg    CH,CL           ;
+        shl     EBP,CL          ;
+        or      EBP,EAX         ;
+        shl     EBX,CL          ;
+
+        mov     EDX,ESI         ;
+        xchg    CH,CL           ;
+        shr     EDX,CL          ;
+
+        mov     EAX,EDI         ;
+        shr     EAX,CL          ;
+        xchg    CH,CL           ;
+        shl     EDI,CL          ;
+        shl     ESI,CL          ;
+        or      EAX,ESI         ;
+
+        div     EBP             ;
+        push    EBP             ;
+        mov     EBP,EAX         ;
+        mov     ESI,EDX         ;
+
+        mul     EBX             ;
+        cmp     EDX,ESI         ;
+        ja      L1              ;
+        jb      L2              ;
+        cmp     EAX,EDI         ;
+        jbe     L2              ;
+L1:     dec     EBP             ;
+        sub     EAX,EBX         ;
+        sbb     EDX,0[ESP]      ;
+L2:
+        add     ESP,4           ;
+        sub     EDI,EAX         ;
+        sbb     ESI,EDX         ;
+        mov     EAX,ESI         ;
+        xchg    CH,CL           ;
+        shl     EAX,CL          ;
+        xchg    CH,CL           ;
+        shr     EDI,CL          ;
+        or      EDI,EAX         ;
+        shr     ESI,CL          ;
+        mov     EBX,EDI         ;
+        mov     ECX,ESI         ;
+        mov     EAX,EBP         ;
+        xor     EDX,EDX         ;
+
+        pop     EDI             ;
+        pop     ESI             ;
+        pop     EBP             ;
+        ret                     ;
+
+uldiv:  test    EDX,EDX         ;
+        jnz     D3              ;
+        // Both high words are 0, we can use the DIV instruction
+        div     EBX             ;
+        mov     EBX,EDX         ;
+        mov     EDX,ECX         ;       // EDX = ECX = 0
+        ret                     ;
+
+        even                    ;
+D3:     // Divide [EDX,EAX] by EBX
+        mov     ECX,EAX         ;
+        mov     EAX,EDX         ;
+        xor     EDX,EDX         ;
+        div     EBX             ;
+        xchg    ECX,EAX         ;
+        div     EBX             ;
+        // ECX,EAX = result
+        // EDX = remainder
+        mov     EBX,EDX         ;
+        mov     EDX,ECX         ;
+        xor     ECX,ECX         ;
+        ret                     ;
+
+quo0:   // Quotient is 0
+        // Remainder is [EDX,EAX]
+        mov     EBX,EAX         ;
+        mov     ECX,EDX         ;
+        xor     EAX,EAX         ;
+        xor     EDX,EDX         ;
+        ret                     ;
+
+Lleft:  // The quotient is 0 or 1 and EDX >= ECX
+        cmp     EDX,ECX         ;
+        ja      quo1            ;       // [EDX,EAX] > [ECX,EBX]
+        // EDX == ECX
+        cmp     EAX,EBX         ;
+        jb      quo0            ;
+
+quo1:   // Quotient is 1
+        // Remainder is [EDX,EAX] - [ECX,EBX]
+        sub     EAX,EBX         ;
+        sbb     EDX,ECX         ;
+        mov     EBX,EAX         ;
+        mov     ECX,EDX         ;
+        mov     EAX,1           ;
+        xor     EDX,EDX         ;
+        ret                     ;
+    }
+}
+
+
+/***************************************
+ * Signed long divide.
+ * Input:
+ *      [EDX,EAX],[ECX,EBX]
+ * Output:
+ *      [EDX,EAX] = [EDX,EAX] / [ECX,EBX]
+ *      [ECX,EBX] = [EDX,EAX] % [ECX,EBX]
+ *      ESI,EDI destroyed
+ */
+
+void __LDIV__()
+{
+    asm
+    {
+        naked                   ;
+        test    EDX,EDX         ;       // [EDX,EAX] negative?
+        jns     L10             ;       // no
+        //neg64 EDX,EAX         ;       // [EDX,EAX] = -[EDX,EAX]
+          neg   EDX             ;
+          neg   EAX             ;
+          sbb   EDX,0           ;
+        test    ECX,ECX         ;       // [ECX,EBX] negative?
+        jns     L11             ;       // no
+        //neg64 ECX,EBX         ;
+          neg   ECX             ;
+          neg   EBX             ;
+          sbb   ECX,0           ;
+        call    __ULDIV__       ;
+        //neg64 ECX,EBX         ;       // remainder same sign as dividend
+          neg   ECX             ;
+          neg   EBX             ;
+          sbb   ECX,0           ;
+        ret                     ;
+
+L11:    call    __ULDIV__       ;
+        //neg64 ECX,EBX         ;       // remainder same sign as dividend
+          neg   ECX             ;
+          neg   EBX             ;
+          sbb   ECX,0           ;
+        //neg64 EDX,EAX         ;       // quotient is negative
+          neg   EDX             ;
+          neg   EAX             ;
+          sbb   EDX,0           ;
+        ret                     ;
+
+L10:    test    ECX,ECX         ;       // [ECX,EBX] negative?
+        jns     L12             ;       // no (all is positive)
+        //neg64 ECX,EBX         ;
+          neg   ECX             ;
+          neg   EBX             ;
+          sbb   ECX,0           ;
+        call    __ULDIV__       ;
+        //neg64 EDX,EAX         ;       // quotient is negative
+          neg   EDX             ;
+          neg   EAX             ;
+          sbb   EDX,0           ;
+        ret                     ;
+
+L12:    jmp     __ULDIV__       ;
+    }
+}
+
+
+/***************************************
+ * Compare [EDX,EAX] with [ECX,EBX]
+ * Signed
+ * Returns result in flags
+ */
+
+void __LCMP__()
+{
+    asm
+    {
+        naked                   ;
+        cmp     EDX,ECX         ;
+        jne     C1              ;
+        push    EDX             ;
+        xor     EDX,EDX         ;
+        cmp     EAX,EBX         ;
+        jz      C2              ;
+        ja      C3              ;
+        dec     EDX             ;
+        pop     EDX             ;
+        ret                     ;
+
+C3:     inc     EDX             ;
+C2:     pop     EDX             ;
+C1:     ret                     ;
+    }
+}
+
+
+
+
+// Convert ulong to real
+
+private __gshared real adjust = cast(real)0x800_0000_0000_0000 * 0x10;
+
+real __U64_LDBL()
+{
+    version (OSX)
+    {   /* OSX version has to be concerned about 16 byte stack
+         * alignment and the inability to reference the data segment
+         * because of PIC.
+         */
+        asm
+        {   naked                               ;
+            push        EDX                     ;
+            push        EAX                     ;
+            and         dword ptr 4[ESP], 0x7FFFFFFF    ;
+            fild        qword ptr [ESP]         ;
+            test        EDX,EDX                 ;
+            jns         L1                      ;
+            push        0x0000403e              ;
+            push        0x80000000              ;
+            push        0                       ;
+            fld         real ptr [ESP]          ; // adjust
+            add         ESP,12                  ;
+            faddp       ST(1), ST               ;
+        L1:                                     ;
+            add         ESP, 8                  ;
+            ret                                 ;
+        }
+    }
+    else
+    {
+        asm
+        {   naked                               ;
+            push        EDX                     ;
+            push        EAX                     ;
+            and         dword ptr 4[ESP], 0x7FFFFFFF    ;
+            fild        qword ptr [ESP]         ;
+            test        EDX,EDX                 ;
+            jns         L1                      ;
+            fld         real ptr adjust         ;
+            faddp       ST(1), ST               ;
+        L1:                                     ;
+            add         ESP, 8                  ;
+            ret                                 ;
+        }
+    }
+}
+
+// Same as __U64_LDBL, but return result as double in [EDX,EAX]
+ulong __ULLNGDBL()
+{
+    asm
+    {   naked                                   ;
+        call __U64_LDBL                         ;
+        sub  ESP,8                              ;
+        fstp double ptr [ESP]                   ;
+        pop  EAX                                ;
+        pop  EDX                                ;
+        ret                                     ;
+    }
+}
+
+// Convert double to ulong
+
+private __gshared short roundTo0 = 0xFBF;
+
+ulong __DBLULLNG()
+{
+    // BUG: should handle NAN's and overflows
+    version (OSX)
+    {
+        asm
+        {   naked                               ;
+            push        0xFBF                   ; // roundTo0
+            push        0x0000403e              ;
+            push        0x80000000              ;
+            push        0                       ; // adjust
+            push        EDX                     ;
+            push        EAX                     ;
+            fld         double ptr [ESP]        ;
+            sub         ESP,8                   ;
+            fld         real ptr 16[ESP]        ; // adjust
+            fcomp                               ;
+            fstsw       AX                      ;
+            fstcw       8[ESP]                  ;
+            fldcw       28[ESP]                 ; // roundTo0
+            sahf                                ;
+            jae         L1                      ;
+            fld         real ptr 16[ESP]        ; // adjust
+            fsubp       ST(1), ST               ;
+            fistp       qword ptr [ESP]         ;
+            pop         EAX                     ;
+            pop         EDX                     ;
+            fldcw       [ESP]                   ;
+            add         ESP,24                  ;
+            add         EDX,0x8000_0000         ;
+            ret                                 ;
+        L1:                                     ;
+            fistp       qword ptr [ESP]         ;
+            pop         EAX                     ;
+            pop         EDX                     ;
+            fldcw       [ESP]                   ;
+            add         ESP,24                  ;
+            ret                                 ;
+        }
+    }
+    else
+    {
+        asm
+        {   naked                               ;
+            push        EDX                     ;
+            push        EAX                     ;
+            fld         double ptr [ESP]        ;
+            sub         ESP,8                   ;
+            fld         real ptr adjust         ;
+            fcomp                               ;
+            fstsw       AX                      ;
+            fstcw       8[ESP]                  ;
+            fldcw       roundTo0                ;
+            sahf                                ;
+            jae         L1                      ;
+            fld         real ptr adjust         ;
+            fsubp       ST(1), ST               ;
+            fistp       qword ptr [ESP]         ;
+            pop         EAX                     ;
+            pop         EDX                     ;
+            fldcw       [ESP]                   ;
+            add         ESP,8                   ;
+            add         EDX,0x8000_0000         ;
+            ret                                 ;
+        L1:                                     ;
+            fistp       qword ptr [ESP]         ;
+            pop         EAX                     ;
+            pop         EDX                     ;
+            fldcw       [ESP]                   ;
+            add         ESP,8                   ;
+            ret                                 ;
+        }
+    }
+}
+
+// Convert double in ST0 to uint
+
+uint __DBLULNG()
+{
+    // BUG: should handle NAN's and overflows
+    version (OSX)
+    {
+        asm
+        {   naked                               ;
+            push        0xFBF                   ; // roundTo0
+            sub         ESP,12                  ;
+            fstcw       8[ESP]                  ;
+            fldcw       12[ESP]                 ; // roundTo0
+            fistp       qword ptr [ESP]         ;
+            fldcw       8[ESP]                  ;
+            pop         EAX                     ;
+            add         ESP,12                  ;
+            ret                                 ;
+        }
+    }
+    else
+    {
+        asm
+        {   naked                               ;
+            sub         ESP,16                  ;
+            fstcw       8[ESP]                  ;
+            fldcw       roundTo0                ;
+            fistp       qword ptr [ESP]         ;
+            fldcw       8[ESP]                  ;
+            pop         EAX                     ;
+            add         ESP,12                  ;
+            ret                                 ;
+        }
+    }
+}
+
+// Convert real in ST0 to ulong
+
+ulong __LDBLULLNG()
+{
+    version (OSX)
+    {
+	asm
+	{   naked				;
+	    push	0xFBF			; // roundTo0
+	    push	0x0000403e		;
+	    push	0x80000000		;
+	    push	0			; // adjust
+	    sub		ESP,16			;
+	    fld		real ptr 16[ESP]	; // adjust
+	    fcomp				;
+	    fstsw	AX			;
+	    fstcw	8[ESP]			;
+	    fldcw	28[ESP]			; // roundTo0
+	    sahf				;
+	    jae		L1			;
+	    fld		real ptr 16[ESP]	; // adjust
+	    fsubp	ST(1), ST		;
+	    fistp	qword ptr [ESP]		;
+	    pop		EAX			;
+	    pop		EDX			;
+	    fldcw	[ESP]			;
+	    add		ESP,24			;
+	    add		EDX,0x8000_0000		;
+	    ret					;
+	L1:					;
+	    fistp	qword ptr [ESP]		;
+	    pop		EAX			;
+	    pop		EDX			;
+	    fldcw	[ESP]			;
+	    add		ESP,24			;
+	    ret					;
+	}
+    }
+    else
+    {
+	asm
+	{   naked				;
+	    sub		ESP,16			;
+	    fld		real ptr adjust		;
+	    fcomp				;
+	    fstsw	AX			;
+	    fstcw	8[ESP]			;
+	    fldcw	roundTo0		;
+	    sahf				;
+	    jae		L1			;
+	    fld		real ptr adjust		;
+	    fsubp	ST(1), ST		;
+	    fistp	qword ptr [ESP]		;
+	    pop		EAX			;
+	    pop		EDX			;
+	    fldcw	[ESP]			;
+	    add		ESP,8			;
+	    add		EDX,0x8000_0000		;
+	    ret					;
+	L1:					;
+	    fistp	qword ptr [ESP]		;
+	    pop		EAX			;
+	    pop		EDX			;
+	    fldcw	[ESP]			;
+	    add		ESP,8			;
+	    ret					;
+	}
+    }
+}
+
+