Remix.run Logo
▲ orlp a day ago

You can do it with just 3 instructions for IEEE 754-2019 minimumNumber (ignores NaN):

        vminpd          ymm2, ymm1, ymm0
        vcmpunordpd     ymm0, ymm0, ymm0
        vblendvpd       ymm0, ymm2, ymm1, ymm0
If you want proper IEEE 754-2019 minimum (propagate NaN, -0.0 < +0.0, NaN bitpattern picked in the usual way) you can do it in 6:

        vminpd          ymm1, ymm0, ymm1
        vbroadcastsd    ymm2, qword ptr [rip + .LCPI0_0]
        vandpd          ymm2, ymm0, ymm2
        vorpd           ymm1, ymm2, ymm1
        vcmpunordpd     ymm2, ymm0, ymm0
        vblendvpd       ymm0, ymm1, ymm0, ymm2
I personally find this a load of nonsense I don't care about.

If you want propagating NaNs but don't care about signed zero or NaN payload/sign, you can use

        vminpd  ymm2, ymm0, ymm1
        vminpd  ymm1, ymm1, ymm0
        vorpd   ymm0, ymm1, ymm2
What I do in Polars is a bit different, there for propagating NaNs I do

    if (self < other) | self.is_nan() { self } else { other}
this isn't fully optimal on x86-64 but it's fairly simple and autovectorizes decently on various platforms, here's AVX2:

        vcmpltpd        ymm2, ymm0, ymm1
        vcmpunordpd     ymm3, ymm0, ymm0
        vorpd           ymm2, ymm3, ymm2
        vblendvpd       ymm0, ymm1, ymm0, ymm2
▲dzaima 3 hours ago | parent [-]

That vminpd+vminpd+vorpd actually does handle signed zero properly! Screws up NaN payloads to the max though. Can be easily extended to canonicalize the NaN with 2 instrs + constant though of course. (which ends up at the same number of instrs as your proper impl (albeit with worse port distribution and latency), but you get to have a canonical NaN!)

Hit upon https://github.com/llvm/llvm-project/issues/217376 while playing around with proper minimumnum, failing to SMT-verify whatever version of LLVM I had; did find a funky working 6-instr (+ constant) version though:

    vpandn      ymm2, ymm1, ymm0
    vpcmpeqd    ymm2, ymm2, 0x80000000 # whether ymm0 is -0 and ymm1 is +0 (or other cases that magically don't cause issues)
    vcmpltpd    ymm3, ymm0, ymm1
    vcmpunordpd ymm3, ymm3, ymm1 # regular NaN-is-larger ymm0<ymm1
    vpor        ymm2, ymm2, ymm3
    vpblendvb   ymm0, ymm1, ymm0, ymm2