Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix usage of signbit() in SRFI 144 #747

Merged
merged 1 commit into from
Jun 6, 2021
Merged

Conversation

ilammy
Copy link
Contributor

@ilammy ilammy commented Jun 5, 2021

C standard defines signbit() as a macro returning "non-zero value" for negative arguments (see 7.12.3.6 of C11 standard). SRFI 144's flsign-bit is defined to return exactly 1.

Make sure to convert the result of signbit() call into "boolean int" which is either 0 or 1.


This is not a theoretical issue. This causes SRFI 144 test suite to fail on many architectures that are not x86_64 or ARM64.

GCC on x86_64 compiles signbit() as

    movmskpd %xmm0, %eax
    andl     $1, %eax

which indeed returns either 0 or 1. movmskpd extracts 2-bit sign mask from the FP value in src register and stores that in low-order bits of the dst register. Then the unneeded extra bit is masked out, leaving only the lowest bit set or unset.

However, other architectures don't have such conveniences and go with more direct approach. For example, GCC on ARMv7 produces this:

    sub     sp, sp, #8
    vstr.64 d0, [sp]
    ldr     r0, [sp, #4]
    and     r0, r0, #0x80000000
    add     sp, sp, #8
    bx      lr

which effectively returns either 0 or -1. Generated code masks out everything but the sign bit and returns the result as is. The value 0x80000000 is the representation of -1.

Even on i386 signbit() is compiled as

    fldl    4(%esp)
    fxam
    fnstsw  %ax
    fstp    %st(0)
    andl    $512, %eax
    ret

which effectively returns either 0 or 512: fxam sets C1 bit FPU status word to the sign of FP value, then the status word is extracted, the "sign bit" is masked out, and left as is.

@@ -102,7 +102,8 @@

(define-c double frexp (double (result int)))

(define-c int (sign-bit "signbit") (double))
(c-declare "#define sign_bit(v) (!!signbit(v))")
(define-c int (sign-bit "sign_bit") (double))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI you can just say:

(define-c int sign-bit (double))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right. I've pushed an updated version that uses a shorter form.

@ashinn
Copy link
Owner

ashinn commented Jun 6, 2021

Thanks! signbit is a very confusingly named macro :)

C standard defines signbit() as a macro returning "non-zero value" for
negative arguments (see 7.12.3.6 of C11 standard). SRFI 144's flsign-bit
is defined to return exactly 1.

Make sure to convert the result of signbit() call into "boolean int"
which is either 0 or 1.

This is not a theoretical issue. This causes SRFI 144 test suite to fail
on many architectures that are not x86_64.

GCC on x86_64 compiles signbit() as

        movmskpd %xmm0, %eax
        andl     $1, %eax

which indeed returns either 0 or 1. movmskpd extracts 2-bit sign mask
from the FP value in src register and stores that in low-order bits of
the dst register. Then the unneded extra bit is masked out, leaving only
the lowest bit set or unset.

However, other architectures don't have such conveniences and go with
more direct approach. For example, GCC on ARMv7 produces this:

        sub     sp, sp, ashinn#8
        vstr.64 d0, [sp]
        ldr     r0, [sp, ashinn#4]
        and     r0, r0, #0x80000000
        add     sp, sp, ashinn#8
        bx      lr

which effectively returns either 0 or -1. Generated code masks out
everything but the sign bit and returns the result as is. The value
0x80000000 is the representation of -1.

Even on i386 signbit() is compiled as

        fldl    4(%esp)
        fxam
        fnstsw  %ax
        fstp    %st(0)
        andl    $512, %eax
        ret

which effectively returns either 0 or 512: fxam sets C1 bit FPU status
word to the sign of FP value, then the status word is extracted, the
"sign bit" is masked out, and left as is.
@ashinn ashinn merged commit ead3668 into ashinn:master Jun 6, 2021
@ilammy ilammy deleted the sign-bit branch June 8, 2021 07:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants