Add primitive instruction validation pass
authorJeremy Hayes <jeremy@lunarg.com>
Thu, 14 Dec 2017 22:29:37 +0000 (15:29 -0700)
committerDavid Neto <dneto@google.com>
Fri, 15 Dec 2017 14:53:29 +0000 (09:53 -0500)
Android.mk
source/CMakeLists.txt
source/validate.cpp
source/validate.h
source/validate_primitives.cpp [new file with mode: 0644]
test/val/CMakeLists.txt
test/val/val_primitives_test.cpp [new file with mode: 0644]

index 3129e53..5b7d5f5 100644 (file)
@@ -48,6 +48,7 @@ SPVTOOLS_SRC_FILES := \
                source/validate_instruction.cpp \
                source/validate_layout.cpp \
                source/validate_logicals.cpp \
+               source/validate_primitives.cpp \
                source/validate_type_unique.cpp
 
 SPVTOOLS_OPT_SRC_FILES := \
index 9f4d3eb..f03fda9 100644 (file)
@@ -271,6 +271,7 @@ set(SPIRV_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_logicals.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/validate_primitives.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_type_unique.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h
   ${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp
index 8320b6b..bd8d4e4 100644 (file)
@@ -187,6 +187,7 @@ spv_result_t ProcessInstruction(void* user_data,
   if (auto error = BitwisePass(_, inst)) return error;
   if (auto error = ImagePass(_, inst)) return error;
   if (auto error = AtomicsPass(_, inst)) return error;
+  if (auto error = PrimitivesPass(_, inst)) return error;
 
   return SPV_SUCCESS;
 }
index 625713c..28412ae 100644 (file)
@@ -148,6 +148,10 @@ spv_result_t AtomicsPass(ValidationState_t& _,
 spv_result_t CapabilityPass(ValidationState_t& _,
                             const spv_parsed_instruction_t* inst);
 
+/// Validates correctness of primitive instructions.
+spv_result_t PrimitivesPass(ValidationState_t& _,
+                            const spv_parsed_instruction_t* inst);
+
 }  // namespace libspirv
 
 /// @brief Validate the ID usage of the instruction stream
diff --git a/source/validate_primitives.cpp b/source/validate_primitives.cpp
new file mode 100644 (file)
index 0000000..67bc85d
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright (c) 2017 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Validates correctness of primitive SPIR-V instructions.
+
+#include "validate.h"
+
+#include "diagnostic.h"
+#include "opcode.h"
+#include "val/instruction.h"
+#include "val/validation_state.h"
+
+namespace libspirv {
+
+// Validates correctness of composite instructions.
+spv_result_t PrimitivesPass(ValidationState_t& _,
+                            const spv_parsed_instruction_t* inst) {
+  const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
+
+  switch (opcode) {
+    case SpvOpEmitStreamVertex:
+    case SpvOpEndStreamPrimitive: {
+      const uint32_t stream_type = _.GetOperandTypeId(inst, 0);
+      if (!_.IsIntScalarType(stream_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA)
+               << spvOpcodeString(opcode)
+               << ": expected Stream to be int scalar";
+      }
+
+      const uint32_t stream_id = inst->words[1];
+      const SpvOp stream_opcode = _.GetIdOpcode(stream_id);
+      if (!spvOpcodeIsConstant(stream_opcode)) {
+        return _.diag(SPV_ERROR_INVALID_DATA)
+               << spvOpcodeString(opcode)
+               << ": expected Stream to be constant instruction";
+      }
+    }
+
+    default:
+      break;
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace libspirv
index d8fee6b..cd348a4 100644 (file)
@@ -122,6 +122,12 @@ add_spvtools_unittest(TARGET val_atomics
   LIBS ${SPIRV_TOOLS}
 )
 
+add_spvtools_unittest(TARGET val_primitives
+        SRCS val_primitives_test.cpp
+       ${VAL_TEST_COMMON_SRCS}
+  LIBS ${SPIRV_TOOLS}
+)
+
 add_spvtools_unittest(TARGET val_limits
        SRCS val_limits_test.cpp
        ${VAL_TEST_COMMON_SRCS}
diff --git a/test/val/val_primitives_test.cpp b/test/val/val_primitives_test.cpp
new file mode 100644 (file)
index 0000000..63b7b17
--- /dev/null
@@ -0,0 +1,162 @@
+// Copyright (c) 2017 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "unit_spirv.h"
+#include "val_fixtures.h"
+
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+using ValidatePrimitives = spvtest::ValidateBase<bool>;
+
+std::string GenerateShaderCode(
+    const std::string& body,
+    const std::string& capabilities_and_extensions = "",
+    const std::string& execution_model = "Geometry") {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Geometry
+OpCapability GeometryStreams
+)";
+
+  ss << capabilities_and_extensions;
+  ss << "OpMemoryModel Logical GLSL450\n";
+  ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
+
+  ss << R"(
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+%u32vec4 = OpTypeVector %u32 4
+
+%f32_0 = OpConstant %f32 0
+%u32_0 = OpConstant %u32 0
+%u32_1 = OpConstant %u32 1
+%u32_2 = OpConstant %u32 2
+%u32_3 = OpConstant %u32 3
+%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+)";
+
+  ss << body;
+
+  ss << R"(
+OpReturn
+OpFunctionEnd)";
+
+  return ss.str();
+}
+
+TEST_F(ValidatePrimitives, EmitStreamVertexSuccess) {
+  const std::string body = R"(
+OpEmitStreamVertex %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidatePrimitives, EmitStreamVertexNonInt) {
+  const std::string body = R"(
+OpEmitStreamVertex %f32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("EmitStreamVertex: "
+                        "expected Stream to be int scalar"));
+}
+
+TEST_F(ValidatePrimitives, EmitStreamVertexNonScalar) {
+  const std::string body = R"(
+OpEmitStreamVertex %u32vec4_0123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("EmitStreamVertex: "
+                        "expected Stream to be int scalar"));
+}
+
+TEST_F(ValidatePrimitives, EmitStreamVertexNonConstant) {
+  const std::string body = R"(
+%val1 = OpIAdd %u32 %u32_0 %u32_1
+OpEmitStreamVertex %val1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("EmitStreamVertex: "
+                        "expected Stream to be constant instruction"));
+}
+
+TEST_F(ValidatePrimitives, EndStreamPrimitiveSuccess) {
+  const std::string body = R"(
+OpEndStreamPrimitive %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidatePrimitives, EndStreamPrimitiveNonInt) {
+  const std::string body = R"(
+OpEndStreamPrimitive %f32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("EndStreamPrimitive: "
+                        "expected Stream to be int scalar"));
+}
+
+TEST_F(ValidatePrimitives, EndStreamPrimitiveNonScalar) {
+  const std::string body = R"(
+OpEndStreamPrimitive %u32vec4_0123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("EndStreamPrimitive: "
+                        "expected Stream to be int scalar"));
+}
+
+TEST_F(ValidatePrimitives, EndStreamPrimitiveNonConstant) {
+  const std::string body = R"(
+%val1 = OpIAdd %u32 %u32_0 %u32_1
+OpEndStreamPrimitive %val1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("EndStreamPrimitive: "
+                        "expected Stream to be constant instruction"));
+}
+
+}  // anonymous namespace