aboutsummaryrefslogtreecommitdiffstats
path: root/src/compiler/spirv
diff options
context:
space:
mode:
authorJason Ekstrand <[email protected]>2017-08-16 16:15:23 -0700
committerJason Ekstrand <[email protected]>2017-12-04 09:21:09 -0800
commit94ca8e04adf681b0cad6ade1c9f28856efe35ae6 (patch)
treec23833164f50cab767bfb953f1fb0ac121276847 /src/compiler/spirv
parent0c49aa0624c289164b5501a8724b6d9fdbae7f49 (diff)
spirv: Add vtn_fail and vtn_assert helpers
These helpers are much nicer than just using assert because they don't kill your process. Instead, it longjmps back to spirv_to_nir(), cleans up all the temporary memory, and nicely returns NULL. While crashing is completely OK in the Vulkan world, it's not considered to be quite so nice in GL. This should help us to make SPIR-V parsing much more robust. The one downside here is that vtn_assert is not compiled out in release builds like assert() is so it isn't free. Reviewed-by: Tapani Pälli <[email protected]> Reviewed-by: Ian Romanick <[email protected]>
Diffstat (limited to 'src/compiler/spirv')
-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;