aboutsummaryrefslogtreecommitdiff
path: root/src/backend/jit/llvm/llvmjit_error.cpp
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2018-03-21 19:28:28 -0700
committerAndres Freund <andres@anarazel.de>2018-03-21 19:28:28 -0700
commit432bb9e04da4d4a1799b1fe7c723b975cb070c43 (patch)
tree72e1dbf4e401521109fe1081aac4c4c6d28a034f /src/backend/jit/llvm/llvmjit_error.cpp
parent4317cc68a284f041abc583ced4ef7ede2f73fb51 (diff)
downloadpostgresql-432bb9e04da4d4a1799b1fe7c723b975cb070c43.tar.gz
postgresql-432bb9e04da4d4a1799b1fe7c723b975cb070c43.zip
Basic JIT provider and error handling infrastructure.
This commit introduces: 1) JIT provider abstraction, which allows JIT functionality to be implemented in separate shared libraries. That's desirable because it allows to install JIT support as a separate package, and because it allows experimentation with different forms of JITing. 2) JITContexts which can be, using functions introduced in follow up commits, used to emit JITed functions, and have them be cleaned up on error. 3) The outline of a LLVM JIT provider, which will be fleshed out in subsequent commits. Documentation for GUCs added, and for JIT in general, will be added in later commits. Author: Andres Freund, with architectural input from Jeff Davis Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de
Diffstat (limited to 'src/backend/jit/llvm/llvmjit_error.cpp')
-rw-r--r--src/backend/jit/llvm/llvmjit_error.cpp141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/backend/jit/llvm/llvmjit_error.cpp b/src/backend/jit/llvm/llvmjit_error.cpp
new file mode 100644
index 00000000000..edc1c479d01
--- /dev/null
+++ b/src/backend/jit/llvm/llvmjit_error.cpp
@@ -0,0 +1,141 @@
+/*-------------------------------------------------------------------------
+ *
+ * llvmjit_error.cpp
+ * LLVM error related handling that requires interfacing with C++
+ *
+ * Unfortunately neither (re)setting the C++ new handler, nor the LLVM OOM
+ * handler are exposed to C. Therefore this file wraps the necesary code.
+ *
+ * Copyright (c) 2016-2018, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/jit/llvm/llvmjit_error.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+extern "C"
+{
+#include "postgres.h"
+}
+
+#include <llvm/Support/ErrorHandling.h>
+
+#include "jit/llvmjit.h"
+
+
+static int fatal_new_handler_depth = 0;
+static std::new_handler old_new_handler = NULL;
+
+static void fatal_system_new_handler(void);
+#if LLVM_VERSION_MAJOR > 4
+static void fatal_llvm_new_handler(void *user_data, const std::string& reason, bool gen_crash_diag);
+#endif
+static void fatal_llvm_error_handler(void *user_data, const std::string& reason, bool gen_crash_diag);
+
+
+/*
+ * Enter a section in which C++ and LLVM errors are treated as FATAL errors.
+ *
+ * This is necessary for LLVM as LLVM's error handling for such cases
+ * (exit()ing, throwing std::bad_alloc() if compiled with exceptions, abort())
+ * isn't compatible with postgres error handling. Thus in section where LLVM
+ * code, not LLVM generated functions!, is executing, standard new, LLVM OOM
+ * and LLVM fatal errors (some OOM errors masquerade as those) are redirected
+ * to our own error handlers.
+ *
+ * These error handlers FATAL, because there's no reliable way from within
+ * LLVM to throw an error that's guaranteed not to corrupt LLVM's state.
+ *
+ * To avoid disturbing extensions using C++ and/or LLVM, these handlers are
+ * unset when not executing LLVM code. There is no need to call
+ * llvm_leave_fatal_on_oom() when ERRORing out, error recovery resets the
+ * handlers in that case.
+ */
+void
+llvm_enter_fatal_on_oom(void)
+{
+ if (fatal_new_handler_depth == 0)
+ {
+ old_new_handler = std::set_new_handler(fatal_system_new_handler);
+#if LLVM_VERSION_MAJOR > 4
+ llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler);
+#endif
+ llvm::install_fatal_error_handler(fatal_llvm_error_handler);
+ }
+ fatal_new_handler_depth++;
+}
+
+/*
+ * Leave fatal error section started with llvm_enter_fatal_on_oom().
+ */
+void
+llvm_leave_fatal_on_oom(void)
+{
+ fatal_new_handler_depth--;
+ if (fatal_new_handler_depth == 0)
+ {
+ std::set_new_handler(old_new_handler);
+#if LLVM_VERSION_MAJOR > 4
+ llvm::remove_bad_alloc_error_handler();
+#endif
+ llvm::remove_fatal_error_handler();
+ }
+}
+
+/*
+ * Reset fatal error handling. This should only be called in error recovery
+ * loops like PostgresMain()'s.
+ */
+void
+llvm_reset_after_error(void)
+{
+ if (fatal_new_handler_depth != 0)
+ {
+ std::set_new_handler(old_new_handler);
+#if LLVM_VERSION_MAJOR > 4
+ llvm::remove_bad_alloc_error_handler();
+#endif
+ llvm::remove_fatal_error_handler();
+ }
+ fatal_new_handler_depth = 0;
+}
+
+void
+llvm_assert_in_fatal_section(void)
+{
+ Assert(fatal_new_handler_depth > 0);
+}
+
+static void
+fatal_system_new_handler(void)
+{
+ ereport(FATAL,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory"),
+ errdetail("while in LLVM")));
+}
+
+#if LLVM_VERSION_MAJOR > 4
+static void
+fatal_llvm_new_handler(void *user_data,
+ const std::string& reason,
+ bool gen_crash_diag)
+{
+ ereport(FATAL,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory"),
+ errdetail("While in LLVM: %s", reason.c_str())));
+}
+#endif
+
+static void
+fatal_llvm_error_handler(void *user_data,
+ const std::string& reason,
+ bool gen_crash_diag)
+{
+ ereport(FATAL,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("fatal llvm error: %s",
+ reason.c_str())));
+}