rust-lang / rust-lang/rust

std::arch::x86_64::_mm_mulhi_epu16(vector, _mm_set1_epi16(scalar)) generates slow emulated path instead of pmulhuw (regressed in rustc 1.75)

Open
#159,474 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-LLVM C-bug needs-triage P-medium regression-from-stable-to-stable
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Yes, this apparently regressed more than 2 years ago. The following example on godbolt: https://godbolt.org/z/4bY5eM9az

Code
use std::arch::x86_64::*;

#[no_mangle]
#[target_feature(enable = "sse2")]
pub unsafe fn f(buf: &mut [__m128i], factor: i16) {
    let factor = _mm_set1_epi16(factor);
    for i in 0..buf.len() {
        buf[i] = _mm_mulhi_epu16(buf[i], factor);
    }
}
Version it worked on: rustc 1.74

Broadcasts factor in xmm0 before the loop, then uses the desired pmulhuw for mulhi

f:
        test    rsi, rsi
        je      .LBB0_6
        movd    xmm0, edx
        pshuflw xmm0, xmm0, 0
        pshufd  xmm0, xmm0, 0
        mov     eax, esi
        and     eax, 3
        cmp     rsi, 4
        jae     .LBB0_7
        xor     ecx, ecx
        jmp     .LBB0_3
.LBB0_7:
        and     rsi, -4
        lea     rdx, [rdi + 48]
        xor     ecx, ecx
.LBB0_8:
        movdqa  xmm1, xmmword ptr [rdx - 48]
        pmulhuw xmm1, xmm0
        movdqa  xmmword ptr [rdx - 48], xmm1
        movdqa  xmm1, xmmword ptr [rdx - 32]
        pmulhuw xmm1, xmm0
        movdqa  xmmword ptr [rdx - 32], xmm1
        movdqa  xmm1, xmmword ptr [rdx - 16]
        pmulhuw xmm1, xmm0
        movdqa  xmmword ptr [rdx - 16], xmm1
        add     rcx, 4
        movdqa  xmm1, xmmword ptr [rdx]
        pmulhuw xmm1, xmm0
        movdqa  xmmword ptr [rdx], xmm1
        add     rdx, 64
        cmp     rsi, rcx
        jne     .LBB0_8
.LBB0_3:
        test    rax, rax
        je      .LBB0_6
        shl     rcx, 4
        add     rdi, rcx
        shl     rax, 4
        xor     ecx, ecx
.LBB0_5:
        movdqa  xmm1, xmmword ptr [rdi + rcx]
        pmulhuw xmm1, xmm0
        movdqa  xmmword ptr [rdi + rcx], xmm1
        add     rcx, 16
        cmp     rax, rcx
        jne     .LBB0_5
.LBB0_6:
        ret
Version with regression: rustc 1.75

Slow code that emulates pmulhuw through the definition: ((vector_u16 as u32) * (factor_u16 as u32)) >> 16

.LCPI0_0:
        .short  65535
        .short  0
        .short  65535
        .short  0
        .short  65535
        .short  0
        .short  65535
        .short  0
f:
        test    rsi, rsi
        je      .LBB0_3
        movd    xmm0, edx
        pshuflw xmm0, xmm0, 0
        pshufd  xmm1, xmm0, 0
        pxor    xmm2, xmm2
        punpcklwd       xmm0, xmm2
        pand    xmm1, xmmword ptr [rip + .LCPI0_0]
        pshufd  xmm3, xmm1, 245
        pshufd  xmm4, xmm0, 245
.LBB0_2:
        movdqa  xmm5, xmmword ptr [rdi]
        movdqa  xmm6, xmm5
        punpckhwd       xmm6, xmm2
        punpcklwd       xmm5, xmm2
        pshufd  xmm7, xmm5, 245
        pmuludq xmm5, xmm1
        pshufd  xmm5, xmm5, 232
        pmuludq xmm7, xmm3
        pshufd  xmm7, xmm7, 232
        punpckldq       xmm5, xmm7
        pshufd  xmm7, xmm6, 245
        pmuludq xmm6, xmm0
        pshufd  xmm6, xmm6, 232
        pmuludq xmm7, xmm4
        pshufd  xmm7, xmm7, 232
        punpckldq       xmm6, xmm7
        psrad   xmm6, 16
        psrad   xmm5, 16
        packssdw        xmm5, xmm6
        movdqa  xmmword ptr [rdi], xmm5
        add     rdi, 16
        dec     rsi
        jne     .LBB0_2
.LBB0_3:
        ret
Related issues

https://github.com/rust-lang/rust/issues/124216
https://github.com/rust-lang/rust/issues/130782
https://github.com/rust-lang/rust/issues/138725

All of these seem to have been concluded with a resolved LLVM issue already. In the same order:

https://github.com/llvm/llvm-project/issues/132166
https://github.com/llvm/llvm-project/issues/109790
https://github.com/llvm/llvm-project/issues/132166 (duplicate)

Causes

I did not bisect.

One note: stdarch commit 7abc64d5 https://github.com/rust-lang/stdarch/commit/7abc64d599fcc93719f17ee08a03390d81424800 changed the implementation of _mm_mulhi_epu16 from #[link_name = "llvm.x86.sse2.pmulhu.w"] to using Rust portable SIMD.

Manually reverting that by using a _mm_mulhi_epu16 backed by #[link_name = "llvm.x86.sse2.pmulhu.w"] fixes this issue at least on current nightly. Godbolt: https://godbolt.org/z/zqc1fMvjM

Moreover using a runtime variable scalar factor for one of the operand of mulhi_epu16 seems to be necessary to trigger this bad codegen for mulhi_epu16. Doing mulhi_epu16 in a loop on 2 &[__m128i] slices works fine.

Workaround

Use inline assembly for the problematic instructions...

Use case

Fixed point image signal processing which generated optimal code up to rustc 1.72, see godbolt: https://godbolt.org/z/bh6zTqWa3 (This example is further complicated by _mm_unpack*_epi8 breaking in rustc 1.73 due to a different LLVM regression which was fixed first in the LLVM of rustc 1.95)

@rustbot modify labels: +regression-from-stable-to-stable -regression-untriaged

EDIT: fix a copy-paste mistake

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the _mm_mulhi_epu16 implementation change in stdarch commit 7abc64d5 and reproduce the Rust 1.75/current-nightly assembly from the issue's Godbolt example. Compare the portable-SIMD path with the llvm.x86.sse2.pmulhu.w declaration and review the related LLVM issues. Done means the variable-broadcast loop lowers to pmulhuw rather than the emulation sequence, with regression coverage.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.