diff options
-rw-r--r-- | src/compiler/spirv/spirv_to_nir.c | 20 | ||||
-rw-r--r-- | src/compiler/spirv/vtn_private.h | 47 |
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; |