1 /*-------------------------------------------------------------------------
3 * pg_crc32c_armv8_choose.c
4 * Choose between ARMv8 and software CRC-32C implementation.
6 * On first call, checks if the CPU we're running on supports the ARMv8
7 * CRC Extension. If it does, use the special instructions for CRC-32C
8 * computation. Otherwise, fall back to the pure software implementation
11 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
16 * src/port/pg_crc32c_armv8_choose.c
18 *-------------------------------------------------------------------------
24 #include "postgres_fe.h"
27 #if defined(HAVE_ELF_AUX_INFO) || defined(HAVE_GETAUXVAL)
29 #if defined(__linux__) && !defined(__aarch64__) && !defined(HWCAP2_CRC32)
30 #include <asm/hwcap.h>
34 #if defined(__NetBSD__)
35 #include <sys/sysctl.h>
36 #if defined(__aarch64__)
37 #include <aarch64/armreg.h>
41 #include "port/pg_crc32c.h"
44 pg_crc32c_armv8_available(void)
46 #if defined(HAVE_ELF_AUX_INFO)
50 return elf_aux_info(AT_HWCAP
, &value
, sizeof(value
)) == 0 &&
51 (value
& HWCAP_CRC32
) != 0;
53 return elf_aux_info(AT_HWCAP2
, &value
, sizeof(value
)) == 0 &&
54 (value
& HWCAP2_CRC32
) != 0;
56 #elif defined(HAVE_GETAUXVAL)
58 return (getauxval(AT_HWCAP
) & HWCAP_CRC32
) != 0;
60 return (getauxval(AT_HWCAP2
) & HWCAP2_CRC32
) != 0;
62 #elif defined(__NetBSD__)
64 * On NetBSD we can read the Instruction Set Attribute Registers via
65 * sysctl. For doubtless-historical reasons the sysctl interface is
66 * completely different on 64-bit than 32-bit, but the underlying
67 * registers contain the same fields.
69 #define ISAR0_CRC32_BITPOS 16
70 #define ISAR0_CRC32_BITWIDTH 4
71 #define WIDTHMASK(w) ((1 << (w)) - 1)
72 #define SYSCTL_CPU_ID_MAXSIZE 64
75 uint64 sysctlbuf
[SYSCTL_CPU_ID_MAXSIZE
];
76 #if defined(__aarch64__)
77 /* We assume cpu0 is representative of all the machine's CPUs. */
78 const char *path
= "machdep.cpu0.cpu_id";
79 size_t expected_len
= sizeof(struct aarch64_sysctl_cpu_id
);
80 #define ISAR0 ((struct aarch64_sysctl_cpu_id *) sysctlbuf)->ac_aa64isar0
82 const char *path
= "machdep.id_isar";
83 size_t expected_len
= 6 * sizeof(int);
84 #define ISAR0 ((int *) sysctlbuf)[5]
88 /* Fetch the appropriate set of register values. */
89 len
= sizeof(sysctlbuf
);
90 memset(sysctlbuf
, 0, len
);
91 if (sysctlbyname(path
, sysctlbuf
, &len
, NULL
, 0) != 0)
92 return false; /* perhaps kernel is 64-bit and we aren't? */
93 if (len
!= expected_len
)
94 return false; /* kernel API change? */
96 /* Fetch the CRC32 field from ISAR0. */
97 fld
= (ISAR0
>> ISAR0_CRC32_BITPOS
) & WIDTHMASK(ISAR0_CRC32_BITWIDTH
);
100 * Current documentation defines only the field values 0 (No CRC32) and 1
101 * (CRC32B/CRC32H/CRC32W/CRC32X/CRC32CB/CRC32CH/CRC32CW/CRC32CX). Assume
102 * that any future nonzero value will be a superset of 1.
111 * This gets called on the first call. It replaces the function pointer
112 * so that subsequent calls are routed directly to the chosen implementation.
115 pg_comp_crc32c_choose(pg_crc32c crc
, const void *data
, size_t len
)
117 if (pg_crc32c_armv8_available())
118 pg_comp_crc32c
= pg_comp_crc32c_armv8
;
120 pg_comp_crc32c
= pg_comp_crc32c_sb8
;
122 return pg_comp_crc32c(crc
, data
, len
);
125 pg_crc32c (*pg_comp_crc32c
) (pg_crc32c crc
, const void *data
, size_t len
) = pg_comp_crc32c_choose
;