Validate the number of global and local variables.
authorEhsan Nasiri <ehsann@google.com>
Tue, 29 Nov 2016 20:50:34 +0000 (15:50 -0500)
committerDavid Neto <dneto@google.com>
Thu, 1 Dec 2016 21:04:13 +0000 (16:04 -0500)
According to the Universal Limits section of the SPIR-V Spec (2.17), the
number of global variables may not exceed 65,535 and the number of local
variables may not exceed 524,287.

Also added unit tests for each one.

source/val/validation_state.cpp
source/val/validation_state.h
source/validate_instruction.cpp
test/val/val_limits_test.cpp

index c69749c..b11e6fc 100644 (file)
@@ -192,6 +192,8 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx)
       module_capabilities_(),
       ordered_instructions_(),
       all_definitions_(),
+      num_global_vars_(0),
+      num_local_vars_(0),
       grammar_(ctx),
       addressing_model_(SpvAddressingModelLogical),
       memory_model_(SpvMemoryModelSimple),
index 7e69a9d..ad90ef9 100644 (file)
@@ -193,6 +193,18 @@ class ValidationState_t {
   void RegisterSampledImageConsumer(uint32_t sampled_image_id,
                                     uint32_t cons_id);
 
+  /// Returns the number of Global Variables
+  uint32_t num_global_vars() { return num_global_vars_; }
+
+  /// Returns the number of Local Variables
+  uint32_t num_local_vars() { return num_local_vars_; }
+
+  /// Increments the number of Global Variables
+  void incrementNumGlobalVars() { ++num_global_vars_; }
+
+  /// Increments the number of Local Variables
+  void incrementNumLocalVars() { ++num_local_vars_; }
+
  private:
   ValidationState_t(const ValidationState_t&);
 
@@ -236,6 +248,12 @@ class ValidationState_t {
   /// ID Bound from the Header
   uint32_t id_bound_;
 
+  /// Number of Global Variables (Storage Class other than 'Function')
+  uint32_t num_global_vars_;
+
+  /// Number of Local Variables ('Function' Storage Class)
+  uint32_t num_local_vars_;
+
   AssemblyGrammar grammar_;
 
   SpvAddressingModel addressing_model_;
index 3fdd362..0b3d0c0 100644 (file)
@@ -176,6 +176,31 @@ spv_result_t LimitCheckSwitch(ValidationState_t& _,
   return SPV_SUCCESS;
 }
 
+// Ensure the number of variables of the given class does not exceed the limit.
+spv_result_t LimitCheckNumVars(ValidationState_t& _,
+                               const SpvStorageClass storage_class) {
+  if (SpvStorageClassFunction == storage_class) {
+    _.incrementNumLocalVars();
+    const uint32_t num_local_vars_limit = 0x7FFFF;
+    if (_.num_local_vars() > num_local_vars_limit) {
+      return _.diag(SPV_ERROR_INVALID_BINARY)
+             << "Number of local variables ('Function' Storage Class) "
+                "exceeded the valid limit ("
+             << num_local_vars_limit << ").";
+    }
+  } else {
+    _.incrementNumGlobalVars();
+    const uint32_t num_global_vars_limit = 0xFFFF;
+    if (_.num_global_vars() > num_global_vars_limit) {
+      return _.diag(SPV_ERROR_INVALID_BINARY)
+             << "Number of Global Variables (Storage Class other than "
+                "'Function') exceeded the valid limit ("
+             << num_global_vars_limit << ").";
+    }
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t InstructionPass(ValidationState_t& _,
                              const spv_parsed_instruction_t* inst) {
   const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
@@ -191,6 +216,9 @@ spv_result_t InstructionPass(ValidationState_t& _,
   if (opcode == SpvOpVariable) {
     const auto storage_class =
         static_cast<SpvStorageClass>(inst->words[inst->operands[2].offset]);
+    if (auto error = LimitCheckNumVars(_, storage_class)) {
+      return error;
+    }
     if (storage_class == SpvStorageClassGeneric)
       return _.diag(SPV_ERROR_INVALID_BINARY)
              << "OpVariable storage class cannot be Generic";
index 0d34259..3774afe 100644 (file)
@@ -194,3 +194,96 @@ TEST_F(ValidateLimits, OpTypeFunctionBad) {
               HasSubstr("OpTypeFunction may not take more than 255 arguments. "
                         "OpTypeFunction <id> '2' has 256 arguments."));
 }
+
+// Valid: module has 65,535 global variables.
+TEST_F(ValidateLimits, NumGlobalVarsGood) {
+  int num_globals = 65535;
+  std::ostringstream spirv;
+  spirv << header << R"(
+     %int = OpTypeInt 32 0
+%_ptr_int = OpTypePointer Input %int
+  )";
+
+  for (int i = 0; i < num_globals; ++i) {
+    spirv << "%var_" << i << " = OpVariable %_ptr_int Input\n";
+  }
+
+  CompileSuccessfully(spirv.str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// Invalid: module has 65,536 global variables (limit is 65,535).
+TEST_F(ValidateLimits, NumGlobalVarsBad) {
+  int num_globals = 65536;
+  std::ostringstream spirv;
+  spirv << header << R"(
+     %int = OpTypeInt 32 0
+%_ptr_int = OpTypePointer Input %int
+  )";
+
+  for (int i = 0; i < num_globals; ++i) {
+    spirv << "%var_" << i << " = OpVariable %_ptr_int Input\n";
+  }
+
+  CompileSuccessfully(spirv.str());
+  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Number of Global Variables (Storage Class other than "
+                        "'Function') exceeded the valid limit (65535)."));
+}
+
+// Valid: module has 524,287 local variables.
+TEST_F(ValidateLimits, NumLocalVarsGood) {
+  int num_locals = 524287;
+  std::ostringstream spirv;
+  spirv << header << R"(
+ %int      = OpTypeInt 32 0
+ %_ptr_int = OpTypePointer Function %int
+ %voidt    = OpTypeVoid
+ %funct    = OpTypeFunction %voidt
+ %main     = OpFunction %voidt None %funct
+ %entry    = OpLabel
+  )";
+
+  for (int i = 0; i < num_locals; ++i) {
+    spirv << "%var_" << i << " = OpVariable %_ptr_int Function\n";
+  }
+
+  spirv << R"(
+    OpReturn
+    OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv.str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// Invalid: module has 524,288 local variables (limit is 524,287).
+TEST_F(ValidateLimits, NumLocalVarsBad) {
+  int num_locals = 524288;
+  std::ostringstream spirv;
+  spirv << header << R"(
+ %int      = OpTypeInt 32 0
+ %_ptr_int = OpTypePointer Function %int
+ %voidt    = OpTypeVoid
+ %funct    = OpTypeFunction %voidt
+ %main     = OpFunction %voidt None %funct
+ %entry    = OpLabel
+  )";
+
+  for (int i = 0; i < num_locals; ++i) {
+    spirv << "%var_" << i << " = OpVariable %_ptr_int Function\n";
+  }
+
+  spirv << R"(
+    OpReturn
+    OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv.str());
+  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Number of local variables ('Function' Storage Class) "
+                        "exceeded the valid limit (524287)."));
+}
+