llvm / llvm/llvm-project

CRC intrinsics on old cpus won't compile despite sse4.2 targeted attribute when -march=native is set

Open
#194,125 1 comment 0 reactions 0 assignees View on GitHub
backend:X86
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Due to how it's currently implemented, the flag `-mno-crc32` has the peculiar side-effect of changing the semantics of `__attribute__((target("sse4.2")))` from implicitly including crc32 to not implicitly including it. This flag I believe was added with the release of 14.0, and in GCC it was in 12.1.

There is a specific scenario where the mere existence of this flag breaks a build environment that used to work before:
- building old code that relies on `__attribute__((target("sse4.2")))` to force inclusion of otherwise unsupported crc32 intrinsics
- building on an old cpu architecture that doesn't support `crc32` natively
- using -march=native for precise build targeting

What happens is that -march=native brings in -mno-crc32, which in turn alters the semantics of \_\_attribute\_\_((target("sse4.2"))) to no longer enable crc32 support, and the build falls apart due to a target specific option mismatch.

The real world use case is mysql-server which has been relying on this since 2017: https://github.com/mysql/mysql-server/commit/01db23714718f19d22686f0c4de750d69149d0d5. They force inclusion of the crc bytecode via attribute and then use runtime hardware checks to decide whether to execute it or use a compatible fallback. On intel atom (-target-cpu bonnell -target-feature -sse4.2 -target-feature -crc32) on FreeBSD, this breaks the build, as reported in [freebsd#275404](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=275404). The last successful unpatched build was possible on FreeBSD 13.1 (2022) which used llvm 13.0.

```
storage/innobase/ut/crc32.cc:453:10:
error: always_inline function '_mm_crc32_u32' requires target feature 'crc32',
but would be inlined into function 'update' that is compiled without support for 'crc32'
return _mm_crc32_u32(crc, data);
^
```

This can be fixed by updating the code to properly target feature `crc32`. This requires dropping support for old versions and any other compilers that decided not to separate it out. It also might be possible to fudge it by adding -mcrc32 to cflags and hoping it appears last in the list and cancels out -mno-crc32. On the llvm side, it might be worth considering to remove the side-effect shenanigans. This would remove the ability to build sse4.2 without crc32 (a dubious use case). I've read that the original motivation was `-mgeneral-regs-only` but that one is the reverse scenario. Another thought I had was to stop adding -mno-crc32 as an expansion of -march=native and have -mno-sse4.2 handle it the old way. Perhaps give the flag a different, more dangerous looking name. I'm also curious to know if the above breakage was considered and deemed acceptable. Finally, I acknowledge that this is an extremely niche case, so maybe just acknowledging it is the way to go.

Contributor guide

Open the contributing guide

Research direction

Start with the reported failure in storage/innobase/ut/crc32.cc at _mm_crc32_u32, then trace how -march=native, -mno-crc32, and target("sse4.2") are interpreted by the compiler. Compare the possible target-feature semantics and identify an LLVM regression test or entry point that demonstrates the chosen behavior; done means the old-code scenario has a defined, tested outcome.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.