Skip to content

Bad codegen for _mm_packus_epi16 #2208

Description

@hsivonen

Consider https://rust.godbolt.org/z/dY8schnG4

Input:

#![feature(portable_simd)]
use std::simd::ToBytes;
use core::simd::u8x16;
use core::simd::u16x8;
use core::arch::x86_64::_mm_packus_epi16;

#[inline(always)]
pub fn simd_pack_sse2(a: u16x8, b: u16x8) -> u8x16 {
    unsafe {
        _mm_packus_epi16(a.into(), b.into()).into()
    }
}

#[inline(always)]
pub fn simd_pack_portable(a: u16x8, b: u16x8) -> u8x16 {
    let first: u8x16 = a.to_le_bytes();
    let second: u8x16 = b.to_le_bytes();
    let (ret, _) = first.deinterleave(second);
    ret
}

#[inline(always)]
fn split_u16_stride(stride: &[u16; 16]) -> (&[u16; 8], &[u16; 8]) {
    let (chunks, _) = stride.as_chunks::<8>();
    (&chunks[0], &chunks[1])
}

#[unsafe(no_mangle)]
pub fn pack_portable(src: &[u16; 16], dst: &mut [u8; 16]) {
    let (a, b) = split_u16_stride(src);
    let packed = simd_pack_portable((*a).into(), (*b).into());
    *dst = packed.to_array();
}

#[unsafe(no_mangle)]
pub fn pack_sse2(src: &[u16; 16], dst: &mut [u8; 16]) {
    let (a, b) = split_u16_stride(src);
    let packed = simd_pack_sse2((*a).into(), (*b).into());
    *dst = packed.to_array();
}

Output:

.LCPI0_0:
        .short  255
        .short  255
        .short  255
        .short  255
        .short  255
        .short  255
        .short  255
        .short  255
pack_portable:
        movdqu  xmm0, xmmword ptr [rdi]
        movdqu  xmm1, xmmword ptr [rdi + 16]
        movdqa  xmm2, xmmword ptr [rip + .LCPI0_0]
        pand    xmm1, xmm2
        pand    xmm0, xmm2
        packuswb        xmm0, xmm1
        movdqu  xmmword ptr [rsi], xmm0
        ret

.LCPI1_0:
        .short  255
        .short  255
        .short  255
        .short  255
        .short  255
        .short  255
        .short  255
        .short  255
pack_sse2:
        movdqu  xmm0, xmmword ptr [rdi]
        movdqu  xmm1, xmmword ptr [rdi + 16]
        pxor    xmm2, xmm2
        pmaxsw  xmm0, xmm2
        movdqa  xmm3, xmmword ptr [rip + .LCPI1_0]
        pminsw  xmm0, xmm3
        pmaxsw  xmm1, xmm2
        pminsw  xmm1, xmm3
        packuswb        xmm0, xmm1
        movdqu  xmmword ptr [rsi], xmm0
        ret

Expected pack_sse2 to compile like this instead:

pack_sse2:
        movdqu  xmm0, xmmword ptr [rdi]
        movdqu  xmm1, xmmword ptr [rdi + 16]
        packuswb        xmm0, xmm1
        movdqu  xmmword ptr [rsi], xmm0
        ret

By code inspection, #2033 is the likely regressor. It seems problematic that it landed without tests to ensure that the optimizer actually recognizes the pattern.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions