}
}
+// TODO(umar): Check MemoryModel is in module
+// TODO(umar): Check OpVariable storage class is not function in module section
+// TODO(umar): Make sure function declarations appear before function
+// definitions
+// TODO(umar): Better error messages
+// NOTE: This function does not handle CFG related validation
+// Performs logical layout validation. See Section 2.4
+spv_result_t ModuleLayoutPass(ValidationState_t& _,
+ const spv_parsed_instruction_t* inst) {
+ if (_.is_enabled(SPV_VALIDATE_LAYOUT_BIT)) {
+ SpvOp opcode = inst->opcode;
+
+ if (libspirv::ModuleLayoutSection::kModule == _.getLayoutStage()) {
+ // Module scoped instructions are processed by determining if the opcode
+ // is part of the current stage. If it is not then the next stage is
+ // checked.
+ while (_.isOpcodeInCurrentLayoutStage(opcode) == false) {
+ // TODO(umar): Check if the MemoryModel instruction has executed
+ _.progressToNextLayoutStageOrder();
+ if (_.getLayoutStage() == libspirv::ModuleLayoutSection::kFunction) {
+ // All module stages have been processed. Recursivly call
+ // ModuleLayoutPass
+ // to process the next section of the module
+ return ModuleLayoutPass(_, inst);
+ }
+ }
+ } else {
+ // Validate the function layout.
+ switch (opcode) {
+ case SpvOpCapability:
+ case SpvOpExtension:
+ case SpvOpExtInstImport:
+ case SpvOpMemoryModel:
+ case SpvOpEntryPoint:
+ case SpvOpExecutionMode:
+ case SpvOpSourceContinued:
+ case SpvOpSource:
+ case SpvOpSourceExtension:
+ case SpvOpString:
+ case SpvOpName:
+ case SpvOpMemberName:
+ case SpvOpDecorate:
+ case SpvOpMemberDecorate:
+ case SpvOpGroupDecorate:
+ case SpvOpGroupMemberDecorate:
+ case SpvOpDecorationGroup:
+ 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:
+ case SpvOpTypeForwardPointer:
+ case SpvOpConstantTrue:
+ case SpvOpConstantFalse:
+ case SpvOpConstant:
+ case SpvOpConstantComposite:
+ case SpvOpConstantSampler:
+ case SpvOpConstantNull:
+ case SpvOpSpecConstantTrue:
+ case SpvOpSpecConstantFalse:
+ case SpvOpSpecConstant:
+ case SpvOpSpecConstantComposite:
+ case SpvOpSpecConstantOp:
+ return _.diag(SPV_ERROR_INVALID_LAYOUT) << "Invalid Layout";
+ case SpvOpVariable: {
+ const uint32_t* storage_class =
+ inst->words + inst->operands[2].offset;
+ if (*storage_class != SpvStorageClassFunction)
+ return _.diag(SPV_ERROR_INVALID_LAYOUT)
+ << "All OpVariable instructions in a function must have a "
+ "storage class of Function[7]";
+ break;
+ }
+ default:
+ return SPV_SUCCESS;
+ }
+ }
+ }
+ return SPV_SUCCESS;
+}
+
+// Shame
+#define CHECK_RESULT(EXPRESSION) \
+ do{ \
+ spv_result_t ret = EXPRESSION; \
+ if(ret) return ret; \
+} while(false);
+
spv_result_t ProcessInstructions(void* user_data,
const spv_parsed_instruction_t* inst) {
ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
DebugInstructionPass(_, inst);
// TODO(umar): Perform CFG pass
- // TODO(umar): Perform logical layout validation pass
// TODO(umar): Perform data rules pass
// TODO(umar): Perform instruction validation pass
- return SsaPass(_, can_have_forward_declared_ids, inst);
+ spv_result_t ret = SPV_SUCCESS;
+ CHECK_RESULT(ModuleLayoutPass(_, inst))
+ CHECK_RESULT(SsaPass(_, can_have_forward_declared_ids, inst))
+
+ return ret;
}
} // anonymous namespace
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+#include "headers/spirv.h"
#include "validate_types.h"
+#include <algorithm>
#include <map>
#include <string>
#include <unordered_set>
#include <vector>
+using std::find;
using std::string;
-using std::vector;
using std::unordered_set;
+using std::vector;
+
+namespace {
+const vector<vector<SpvOp>>& GetModuleOrder() {
+ // See Section 2.4
+ // clang-format off
+ static const vector<vector<SpvOp>> moduleOrder = {
+ {SpvOpCapability},
+ {SpvOpExtension},
+ {SpvOpExtInstImport},
+ {SpvOpMemoryModel},
+ {SpvOpEntryPoint},
+ {SpvOpExecutionMode},
+ {
+ // first set of debug instructions
+ SpvOpSourceContinued,
+ SpvOpSource,
+ SpvOpSourceExtension,
+ SpvOpString,
+ },
+ {
+ // second set of debug instructions
+ SpvOpName,
+ SpvOpMemberName
+ },
+ {
+ // annotation instructions
+ SpvOpDecorate,
+ SpvOpMemberDecorate,
+ SpvOpGroupDecorate,
+ SpvOpGroupMemberDecorate,
+ SpvOpDecorationGroup
+ },
+ {
+ // All type and constant instructions
+ SpvOpTypeVoid,
+ SpvOpTypeBool,
+ SpvOpTypeInt,
+ SpvOpTypeFloat,
+ SpvOpTypeVector,
+ SpvOpTypeMatrix,
+ SpvOpTypeImage,
+ SpvOpTypeSampler,
+ SpvOpTypeSampledImage,
+ SpvOpTypeArray,
+ SpvOpTypeRuntimeArray,
+ SpvOpTypeStruct,
+ SpvOpTypeOpaque,
+ SpvOpTypePointer,
+ SpvOpTypeFunction,
+ SpvOpTypeEvent,
+ SpvOpTypeDeviceEvent,
+ SpvOpTypeReserveId,
+ SpvOpTypeQueue,
+ SpvOpTypePipe,
+ SpvOpTypeForwardPointer,
+ SpvOpConstantTrue,
+ SpvOpConstantFalse,
+ SpvOpConstant,
+ SpvOpConstantComposite,
+ SpvOpConstantSampler,
+ SpvOpConstantNull,
+ SpvOpSpecConstantTrue,
+ SpvOpSpecConstantFalse,
+ SpvOpSpecConstant,
+ SpvOpSpecConstantComposite,
+ SpvOpSpecConstantOp,
+ SpvOpVariable,
+ SpvOpLine
+ }
+ };
+ // clang-format on
+
+ return moduleOrder;
+}
+}
namespace libspirv {
ValidationState_t::ValidationState_t(spv_diagnostic* diag, uint32_t options)
- : diagnostic_(diag), instruction_counter_(0), validation_flags_(options) {}
+ : diagnostic_(diag),
+ instruction_counter_(0),
+ defined_ids_{},
+ unresolved_forward_ids_{},
+ validation_flags_(options),
+ operand_names_{},
+ module_layout_order_stage_(0),
+ current_layout_stage_(ModuleLayoutSection::kModule) {}
spv_result_t ValidationState_t::defineId(uint32_t id) {
if (defined_ids_.find(id) == end(defined_ids_)) {
return out;
}
-//
bool ValidationState_t::isDefinedId(uint32_t id) const {
return defined_ids_.find(id) != end(defined_ids_);
}
return instruction_counter_++;
}
+ModuleLayoutSection ValidationState_t::getLayoutStage() const {
+ return current_layout_stage_;
+}
+
+void ValidationState_t::progressToNextLayoutStageOrder() {
+ module_layout_order_stage_ +=
+ module_layout_order_stage_ < GetModuleOrder().size();
+ if (module_layout_order_stage_ >= GetModuleOrder().size()) {
+ current_layout_stage_ = libspirv::ModuleLayoutSection::kFunction;
+ }
+}
+
+bool ValidationState_t::isOpcodeInCurrentLayoutStage(SpvOp op) {
+ const vector<SpvOp>& currentStage =
+ GetModuleOrder()[module_layout_order_stage_];
+ return end(currentStage) != find(begin(currentStage), end(currentStage), op);
+}
+
libspirv::DiagnosticStream ValidationState_t::diag(
spv_result_t error_code) const {
return libspirv::DiagnosticStream(
--- /dev/null
+// Copyright (c) 2015 The Khronos Group Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+// https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE 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
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+// Validation tests for Logical Layout
+
+#include "gmock/gmock.h"
+#include "UnitSPIRV.h"
+#include "ValidateFixtures.h"
+
+#include <functional>
+#include <sstream>
+#include <string>
+#include <utility>
+
+using std::function;
+using std::ostream_iterator;
+using std::pair;
+using std::stringstream;
+using std::string;
+using std::tie;
+using std::tuple;
+using std::vector;
+
+using ::testing::HasSubstr;
+
+using pred_type = function<bool(int)>;
+using ValidateLayout =
+ spvtest::ValidateBase<tuple<int, tuple<string, pred_type, pred_type>>,
+ SPV_VALIDATE_LAYOUT_BIT>;
+
+namespace {
+
+// returns true if order is equal to VAL
+template <int VAL>
+bool Equals(int order) {
+ return order == VAL;
+}
+
+// returns true if order is between MIN and MAX(inclusive)
+template <int MIN, int MAX>
+struct Range {
+ bool operator()(int order) { return order >= MIN && order <= MAX; }
+};
+
+template <typename... T>
+bool RangeSet(int order) {
+ for (bool val : {T()(order)...})
+ if (!val) return val;
+ return false;
+}
+
+// SPIRV source used to test the logical layout
+const vector<string>& getInstructions() {
+ // clang-format off
+ static const vector<string> instructions = {
+ "OpCapability Matrix",
+ "OpExtension \"TestExtension\"",
+ "%inst = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint GLCompute %func \"\"",
+ "OpExecutionMode %func LocalSize 1 1 1",
+ "%str = OpString \"Test String\"",
+ "OpSource GLSL 450 %str \"uniform vec3 var = vec3(4.0);\"",
+ "OpSourceContinued \"void main(){return;}\"",
+ "OpSourceExtension \"Test extension\"",
+ "OpName %id \"MyID\"",
+ "OpMemberName %struct 1 \"my_member\"",
+ "OpDecorate %dgrp RowMajor",
+ "OpMemberDecorate %struct 1 RowMajor",
+ "%dgrp = OpDecorationGroup",
+ "OpGroupDecorate %dgrp %mat33 %mat44",
+ "%intt = OpTypeInt 32 1",
+ "%floatt = OpTypeFloat 32",
+ "%voidt = OpTypeVoid",
+ "%boolt = OpTypeBool",
+ "%vec4 = OpTypeVector %intt 4",
+ "%vec3 = OpTypeVector %intt 3",
+ "%mat33 = OpTypeMatrix %vec3 3",
+ "%mat44 = OpTypeMatrix %vec4 4",
+ "%struct = OpTypeStruct %intt %mat33",
+ "%vfunct = OpTypeFunction %voidt",
+ "%viifunct = OpTypeFunction %voidt %intt %intt",
+ "%one = OpConstant %intt 1",
+ // TODO(umar): OpConstant fails because the type is not defined
+ // TODO(umar): OpGroupMemberDecorate
+ "OpLine %str 3 4",
+ "%func = OpFunction %voidt None %vfunct",
+ "OpFunctionEnd",
+ "%func2 = OpFunction %voidt None %viifunct",
+ "%funcp1 = OpFunctionParameter %intt",
+ "%funcp2 = OpFunctionParameter %intt",
+ "%fLabel = OpLabel",
+ " OpNop",
+ "OpReturn",
+ "OpFunctionEnd"
+ };
+ return instructions;
+}
+
+pred_type All = Range<0, 1000>();
+
+INSTANTIATE_TEST_CASE_P(InstructionsOrder,
+ ValidateLayout,
+ ::testing::Combine(::testing::Range((int)0, (int)getInstructions().size()),
+ // | Instruction | Line(s) valid | Lines to compile
+ ::testing::Values( make_tuple( string("OpCapability") , Equals<0> , All)
+ , make_tuple(string("OpExtension") , Equals<1> , All)
+ , make_tuple(string("OpExtInstImport") , Equals<2> , All)
+ , make_tuple(string("OpMemoryModel") , Equals<3> , All)
+ , make_tuple(string("OpEntryPoint") , Equals<4> , All)
+ , make_tuple(string("OpExecutionMode") , Equals<5> , All)
+ , make_tuple(string("OpSource ") , Range<6, 9>() , All)
+ , make_tuple(string("OpSourceContinued ") , Range<6, 9>() , All)
+ , make_tuple(string("OpSourceExtension ") , Range<6, 9>() , All)
+ , make_tuple(string("OpString ") , Range<6, 9>() , All)
+ , make_tuple(string("OpName ") , Range<10, 11>() , All)
+ , make_tuple(string("OpMemberName ") , Range<10, 11>() , All)
+ , make_tuple(string("OpDecorate ") , Range<12, 15>() , All)
+ , make_tuple(string("OpMemberDecorate ") , Range<12, 15>() , All)
+ , make_tuple(string("OpGroupDecorate ") , Range<12, 15>() , All)
+ , make_tuple(string("OpDecorationGroup") , Range<12, 15>() , All)
+ , make_tuple(string("OpTypeBool") , Range<16, 28>() , All)
+ , make_tuple(string("OpTypeVoid") , Range<16, 28>() , All)
+ , make_tuple(string("OpTypeFloat") , Range<16, 28>() , All)
+ , make_tuple(string("OpTypeInt") , Range<16, 28>() , static_cast<pred_type>(Range<0, 25>()))
+ , make_tuple(string("OpTypeVector %intt 4") , Range<16, 28>() , All)
+ , make_tuple(string("OpTypeMatrix %vec4 4") , Range<16, 28>() , All)
+ , make_tuple(string("OpTypeStruct") , Range<16, 28>() , All)
+ , make_tuple(string("%vfunct = OpTypeFunction"), Range<16, 28>() , All)
+ , make_tuple(string("OpConstant") , Range<19, 28>() , static_cast<pred_type>(Range<19, 100>()))
+ //, make_tuple(string("OpLabel") , RangeSet<Range<29,31>, Range<35, 36>, > , All)
+ )));
+// clang-format on
+
+// Creates a new vector which removes the string if the substr is found in the
+// instructions vector and reinserts it in the location specified by order.
+// NOTE: This will not work correctly if there are two instances of substr in
+// instructions
+vector<string> GenerateCode(string substr, int order) {
+ vector<string> code(getInstructions().size());
+ vector<string> inst(1);
+ partition_copy(begin(getInstructions()), end(getInstructions()), begin(code),
+ begin(inst), [=](const string& str) {
+ return string::npos == str.find(substr);
+ });
+
+ code.insert(begin(code) + order, inst.front());
+ return code;
+}
+
+// This test will check the logical layout of a binary by removing each
+// instruction in the pair of the INSTANTIATE_TEST_CASE_P call and moving it in
+// the SPIRV source formed by combining the vector "instructions"
+//
+// NOTE: The test will only execute with the SPV_VALIDATE_LAYOUT_BIT flag so SSA
+// and other tests are not performed
+TEST_P(ValidateLayout, Layout) {
+ int order;
+ string instruction;
+ pred_type pred;
+ pred_type test_pred; // Predicate to determine if the test should be build
+ tuple<string, pred_type, pred_type> testCase;
+
+ tie(order, testCase) = GetParam();
+ tie(instruction, pred, test_pred) = testCase;
+
+ // Skip test which break the code generation
+ if (!test_pred(order)) return;
+
+ vector<string> code = GenerateCode(instruction, order);
+
+ stringstream ss;
+ copy(begin(code), end(code), ostream_iterator<string>(ss, "\n"));
+
+ // printf("code: \n%s\n", ss.str().c_str());
+ CompileSuccessfully(ss.str());
+ if (pred(order)) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions())
+ << "Order: " << order << "\nInstruction: " << instruction;
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions())
+ << "Order: " << order << "\nInstruction: " << instruction;
+ }
+}
+
+TEST_F(ValidateLayout, DISABLED_MemoryModelMissing) {
+ string str = R"(
+ OpCapability Matrix
+ OpExtension "TestExtension"
+ %inst = OpExtInstImport "GLSL.std.450"
+ OpEntryPoint GLCompute %func ""
+ OpExecutionMode %func LocalSize 1 1 1
+ )";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+}
+
+// TODO(umar): Test optional instructions
+// TODO(umar): Test logical layout of functions
+}