summaryrefslogtreecommitdiffstats
path: root/src/port/pg_crc32c_armv8_choose.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/port/pg_crc32c_armv8_choose.c')
-rw-r--r--src/port/pg_crc32c_armv8_choose.c95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/port/pg_crc32c_armv8_choose.c b/src/port/pg_crc32c_armv8_choose.c
new file mode 100644
index 0000000..6443e6a
--- /dev/null
+++ b/src/port/pg_crc32c_armv8_choose.c
@@ -0,0 +1,95 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_crc32c_armv8_choose.c
+ * Choose between ARMv8 and software CRC-32C implementation.
+ *
+ * On first call, checks if the CPU we're running on supports the ARMv8
+ * CRC Extension. If it does, use the special instructions for CRC-32C
+ * computation. Otherwise, fall back to the pure software implementation
+ * (slicing-by-8).
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/port/pg_crc32c_armv8_choose.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <setjmp.h>
+#include <signal.h>
+
+#include "port/pg_crc32c.h"
+
+
+static sigjmp_buf illegal_instruction_jump;
+
+/*
+ * Probe by trying to execute pg_comp_crc32c_armv8(). If the instruction
+ * isn't available, we expect to get SIGILL, which we can trap.
+ */
+static void
+illegal_instruction_handler(SIGNAL_ARGS)
+{
+ siglongjmp(illegal_instruction_jump, 1);
+}
+
+static bool
+pg_crc32c_armv8_available(void)
+{
+ uint64 data = 42;
+ int result;
+
+ /*
+ * Be careful not to do anything that might throw an error while we have
+ * the SIGILL handler set to a nonstandard value.
+ */
+ pqsignal(SIGILL, illegal_instruction_handler);
+ if (sigsetjmp(illegal_instruction_jump, 1) == 0)
+ {
+ /* Rather than hard-wiring an expected result, compare to SB8 code */
+ result = (pg_comp_crc32c_armv8(0, &data, sizeof(data)) ==
+ pg_comp_crc32c_sb8(0, &data, sizeof(data)));
+ }
+ else
+ {
+ /* We got the SIGILL trap */
+ result = -1;
+ }
+ pqsignal(SIGILL, SIG_DFL);
+
+#ifndef FRONTEND
+ /* We don't expect this case, so complain loudly */
+ if (result == 0)
+ elog(ERROR, "crc32 hardware and software results disagree");
+
+ elog(DEBUG1, "using armv8 crc32 hardware = %d", (result > 0));
+#endif
+
+ return (result > 0);
+}
+
+/*
+ * This gets called on the first call. It replaces the function pointer
+ * so that subsequent calls are routed directly to the chosen implementation.
+ */
+static pg_crc32c
+pg_comp_crc32c_choose(pg_crc32c crc, const void *data, size_t len)
+{
+ if (pg_crc32c_armv8_available())
+ pg_comp_crc32c = pg_comp_crc32c_armv8;
+ else
+ pg_comp_crc32c = pg_comp_crc32c_sb8;
+
+ return pg_comp_crc32c(crc, data, len);
+}
+
+pg_crc32c (*pg_comp_crc32c) (pg_crc32c crc, const void *data, size_t len) = pg_comp_crc32c_choose;