Simon Jakobi pushed to branch wip/sjakobi/T24813 at Glasgow Haskell Compiler / GHC
Commits:
-
4b7193ab
by Simon Jakobi at 2026-09-06T02:26:01+02:00
5 changed files:
- + changelog.d/ncg-x86-subword-popcnt
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- + testsuite/tests/codeGen/should_gen_asm/T24813.asm
- + testsuite/tests/codeGen/should_gen_asm/T24813.cmm
- testsuite/tests/codeGen/should_gen_asm/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +synopsis: The x86 native code generator now emits shorter code for ``popCnt8#``
|
|
| 3 | + and ``popCnt16#`` with ``-msse4.2``
|
|
| 4 | +description:
|
|
| 5 | + Sub-word popcounts now use the 32-bit ``popcnt`` instruction, which
|
|
| 6 | + zero-extends its result for free, and skip zero-extending an argument that
|
|
| 7 | + is already zero-extended, as in ``popCount`` on ``Word8`` or ``Word16``.
|
|
| 8 | + The popcount sequence shrinks from up to five instructions to one to three.
|
|
| 9 | +mrs: !16640
|
|
| 10 | +issues: #24813 |
| ... | ... | @@ -6739,20 +6739,23 @@ genPopCnt bid width dst src = do |
| 6739 | 6739 | |
| 6740 | 6740 | True -> do
|
| 6741 | 6741 | code_src <- getAnyReg src
|
| 6742 | - src_r <- getNewRegNat format
|
|
| 6742 | + -- Sub-word popcounts use 32-bit POPCNT, whose write zero-extends the
|
|
| 6743 | + -- result to the full register.
|
|
| 6744 | + src_r <- getNewRegNat (max II32 format)
|
|
| 6743 | 6745 | let dst_r = getRegisterReg platform (CmmLocal dst)
|
| 6746 | + -- A sub-word argument is a full Word# whose upper bits may be
|
|
| 6747 | + -- dirty, so zero-extend it unless it visibly already is.
|
|
| 6748 | + zx_src
|
|
| 6749 | + | CmmMachOp (MO_UU_Conv from _) [_] <- src, from <= width
|
|
| 6750 | + = nilOL
|
|
| 6751 | + | otherwise
|
|
| 6752 | + = unitOL (MOVZxL format (OpReg src_r) (OpReg src_r))
|
|
| 6744 | 6753 | return $ code_src src_r `appOL`
|
| 6745 | - (if width == W8 then
|
|
| 6746 | - -- The POPCNT instruction doesn't take a r/m8
|
|
| 6747 | - unitOL (MOVZxL II8 (OpReg src_r) (OpReg src_r)) `appOL`
|
|
| 6748 | - unitOL (POPCNT II16 (OpReg src_r) dst_r)
|
|
| 6749 | - else
|
|
| 6750 | - unitOL (POPCNT format (OpReg src_r) dst_r)) `appOL`
|
|
| 6751 | 6754 | (if width == W8 || width == W16 then
|
| 6752 | - -- We used a 16-bit destination register above,
|
|
| 6753 | - -- so zero-extend
|
|
| 6754 | - unitOL (MOVZxL II16 (OpReg dst_r) (OpReg dst_r))
|
|
| 6755 | - else nilOL)
|
|
| 6755 | + zx_src `appOL`
|
|
| 6756 | + unitOL (POPCNT II32 (OpReg src_r) dst_r)
|
|
| 6757 | + else
|
|
| 6758 | + unitOL (POPCNT format (OpReg src_r) dst_r))
|
|
| 6756 | 6759 | |
| 6757 | 6760 | False ->
|
| 6758 | 6761 | -- generate C call to hs_popcntN in ghc-prim
|
| 1 | +popcnt8:
|
|
| 2 | + movzbl
|
|
| 3 | + popcnt
|
|
| 4 | + movq
|
|
| 5 | + jmp
|
|
| 6 | +popcnt16:
|
|
| 7 | + movzwl
|
|
| 8 | + popcnt
|
|
| 9 | + movq
|
|
| 10 | + jmp |
| 1 | +#include "Cmm.h"
|
|
| 2 | + |
|
| 3 | +// Popcount of a zero-extended sub-word value should be a single
|
|
| 4 | +// zero-extending load followed by one POPCNT (#24813).
|
|
| 5 | + |
|
| 6 | +popcnt8 (W_ p) {
|
|
| 7 | + W_ r;
|
|
| 8 | + (r) = prim %popcnt8(%zx64(bits8[p]));
|
|
| 9 | + return (r);
|
|
| 10 | +}
|
|
| 11 | + |
|
| 12 | +popcnt16 (W_ p) {
|
|
| 13 | + W_ r;
|
|
| 14 | + (r) = prim %popcnt16(%zx64(bits16[p]));
|
|
| 15 | + return (r);
|
|
| 16 | +} |
| ... | ... | @@ -27,6 +27,7 @@ test('T25233', [unless(arch('x86_64'), skip), |
| 27 | 27 | when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-O'])
|
| 28 | 28 | test('T25233b', [unless(arch('x86_64'), skip),
|
| 29 | 29 | when(unregisterised(), skip)], compile_grep_asm, ['cmm', True, ''])
|
| 30 | +test('T24813', is_amd64_codegen, compile_cmp_asm, ['cmm', '-msse4.2'])
|
|
| 30 | 31 | is_aarch64_codegen = [
|
| 31 | 32 | unless(arch('aarch64'), skip),
|
| 32 | 33 | when(unregisterised(), skip),
|