Useless jp / jnp assembly instruction on x86_64
Asked Answered
T

1

6

I'm trying to figure out what purpose jp/jnp instructions serve in LLVM-generated C code. Sample:

int main(int argc, const char * argv[]) {
    double value = 1.5;

    if (value == 1.5) {
        value = 3.0;
    }

    return 0;
}

Assembly output:

Ltmp4:
    movsd   LCPI0_0(%rip), %xmm0
    movl    $0, -4(%rbp)
    movl    %edi, -8(%rbp)
    movq    %rsi, -16(%rbp)
Ltmp5:
    movsd   %xmm0, -24(%rbp)
Ltmp6:
    movsd   -24(%rbp), %xmm1
    ucomisd %xmm0, %xmm1
    jne LBB0_2
    jp  LBB0_2
## BB#1:
    movabsq $3, %rax
    cvtsi2sdq   %rax, %xmm0
Ltmp7:
    movsd   %xmm0, -24(%rbp)
Ltmp8:
LBB0_2:
    movl    $0, %eax
    popq    %rbp
    retq

The jne is checking if value != 1.5 and jumping over the assignment, but what is the jp doing in this context?

Thorathoracic answered 28/1, 2015 at 0:50 Comment(1)
I think you told llvm not to make optimized code, or something. There's no way a compiler would ever emit mov $0, %eax instead of xor %eax, %eax with any level of optimization. There's other nasty stuff in that code: movabsq $3, %rax is 10 bytes, compared to movl $3, %eax, but has a completely identical effect.Hemiplegia
I
13

jne is jump if not equal, i.e. jump if the zero flag is not set. jp is jump if parity.

ucomisd is defined to compare two doubles. It will indicate that they are one of four things: unordered, equal, greater than or less than.

The zero flag is set if the numbers are unordered or equal. So the jne avoids the remaining cases of greater than or less than.

Parity is set only if the result is unordered. The jp catches that.

So the two together avoid: unordered, greater than, less than. Leaving only the fourth possibility, of equal.

Illusionist answered 28/1, 2015 at 0:50 Comment(9)
A note on terminology: "Unordered" means that the two arguments have no relaitve order; that is, that at least one of them is a NaN. If they are non-NaNs and equal, that is not the same is "unordered".Waki
That means, however, that the jne catches the case of the numbers being unequal, and the jp catches the case of them being unordered. These are different cases, and not redundant.Waki
@Waki indeed; a failure of terminology here. It's all three flags set for unordered.Illusionist
So to confirm, there are two scenarios where a jump to LBB0_2 would occur: 1) value != 1.5 2) value and 1.5 being unordered (e.g. value being a NaN)Thorathoracic
@Illusionist sorry to nitpick, but I disagree that the jp is redundant. And after experimenting with -Os and -O3, I see no evidence to support your claim that it's optimized out.Lichi
@Lichi can you tell me on what occasion parity will be set but zero will not be set? jp will be effective only if the result of the comparison is 'unordered'. 'Unordered' also sets zero. I'll put what LLVM is generating into my answer as it won't fit here.Illusionist
My understanding is that ucomisd signals that two numbers are equal by setting ZF=1 and PF=0. If both flags are 1, then that indicates that one or both of the numbers are NAN. You can ignore the PF only if you know that neither input to ucomisd is NAN. Note: I didn't try the "unsafe math optimization" option, that may remove the check for PF.Lichi
@Lichi on further inspection, you're right and I was wrong. Answer heavily revised and marked as community wiki to record that it'd be inaccurate to call me the sole owner of it (and, for the record, my optimised code test's attempt to prevent the conditional from being optimised out was flawed).Illusionist
@Illusionist Cheers, you have my respect as colleague and scientist, for the scientist seeks only true knowledge, whereas the politician seeks only to hide mistakes.Lichi

© 2022 - 2024 — McMap. All rights reserved.