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