| /* |
| * 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->operands[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; |
| } |
| |