[Mesa-dev] [PATCH 7/9] spirv: Add vtn_fail and vtn_assert helpers

Jason Ekstrand jason at jlekstrand.net
Thu Aug 17 17:22:21 UTC 2017


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.
---
 src/compiler/spirv/spirv_to_nir.c | 20 ++++++++++++++++++++
 src/compiler/spirv/vtn_private.h  | 31 +++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/src/compiler/spirv/spirv_to_nir.c b/src/compiler/spirv/spirv_to_nir.c
index e59f2b2..af542e8 100644
--- a/src/compiler/spirv/spirv_to_nir.c
+++ b/src/compiler/spirv/spirv_to_nir.c
@@ -104,6 +104,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 {
@@ -3420,6 +3434,12 @@ spirv_to_nir(const uint32_t *words, size_t word_count,
    b->entry_point_name = entry_point_name;
    b->ext = ext;
 
+   /* 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 3eb601d..f640289 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,32 @@ 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.
+ *
+ * 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__)
+
+#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,
@@ -474,6 +502,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;
-- 
2.5.0.400.gff86faf



More information about the mesa-dev mailing list