1 /*-------------------------------------------------------------------------
2 *
3 * pg_crc32c_armv8_choose.c
4 * Choose between ARMv8 and software CRC-32C implementation.
5 *
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
9 * (slicing-by-8).
10 *
11 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
13 *
14 *
15 * IDENTIFICATION
16 * src/port/pg_crc32c_armv8_choose.c
17 *
18 *-------------------------------------------------------------------------
19 */
20
21 #ifndef FRONTEND
22 #include "postgres.h"
23 #else
24 #include "postgres_fe.h"
25 #endif
26
27 #include <setjmp.h>
28 #include <signal.h>
29
30 #include "port/pg_crc32c.h"
31
32
33 static sigjmp_buf illegal_instruction_jump;
34
35 /*
36 * Probe by trying to execute pg_comp_crc32c_armv8(). If the instruction
37 * isn't available, we expect to get SIGILL, which we can trap.
38 */
39 static void
illegal_instruction_handler(SIGNAL_ARGS)40 illegal_instruction_handler(SIGNAL_ARGS)
41 {
42 siglongjmp(illegal_instruction_jump, 1);
43 }
44
45 static bool
pg_crc32c_armv8_available(void)46 pg_crc32c_armv8_available(void)
47 {
48 uint64 data = 42;
49 int result;
50
51 /*
52 * Be careful not to do anything that might throw an error while we have
53 * the SIGILL handler set to a nonstandard value.
54 */
55 pqsignal(SIGILL, illegal_instruction_handler);
56 if (sigsetjmp(illegal_instruction_jump, 1) == 0)
57 {
58 /* Rather than hard-wiring an expected result, compare to SB8 code */
59 result = (pg_comp_crc32c_armv8(0, &data, sizeof(data)) ==
60 pg_comp_crc32c_sb8(0, &data, sizeof(data)));
61 }
62 else
63 {
64 /* We got the SIGILL trap */
65 result = -1;
66 }
67 pqsignal(SIGILL, SIG_DFL);
68
69 #ifndef FRONTEND
70 /* We don't expect this case, so complain loudly */
71 if (result == 0)
72 elog(ERROR, "crc32 hardware and software results disagree");
73
74 elog(DEBUG1, "using armv8 crc32 hardware = %d", (result > 0));
75 #endif
76
77 return (result > 0);
78 }
79
80 /*
81 * This gets called on the first call. It replaces the function pointer
82 * so that subsequent calls are routed directly to the chosen implementation.
83 */
84 static pg_crc32c
pg_comp_crc32c_choose(pg_crc32c crc,const void * data,size_t len)85 pg_comp_crc32c_choose(pg_crc32c crc, const void *data, size_t len)
86 {
87 if (pg_crc32c_armv8_available())
88 pg_comp_crc32c = pg_comp_crc32c_armv8;
89 else
90 pg_comp_crc32c = pg_comp_crc32c_sb8;
91
92 return pg_comp_crc32c(crc, data, len);
93 }
94
95 pg_crc32c (*pg_comp_crc32c) (pg_crc32c crc, const void *data, size_t len) = pg_comp_crc32c_choose;
96