summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlejandro Piñeiro <[email protected]>2018-01-18 12:31:52 +0100
committerAlejandro Piñeiro <[email protected]>2018-03-30 09:14:56 +0200
commit9063bf7ad8a9175c4eddba5d06d887c507f6b5be (patch)
tree997c580adcf89df5cc9b593b700777e2160f5a14 /src
parentbebe3d626e562bd2dd027f9bcd5fdeeab62997cd (diff)
nir/spirv: add gl_spirv_validation method
ARB_gl_spirv adds the ability to use SPIR-V binaries, and a new method, glSpecializeShader. Here we add a new function to do the validation for this function: 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>."" 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) v6: update after vtn_builder_create method renamed, add explanatory comment, tweak existing comment and commit message (Timothy)
Diffstat (limited to 'src')
-rw-r--r--src/compiler/Makefile.sources1
-rw-r--r--src/compiler/nir/meson.build1
-rw-r--r--src/compiler/spirv/gl_spirv.c271
-rw-r--r--src/compiler/spirv/nir_spirv.h5
-rw-r--r--src/compiler/spirv/spirv_to_nir.c35
-rw-r--r--src/compiler/spirv/vtn_private.h6
6 files changed, 305 insertions, 14 deletions
diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources
index 273ce4cde49..aca9dab476e 100644
--- a/src/compiler/Makefile.sources
+++ b/src/compiler/Makefile.sources
@@ -297,6 +297,7 @@ SPIRV_GENERATED_FILES = \
SPIRV_FILES = \
spirv/GLSL.ext.AMD.h \
spirv/GLSL.std.450.h \
+ spirv/gl_spirv.c \
spirv/nir_spirv.h \
spirv/spirv.h \
spirv/spirv_info.h \
diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build
index db89e276139..b28a565d0c6 100644
--- a/src/compiler/nir/meson.build
+++ b/src/compiler/nir/meson.build
@@ -186,6 +186,7 @@ files_libnir = files(
'nir_worklist.h',
'../spirv/GLSL.ext.AMD.h',
'../spirv/GLSL.std.450.h',
+ '../spirv/gl_spirv.c',
'../spirv/nir_spirv.h',
'../spirv/spirv.h',
'../spirv/spirv_info.h',
diff --git a/src/compiler/spirv/gl_spirv.c b/src/compiler/spirv/gl_spirv.c
new file mode 100644
index 00000000000..edb635a09d9
--- /dev/null
+++ b/src/compiler/spirv/gl_spirv.c
@@ -0,0 +1,271 @@
+/*
+ * 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_create_builder(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;
+ }
+
+ /* Skip the SPIR-V header, handled at vtn_create_builder */
+ 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 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 d895d82055c..2550ef0a233 100644
--- a/src/compiler/spirv/spirv_to_nir.c
+++ b/src/compiler/spirv/spirv_to_nir.c
@@ -466,7 +466,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)
{
@@ -3191,6 +3191,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)
@@ -3379,20 +3397,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 47d06f0f919..d8a00f99b97 100644
--- a/src/compiler/spirv/vtn_private.h
+++ b/src/compiler/spirv/vtn_private.h
@@ -723,6 +723,12 @@ struct vtn_builder* vtn_create_builder(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)
{