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