CRC intrinsics on old cpus won't compile despite sse4.2 targeted attribute when -march=native is set
- 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
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