summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/compiler/spirv/spirv_to_nir.c20
-rw-r--r--src/compiler/spirv/vtn_private.h47
2 files changed, 67 insertions, 0 deletions
diff --git a/src/compiler/spirv/spirv_to_nir.c b/src/compiler/spirv/spirv_to_nir.c
index 676153dace1..e26775ba1a6 100644
--- a/src/compiler/spirv/spirv_to_nir.c
+++ b/src/compiler/spirv/spirv_to_nir.c
@@ -106,6 +106,20 @@ _vtn_warn(struct vtn_builder *b, const char *file, unsigned line,
va_end(args);
}
+void
+_vtn_fail(struct vtn_builder *b, const char *file, unsigned line,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vtn_log_err(b, NIR_SPIRV_DEBUG_LEVEL_ERROR, "SPIR-V parsing FAILED:\n",
+ file, line, fmt, args);
+ va_end(args);
+
+ longjmp(b->fail_jump, 1);
+}
+
struct spec_constant_value {
bool is_double;
union {
@@ -3418,6 +3432,12 @@ spirv_to_nir(const uint32_t *words, size_t word_count,
b->entry_point_name = entry_point_name;
b->options = options;
+ /* See also _vtn_fail() */
+ if (setjmp(b->fail_jump)) {
+ ralloc_free(b);
+ return NULL;
+ }
+
const uint32_t *word_end = words + word_count;
/* Handle the SPIR-V header (first 4 dwords) */
diff --git a/src/compiler/spirv/vtn_private.h b/src/compiler/spirv/vtn_private.h
index cac4d45864b..d07f6a2beb2 100644
--- a/src/compiler/spirv/vtn_private.h
+++ b/src/compiler/spirv/vtn_private.h
@@ -28,6 +28,8 @@
#ifndef _VTN_PRIVATE_H_
#define _VTN_PRIVATE_H_
+#include <setjmp.h>
+
#include "nir/nir.h"
#include "nir/nir_builder.h"
#include "util/u_dynarray.h"
@@ -49,6 +51,48 @@ void _vtn_warn(struct vtn_builder *b, const char *file, unsigned line,
const char *fmt, ...) PRINTFLIKE(4, 5);
#define vtn_warn(...) _vtn_warn(b, __FILE__, __LINE__, __VA_ARGS__)
+/** Fail SPIR-V parsing
+ *
+ * This function logs an error and then bails out of the shader compile using
+ * longjmp. This being safe relies on two things:
+ *
+ * 1) We must guarantee that setjmp is called after allocating the builder
+ * and setting up b->debug (so that logging works) but before before any
+ * errors have a chance to occur.
+ *
+ * 2) While doing the SPIR-V -> NIR conversion, we need to be careful to
+ * ensure that all heap allocations happen through ralloc and are parented
+ * to the builder. This way they will get properly cleaned up on error.
+ *
+ * 3) We must ensure that _vtn_fail is never called while a mutex lock or a
+ * reference to any other resource is held with the exception of ralloc
+ * objects which are parented to the builder.
+ *
+ * So long as these two things continue to hold, we can easily longjmp back to
+ * spirv_to_nir(), clean up the builder, and return NULL.
+ */
+void _vtn_fail(struct vtn_builder *b, const char *file, unsigned line,
+ const char *fmt, ...) NORETURN PRINTFLIKE(4, 5);
+#define vtn_fail(...) _vtn_fail(b, __FILE__, __LINE__, __VA_ARGS__)
+
+/** Fail if the given expression evaluates to true */
+#define vtn_fail_if(expr, ...) \
+ do { \
+ if (unlikely(expr)) \
+ vtn_fail(__VA_ARGS__); \
+ } while (0)
+
+/** Assert that a condition is true and, if it isn't, vtn_fail
+ *
+ * This macro is transitional only and should not be used in new code. Use
+ * vtn_fail_if and provide a real message instead.
+ */
+#define vtn_assert(expr) \
+ do { \
+ if (!likely(expr)) \
+ vtn_fail("%s", #expr); \
+ } while (0)
+
enum vtn_value_type {
vtn_value_type_invalid = 0,
vtn_value_type_undef,
@@ -478,6 +522,9 @@ struct vtn_decoration {
struct vtn_builder {
nir_builder nb;
+ /* Used by vtn_fail to jump back to the beginning of SPIR-V compilation */
+ jmp_buf fail_jump;
+
const uint32_t *spirv;
nir_shader *shader;