[Mesa-dev] [PATCH v4 03/10] nir/spirv: add gl_spirv_validation method
Jason Ekstrand
jason at jlekstrand.net
Mon Jan 15 16:28:11 UTC 2018
On January 15, 2018 06:46:13 Alejandro PiƱeiro <apinheiro at igalia.com> wrote:
> ARB_gl_spirv adds the ability to use SPIR-V binaries, and a new
> method, glSpecializeShader. From OpenGL 4.6 spec, section 7.2.1
> "Shader Specialization", error table:
>
> INVALID_VALUE is generated if <pEntryPoint> does not name a valid
> entry point for <shader>.
>
> INVALID_VALUE is generated if any element of <pConstantIndex>
> refers to a specialization constant that does not exist in the
> shader module contained in <shader>.""
>
> But we are not really interested on creating the nir shader at that
> point, and adding nir structures on the gl_program, so at that point
> we are just interested on the error checking.
>
> So we add a new method focused on just checking those errors. It still
> needs to parse the binary, but skips what it is not needed, and
> doesn't create the nir shader.
>
> v2: rebase update (spirv_to_nir options added, changes on the warning
> logging, and others)
> v3: include passing options on common initialization, doesn't call
> setjmp on common_initialization
> ---
> src/compiler/spirv/nir_spirv.h | 5 +
> src/compiler/spirv/spirv_to_nir.c | 191 ++++++++++++++++++++++++++++++++++----
> 2 files changed, 180 insertions(+), 16 deletions(-)
>
> diff --git a/src/compiler/spirv/nir_spirv.h b/src/compiler/spirv/nir_spirv.h
> index a2c40e57d18..d2766abb7f9 100644
> --- a/src/compiler/spirv/nir_spirv.h
> +++ b/src/compiler/spirv/nir_spirv.h
> @@ -41,6 +41,7 @@ struct nir_spirv_specialization {
> uint32_t data32;
> uint64_t data64;
> };
> + bool defined_on_module;
> };
>
> enum nir_spirv_debug_level {
> @@ -69,6 +70,10 @@ struct spirv_to_nir_options {
> } debug;
> };
>
> +bool gl_spirv_validation(const uint32_t *words, size_t word_count,
> + struct nir_spirv_specialization *spec, unsigned
> num_spec,
> + gl_shader_stage stage, const char *entry_point_name);
> +
> nir_function *spirv_to_nir(const uint32_t *words, size_t word_count,
> struct nir_spirv_specialization *specializations,
> unsigned num_specializations,
> diff --git a/src/compiler/spirv/spirv_to_nir.c
> b/src/compiler/spirv/spirv_to_nir.c
> index c6df764682e..2143cd9df31 100644
> --- a/src/compiler/spirv/spirv_to_nir.c
> +++ b/src/compiler/spirv/spirv_to_nir.c
> @@ -1332,6 +1332,7 @@ spec_constant_decoration_cb(struct vtn_builder *b,
> struct vtn_value *v,
> const_value->data64 = b->specializations[i].data64;
> else
> const_value->data32 = b->specializations[i].data32;
> + b->specializations[i].defined_on_module = true;
> return;
> }
> }
> @@ -1366,7 +1367,13 @@ handle_workgroup_size_decoration_cb(struct
> vtn_builder *b,
> const struct vtn_decoration *dec,
> void *data)
> {
> + /* This can happens if we are gl_spirv_validation. We can return safely, as
> + * we don't need the workgroup info for such validation. */
> + if (b->shader == NULL)
> + return;
I don't think that re-using these two functions is really buying us
anything. We could just make spec constant validation versions that just
do what's needed there.
> +
> vtn_assert(member == -1);
> +
> if (dec->decoration != SpvDecorationBuiltIn ||
> dec->literals[0] != SpvBuiltInWorkgroupSize)
> return;
> @@ -3263,6 +3270,49 @@ vtn_handle_preamble_instruction(struct vtn_builder
> *b, SpvOp opcode,
> return true;
> }
>
> +/*
> + * gl_spirv validation. Just need to check for the entry point.
> + */
> +static bool
> +vtn_validate_preamble_instruction(struct vtn_builder *b, SpvOp opcode,
> + const uint32_t *w, unsigned count)
> +{
> + switch (opcode) {
> + /* The following opcodes are not needed for gl_spirv, so we can skip
> + * them.
> + */
> + case SpvOpSource:
> + case SpvOpSourceExtension:
> + case SpvOpSourceContinued:
> + case SpvOpExtension:
> + case SpvOpCapability:
> + case SpvOpExtInstImport:
> + case SpvOpMemoryModel:
> + case SpvOpString:
> + case SpvOpName:
> + case SpvOpMemberName:
> + case SpvOpExecutionMode:
> + case SpvOpDecorationGroup:
> + case SpvOpMemberDecorate:
> + case SpvOpGroupDecorate:
> + case SpvOpGroupMemberDecorate:
> + break;
> +
> + case SpvOpEntryPoint:
> + vtn_handle_preamble_instruction(b, opcode, w, count);
> + break;
> +
> + case SpvOpDecorate:
> + vtn_handle_decoration(b, opcode, w, count);
> + break;
> +
> + default:
> + return false; /* End of preamble */
> + }
> +
> + return true;
> +}
> +
> static void
> vtn_handle_execution_mode(struct vtn_builder *b, struct vtn_value *entry_point,
> const struct vtn_decoration *mode, void *data)
> @@ -3473,6 +3523,22 @@ vtn_handle_variable_or_type_instruction(struct
> vtn_builder *b, SpvOp opcode,
> }
>
> static bool
> +vtn_handle_constant_or_type_instruction(struct vtn_builder *b, SpvOp opcode,
> + const uint32_t *w, unsigned count)
> +{
> + switch (opcode) {
> + case SpvOpUndef:
> + case SpvOpVariable:
> + break;
> +
> + default:
> + return vtn_handle_variable_or_type_instruction(b, opcode, w, count);
> + }
> +
> + return true;
> +}
> +
> +static bool
> vtn_handle_body_instruction(struct vtn_builder *b, SpvOp opcode,
> const uint32_t *w, unsigned count)
> {
> @@ -3775,12 +3841,10 @@ vtn_handle_body_instruction(struct vtn_builder *b,
> SpvOp opcode,
> return true;
> }
>
> -nir_function *
> -spirv_to_nir(const uint32_t *words, size_t word_count,
> - struct nir_spirv_specialization *spec, unsigned num_spec,
> - gl_shader_stage stage, const char *entry_point_name,
> - const struct spirv_to_nir_options *options,
> - const nir_shader_compiler_options *nir_options)
> +static struct vtn_builder*
> +common_initialization(const uint32_t *words, size_t word_count,
> + gl_shader_stage stage, const char *entry_point_name,
> + const struct spirv_to_nir_options *options)
How about vtn_builder_create?
> {
> /* Initialize the stn_builder object */
> struct vtn_builder *b = rzalloc(NULL, struct vtn_builder);
> @@ -3794,14 +3858,6 @@ 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) */
> vtn_assert(word_count > 5);
>
> @@ -3811,11 +3867,114 @@ spirv_to_nir(const uint32_t *words, size_t word_count,
> unsigned value_id_bound = words[3];
> vtn_assert(words[4] == 0);
>
> - words+= 5;
> -
> b->value_id_bound = value_id_bound;
> b->values = rzalloc_array(b, struct vtn_value, value_id_bound);
>
> + return b;
> +}
> +
> +/*
> + * Since OpenGL 4.6 you can use SPIR-V modules directly on OpenGL. One of the
> + * new methods, glSpecializeShader include some possible errors when trying to
> + * use it. From OpenGL 4.6, Section 7.2.1, "Shader Specialization":
> + *
> + * "void SpecializeShaderARB(uint shader,
> + * const char* pEntryPoint,
> + * uint numSpecializationConstants,
> + * const uint* pConstantIndex,
> + * const uint* pConstantVaulue);
> + * <skip>
> + *
> + * INVALID_VALUE is generated if <pEntryPoint> does not name a valid
> + * entry point for <shader>.
> + *
> + * An INVALID_VALUE error is generated if any element of pConstantIndex refers
> + * to a specialization constant that does not exist in the shader module
> + * contained in shader."
> + *
> + * We could do those checks on spirv_to_nir, but we are only interested on the
> + * full translation later, during linking. This method is a simplified version
> + * of spirv_to_nir, looking for only the checks needed by SpecializeShader.
> + *
> + * This method returns NULL if no entry point was found, and fill the
> + * nir_spirv_specialization field "defined_on_module" accordingly. Caller
> + * would need to trigger the specific errors.
> + *
> + */
> +bool
> +gl_spirv_validation(const uint32_t *words, size_t word_count,
> + struct nir_spirv_specialization *spec, unsigned num_spec,
> + gl_shader_stage stage, const char *entry_point_name)
Would it be reasonable to out this in it's own file? It seems to me like
the only thing you really need to re-use is handle_decoration and the other
attempts at code re-use are just confusing things.
> +{
> + /* vtn_warn/vtn_log uses debug.func. Setting a null to prevent crash. Not
> + * need to print the warnings now, would be done later, on the real
> + * spirv_to_nir
> + */
> + const struct spirv_to_nir_options options = { .debug.func = NULL};
> + const uint32_t *word_end = words + word_count;
> +
> + struct vtn_builder *b = common_initialization(words, word_count,
> + stage, entry_point_name,
> + &options);
> +
> + /* See also _vtn_fail() */
> + if (setjmp(b->fail_jump)) {
> + ralloc_free(b);
> + return false;
> + }
> +
> + if (b == NULL)
> + return false;
These two checks are in the wrong order.
> +
> + words+= 5;
> +
> + /* Search entry point from preamble */
> + words = vtn_foreach_instruction(b, words, word_end,
> + vtn_validate_preamble_instruction);
> +
> + if (b->entry_point == NULL) {
> + ralloc_free(b);
> + return false;
> + }
> +
> + b->specializations = spec;
> + b->num_specializations = num_spec;
> +
> + /* Handle type, and constant instructions (we don't need to handle
> + * variables for gl_spirv)
> + */
> + words = vtn_foreach_instruction(b, words, word_end,
> + vtn_handle_constant_or_type_instruction);
> +
> + ralloc_free(b);
> +
> + return true;
> +}
> +
> +nir_function *
> +spirv_to_nir(const uint32_t *words, size_t word_count,
> + struct nir_spirv_specialization *spec, unsigned num_spec,
> + gl_shader_stage stage, const char *entry_point_name,
> + const struct spirv_to_nir_options *options,
> + const nir_shader_compiler_options *nir_options)
> +
> +{
> + const uint32_t *word_end = words + word_count;
> +
> + struct vtn_builder *b = common_initialization(words, word_count,
> + stage, entry_point_name,
> + options);
> + /* See also _vtn_fail() */
> + if (setjmp(b->fail_jump)) {
> + ralloc_free(b);
> + return NULL;
> + }
> +
> + if (b == NULL)
> + return NULL;
Again, the null check needs to go first.
> +
> + words+= 5;
> +
> /* Handle all the preamble instructions */
> words = vtn_foreach_instruction(b, words, word_end,
> vtn_handle_preamble_instruction);
> --
> 2.11.0
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
More information about the mesa-dev
mailing list