[Mesa-dev] [PATCH 04/11] nir/spirv: add gl_spirv_validation method
Alejandro Piñeiro
apinheiro at igalia.com
Fri Mar 16 11:27:45 UTC 2018
On 16/03/18 12:11, Rob Clark wrote:
> Just curious, how much different is this from what spirv-val does?
Well, spirv-val does a full validation of the SPIR-V binary. Here we are
just doing the basic validation required by the method
glSpecializeShadeARB, introduced by ARB_gl_spirv. That's the reason the
method is called gl_spirv_validation and not spirv_validation.
Specifically the minimum enough to be able to raise the following
possible errors from that method:
"INVALID_VALUE is generated if <pEntryPoint> does not match the
<Name> of any OpEntryPoint declaration in the SPIR-V module
associated with <shader>.
INVALID_OPERATION is generated if the <Execution Mode> of the
OpEntryPoint indicated by <pEntryPoint> does not match the type
of <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>."
So plenty of invalid SPIR-V binaries would be able to pass that point.
And that is "okish" to not check that. Also from ARB_gl_spirv spec:
" The OpenGL API expects the SPIR-V module to have already
been validated, and can return an error if it discovers anything invalid
in the module. An invalid SPIR-V module is allowed to result in
undefined behavior."
> We
> are going to end up using spirv-tools from clover, so if all of this
> is just to avoid a dependency on spirv-tools, is it worth it?
As I mentioned, this is basically because we don't need so such deep
validation. Perhaps the name is slightly confusing even with the gl_
prefix. In any case, note that with this patch we add a big comment
(almost a test wall) for this method that explain what it is really
validating.
>
> BR,
> -R
>
> On Thu, Mar 8, 2018 at 3:19 AM, 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
>>
>> v4: (after Jason comments):
>> * Rename common_initialization to vtn_builder_create
>> * Move validation method and their helpers to own source file.
>> * Create own handle_constant_decoration_cb instead of reuse existing one
>>
>> v5: put vtn_build_create refactoring to their own patch (Jason)
>> ---
>> src/compiler/Makefile.sources | 1 +
>> src/compiler/nir/meson.build | 1 +
>> src/compiler/spirv/gl_spirv.c | 268 ++++++++++++++++++++++++++++++++++++++
>> src/compiler/spirv/nir_spirv.h | 5 +
>> src/compiler/spirv/spirv_to_nir.c | 35 +++--
>> src/compiler/spirv/vtn_private.h | 6 +
>> 6 files changed, 302 insertions(+), 14 deletions(-)
>> create mode 100644 src/compiler/spirv/gl_spirv.c
>>
>> diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources
>> index 37340ba809e..24218985d6d 100644
>> --- a/src/compiler/Makefile.sources
>> +++ b/src/compiler/Makefile.sources
>> @@ -295,6 +295,7 @@ SPIRV_GENERATED_FILES = \
>> spirv/vtn_gather_types.c
>>
>> SPIRV_FILES = \
>> + spirv/gl_spirv.c \
>> spirv/GLSL.std.450.h \
>> spirv/nir_spirv.h \
>> spirv/spirv.h \
>> diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build
>> index a70c236b958..e7a94bcc1cf 100644
>> --- a/src/compiler/nir/meson.build
>> +++ b/src/compiler/nir/meson.build
>> @@ -183,6 +183,7 @@ files_libnir = files(
>> 'nir_vla.h',
>> 'nir_worklist.c',
>> 'nir_worklist.h',
>> + '../spirv/gl_spirv.c',
>> '../spirv/GLSL.std.450.h',
>> '../spirv/nir_spirv.h',
>> '../spirv/spirv.h',
>> diff --git a/src/compiler/spirv/gl_spirv.c b/src/compiler/spirv/gl_spirv.c
>> new file mode 100644
>> index 00000000000..e82686bfe0d
>> --- /dev/null
>> +++ b/src/compiler/spirv/gl_spirv.c
>> @@ -0,0 +1,268 @@
>> +/*
>> + * Copyright © 2017 Intel Corporation
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a
>> + * copy of this software and associated documentation files (the "Software"),
>> + * to deal in the Software without restriction, including without limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice (including the next
>> + * paragraph) shall be included in all copies or substantial portions of the
>> + * Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
>> + * IN THE SOFTWARE.
>> + *
>> + *
>> + */
>> +
>> +#include "nir_spirv.h"
>> +
>> +#include "vtn_private.h"
>> +#include "spirv_info.h"
>> +
>> +static bool
>> +vtn_validate_preamble_instruction(struct vtn_builder *b, SpvOp opcode,
>> + const uint32_t *w, unsigned count)
>> +{
>> + switch (opcode) {
>> + 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_entry_point(b, w, count);
>> + break;
>> +
>> + case SpvOpDecorate:
>> + vtn_handle_decoration(b, opcode, w, count);
>> + break;
>> +
>> + default:
>> + return false; /* End of preamble */
>> + }
>> +
>> + return true;
>> +}
>> +
>> +static void
>> +spec_constant_decoration_cb(struct vtn_builder *b, struct vtn_value *v,
>> + int member, const struct vtn_decoration *dec,
>> + void *data)
>> +{
>> + vtn_assert(member == -1);
>> + if (dec->decoration != SpvDecorationSpecId)
>> + return;
>> +
>> + for (unsigned i = 0; i < b->num_specializations; i++) {
>> + if (b->specializations[i].id == dec->literals[0]) {
>> + b->specializations[i].defined_on_module = true;
>> + return;
>> + }
>> + }
>> +}
>> +
>> +static void
>> +vtn_validate_handle_constant(struct vtn_builder *b, SpvOp opcode,
>> + const uint32_t *w, unsigned count)
>> +{
>> + struct vtn_value *val = vtn_push_value(b, w[2], vtn_value_type_constant);
>> +
>> + switch (opcode) {
>> + case SpvOpConstant:
>> + case SpvOpConstantNull:
>> + case SpvOpSpecConstantComposite:
>> + case SpvOpConstantComposite:
>> + /* Nothing to do here for gl_spirv needs */
>> + break;
>> +
>> + case SpvOpConstantTrue:
>> + case SpvOpConstantFalse:
>> + case SpvOpSpecConstantTrue:
>> + case SpvOpSpecConstantFalse:
>> + case SpvOpSpecConstant:
>> + case SpvOpSpecConstantOp:
>> + vtn_foreach_decoration(b, val, spec_constant_decoration_cb, NULL);
>> + break;
>> +
>> + case SpvOpConstantSampler:
>> + vtn_fail("OpConstantSampler requires Kernel Capability");
>> + break;
>> +
>> + default:
>> + vtn_fail("Unhandled opcode");
>> + }
>> +}
>> +
>> +static bool
>> +vtn_validate_handle_constant_instruction(struct vtn_builder *b, SpvOp opcode,
>> + const uint32_t *w, unsigned count)
>> +{
>> + switch (opcode) {
>> + case SpvOpSource:
>> + case SpvOpSourceContinued:
>> + case SpvOpSourceExtension:
>> + case SpvOpExtension:
>> + case SpvOpCapability:
>> + case SpvOpExtInstImport:
>> + case SpvOpMemoryModel:
>> + case SpvOpEntryPoint:
>> + case SpvOpExecutionMode:
>> + case SpvOpString:
>> + case SpvOpName:
>> + case SpvOpMemberName:
>> + case SpvOpDecorationGroup:
>> + case SpvOpDecorate:
>> + case SpvOpMemberDecorate:
>> + case SpvOpGroupDecorate:
>> + case SpvOpGroupMemberDecorate:
>> + vtn_fail("Invalid opcode types and variables section");
>> + break;
>> +
>> + case SpvOpTypeVoid:
>> + case SpvOpTypeBool:
>> + case SpvOpTypeInt:
>> + case SpvOpTypeFloat:
>> + case SpvOpTypeVector:
>> + case SpvOpTypeMatrix:
>> + case SpvOpTypeImage:
>> + case SpvOpTypeSampler:
>> + case SpvOpTypeSampledImage:
>> + case SpvOpTypeArray:
>> + case SpvOpTypeRuntimeArray:
>> + case SpvOpTypeStruct:
>> + case SpvOpTypeOpaque:
>> + case SpvOpTypePointer:
>> + case SpvOpTypeFunction:
>> + case SpvOpTypeEvent:
>> + case SpvOpTypeDeviceEvent:
>> + case SpvOpTypeReserveId:
>> + case SpvOpTypeQueue:
>> + case SpvOpTypePipe:
>> + /* We don't need to handle types */
>> + break;
>> +
>> + case SpvOpConstantTrue:
>> + case SpvOpConstantFalse:
>> + case SpvOpConstant:
>> + case SpvOpConstantComposite:
>> + case SpvOpConstantSampler:
>> + case SpvOpConstantNull:
>> + case SpvOpSpecConstantTrue:
>> + case SpvOpSpecConstantFalse:
>> + case SpvOpSpecConstant:
>> + case SpvOpSpecConstantComposite:
>> + case SpvOpSpecConstantOp:
>> + vtn_validate_handle_constant(b, opcode, w, count);
>> + break;
>> +
>> + case SpvOpUndef:
>> + case SpvOpVariable:
>> + /* We don't need to handle them */
>> + break;
>> +
>> + default:
>> + return false; /* End of preamble */
>> + }
>> +
>> + return true;
>> +}
>> +
>> +/*
>> + * 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* pConstantValue);
>> + * <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)
>> +{
>> + /* 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 = vtn_builder_create(words, word_count,
>> + stage, entry_point_name,
>> + &options);
>> +
>> + if (b == NULL)
>> + return false;
>> +
>> + /* See also _vtn_fail() */
>> + if (setjmp(b->fail_jump)) {
>> + ralloc_free(b);
>> + return false;
>> + }
>> +
>> + 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 constant instructions (we don't need to handle
>> + * variables or types for gl_spirv)
>> + */
>> + words = vtn_foreach_instruction(b, words, word_end,
>> + vtn_validate_handle_constant_instruction);
>> +
>> + ralloc_free(b);
>> +
>> + return true;
>> +}
>> +
>> diff --git a/src/compiler/spirv/nir_spirv.h b/src/compiler/spirv/nir_spirv.h
>> index 2b0bdaec013..87d4120c380 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 {
>> @@ -70,6 +71,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 fb63df209ce..66b87c049bb 100644
>> --- a/src/compiler/spirv/spirv_to_nir.c
>> +++ b/src/compiler/spirv/spirv_to_nir.c
>> @@ -461,7 +461,7 @@ vtn_foreach_execution_mode(struct vtn_builder *b, struct vtn_value *value,
>> }
>> }
>>
>> -static void
>> +void
>> vtn_handle_decoration(struct vtn_builder *b, SpvOp opcode,
>> const uint32_t *w, unsigned count)
>> {
>> @@ -3164,6 +3164,24 @@ stage_for_execution_model(struct vtn_builder *b, SpvExecutionModel model)
>> spirv_capability_to_string(cap)); \
>> } while(0)
>>
>> +
>> +void
>> +vtn_handle_entry_point(struct vtn_builder *b, const uint32_t *w,
>> + unsigned count)
>> +{
>> + struct vtn_value *entry_point = &b->values[w[2]];
>> + /* Let this be a name label regardless */
>> + unsigned name_words;
>> + entry_point->name = vtn_string_literal(b, &w[3], count - 3, &name_words);
>> +
>> + if (strcmp(entry_point->name, b->entry_point_name) != 0 ||
>> + stage_for_execution_model(b, w[1]) != b->entry_point_stage)
>> + return;
>> +
>> + vtn_assert(b->entry_point == NULL);
>> + b->entry_point = entry_point;
>> +}
>> +
>> static bool
>> vtn_handle_preamble_instruction(struct vtn_builder *b, SpvOp opcode,
>> const uint32_t *w, unsigned count)
>> @@ -3352,20 +3370,9 @@ vtn_handle_preamble_instruction(struct vtn_builder *b, SpvOp opcode,
>> w[2] == SpvMemoryModelGLSL450);
>> break;
>>
>> - case SpvOpEntryPoint: {
>> - struct vtn_value *entry_point = &b->values[w[2]];
>> - /* Let this be a name label regardless */
>> - unsigned name_words;
>> - entry_point->name = vtn_string_literal(b, &w[3], count - 3, &name_words);
>> -
>> - if (strcmp(entry_point->name, b->entry_point_name) != 0 ||
>> - stage_for_execution_model(b, w[1]) != b->entry_point_stage)
>> - break;
>> -
>> - vtn_assert(b->entry_point == NULL);
>> - b->entry_point = entry_point;
>> + case SpvOpEntryPoint:
>> + vtn_handle_entry_point(b, w, count);
>> break;
>> - }
>>
>> case SpvOpString:
>> vtn_push_value(b, w[1], vtn_value_type_string)->str =
>> diff --git a/src/compiler/spirv/vtn_private.h b/src/compiler/spirv/vtn_private.h
>> index a0934158df8..2219f4619dc 100644
>> --- a/src/compiler/spirv/vtn_private.h
>> +++ b/src/compiler/spirv/vtn_private.h
>> @@ -723,6 +723,12 @@ struct vtn_builder* vtn_builder_create(const uint32_t *words, size_t word_count,
>> gl_shader_stage stage, const char *entry_point_name,
>> const struct spirv_to_nir_options *options);
>>
>> +void vtn_handle_entry_point(struct vtn_builder *b, const uint32_t *w,
>> + unsigned count);
>> +
>> +void vtn_handle_decoration(struct vtn_builder *b, SpvOp opcode,
>> + const uint32_t *w, unsigned count);
>> +
>> static inline uint32_t
>> vtn_align_u32(uint32_t v, uint32_t a)
>> {
>> --
>> 2.14.1
>>
>> _______________________________________________
>> 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