Add a WIP WebGPU environment. It disallows OpUndef

Add SPV_ENV_WEBGPU_0 for work-in-progress WebGPU.

val: Disallow OpUndef in WebGPU env

Silence unused variable warnings when !defined(SPIRV_EFFCE)

Limit visibility of validate_instruction.cpp's symbols
  Only InstructionPass needs to be visible so all other functions are put
  in an anonymous namespace inside the libspirv namespace.
This commit is contained in:
Corentin Wallez 2018-06-20 13:29:38 -04:00 committed by David Neto
parent e7ace1b280
commit ba602c9059
17 changed files with 124 additions and 27 deletions

View File

@ -412,6 +412,7 @@ typedef enum {
SPV_ENV_OPENCL_EMBEDDED_2_2, // OpenCL Embedded Profile 2.2 latest revision.
SPV_ENV_UNIVERSAL_1_3, // SPIR-V 1.3 latest revision, no other restrictions.
SPV_ENV_VULKAN_1_1, // Vulkan 1.1 latest revision.
SPV_ENV_WEBGPU_0, // Work in progress WebGPU 1.0.
} spv_target_env;
// SPIR-V Validator can be parameterized with the following Universal Limits.

View File

@ -81,6 +81,7 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
case SPV_ENV_OPENGL_4_5:
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_VULKAN_1_1:
case SPV_ENV_WEBGPU_0:
*pExtInstTable = &kTable_1_0;
return SPV_SUCCESS;
default:

View File

@ -58,6 +58,8 @@ const char* spvTargetEnvDescription(spv_target_env env) {
return "SPIR-V 1.3";
case SPV_ENV_VULKAN_1_1:
return "SPIR-V 1.3 (under Vulkan 1.1 semantics)";
case SPV_ENV_WEBGPU_0:
return "SPIR-V 1.3 (under WIP WebGPU semantics)";
}
assert(0 && "Unhandled SPIR-V target environment");
return "";
@ -87,6 +89,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
return SPV_SPIRV_VERSION_WORD(1, 2);
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_VULKAN_1_1:
case SPV_ENV_WEBGPU_0:
return SPV_SPIRV_VERSION_WORD(1, 3);
}
assert(0 && "Unhandled SPIR-V target environment");
@ -154,6 +157,9 @@ bool spvParseTargetEnv(const char* s, spv_target_env* env) {
} else if (match("opengl4.5")) {
if (env) *env = SPV_ENV_OPENGL_4_5;
return true;
} else if (match("webgpu0")) {
if (env) *env = SPV_ENV_WEBGPU_0;
return true;
} else {
if (env) *env = SPV_ENV_UNIVERSAL_1_0;
return false;
@ -179,6 +185,7 @@ bool spvIsVulkanEnv(spv_target_env env) {
case SPV_ENV_OPENCL_2_2:
case SPV_ENV_OPENCL_EMBEDDED_2_2:
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_WEBGPU_0:
return false;
case SPV_ENV_VULKAN_1_0:
case SPV_ENV_VULKAN_1_1:

View File

@ -37,6 +37,7 @@ spv_context spvContextCreate(spv_target_env env) {
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_VULKAN_1_1:
case SPV_ENV_WEBGPU_0:
break;
default:
return nullptr;

View File

@ -166,6 +166,14 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx,
memory_model_(SpvMemoryModelMax),
in_function_(false) {
assert(opt && "Validator options may not be Null.");
switch (context_->target_env) {
case SPV_ENV_WEBGPU_0:
features_.bans_op_undef = true;
break;
default:
break;
}
}
spv_result_t ValidationState_t::ForwardDeclareId(uint32_t id) {

View File

@ -58,7 +58,7 @@ enum ModuleLayoutSection {
/// This class manages the state of the SPIR-V validation as it is being parsed.
class ValidationState_t {
public:
// Features that can optionally be turned on by a capability.
// Features that can optionally be turned on by a capability or environment.
struct Feature {
bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width?
bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width?
@ -74,6 +74,9 @@ class ValidationState_t {
// Permit group oerations Reduce, InclusiveScan, ExclusiveScan
bool group_ops_reduce_and_scans = false;
// Disallows the use of OpUndef
bool bans_op_undef = false;
};
ValidationState_t(const spv_const_context context,
@ -570,7 +573,7 @@ class ValidationState_t {
bool in_function_;
/// The state of optional features. These are determined by capabilities
/// declared by the module.
/// declared by the module and the environment.
Feature features_;
/// Maps function ids to function stat objects.

View File

@ -37,11 +37,7 @@
#include "val/function.h"
#include "val/validation_state.h"
using libspirv::AssemblyGrammar;
using libspirv::CapabilitySet;
using libspirv::DiagnosticStream;
using libspirv::ExtensionSet;
using libspirv::ValidationState_t;
namespace libspirv {
namespace {
@ -86,8 +82,7 @@ CapabilitySet EnablingCapabilitiesForOp(const ValidationState_t& state,
case SpvOpGroupFMaxNonUniformAMD:
case SpvOpGroupUMaxNonUniformAMD:
case SpvOpGroupSMaxNonUniformAMD:
if (state.HasExtension(libspirv::kSPV_AMD_shader_ballot))
return CapabilitySet();
if (state.HasExtension(kSPV_AMD_shader_ballot)) return CapabilitySet();
break;
default:
break;
@ -183,11 +178,7 @@ ExtensionSet RequiredExtensions(const ValidationState_t& state,
return {};
}
} // namespace
namespace libspirv {
// Return SPV_ERROR_INVALID_BINARY and emit a diagnostic if the instruction
// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction
// is explicitly reserved in the SPIR-V core spec. Otherwise return
// SPV_SUCCESS.
spv_result_t ReservedCheck(ValidationState_t& _,
@ -211,6 +202,26 @@ spv_result_t ReservedCheck(ValidationState_t& _,
return SPV_SUCCESS;
}
// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction
// is invalid because of an execution environment constraint.
spv_result_t EnvironmentCheck(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
switch (opcode) {
case SpvOpUndef:
if (_.features().bans_op_undef) {
return _.diag(SPV_ERROR_INVALID_BINARY) << "OpUndef is disallowed";
}
break;
default:
break;
}
return SPV_SUCCESS;
}
// Returns SPV_ERROR_INVALID_CAPABILITY and emits a diagnostic if the
// instruction is invalid because the required capability isn't declared
// in the module.
spv_result_t CapabilityCheck(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
@ -269,9 +280,9 @@ spv_result_t ExtensionCheck(ValidationState_t& _,
return SPV_SUCCESS;
}
// Checks that the instruction can be used in this target environment.
// Assumes that CapabilityCheck has checked direct capability dependencies
// for the opcode.
// Checks that the instruction can be used in this target environment's base
// version. Assumes that CapabilityCheck has checked direct capability
// dependencies for the opcode.
spv_result_t VersionCheck(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
const auto opcode = static_cast<SpvOp>(inst->opcode);
@ -516,6 +527,8 @@ void CheckIfKnownExtension(ValidationState_t& _,
}
}
} // namespace
spv_result_t InstructionPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
@ -582,6 +595,7 @@ spv_result_t InstructionPass(ValidationState_t& _,
if (auto error = ExtensionCheck(_, inst)) return error;
if (auto error = ReservedCheck(_, inst)) return error;
if (auto error = EnvironmentCheck(_, inst)) return error;
if (auto error = CapabilityCheck(_, inst)) return error;
if (auto error = LimitCheckIdBound(_, inst)) return error;
if (auto error = LimitCheckStruct(_, inst)) return error;

View File

@ -47,6 +47,9 @@ bool Validate(const std::vector<uint32_t>& bin) {
}
void Match(const std::string& checks, ir::IRContext* context) {
// Silence unused warnings with !defined(SPIRV_EFFCE)
(void)checks;
std::vector<uint32_t> bin;
context->module()->ToBinary(&bin, true);
EXPECT_TRUE(Validate(bin));

View File

@ -42,6 +42,9 @@ bool Validate(const std::vector<uint32_t>& bin) {
}
void Match(const std::string& checks, ir::IRContext* context) {
// Silence unused warnings with !defined(SPIRV_EFFCE)
(void)checks;
std::vector<uint32_t> bin;
context->module()->ToBinary(&bin, true);
EXPECT_TRUE(Validate(bin));

View File

@ -91,6 +91,7 @@ INSTANTIATE_TEST_CASE_P(
{"opencl2.0embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_0},
{"opencl2.1embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_1},
{"opencl2.2embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_2},
{"webgpu0", true, SPV_ENV_WEBGPU_0},
{"opencl2.3", false, SPV_ENV_UNIVERSAL_1_0},
{"opencl3.0", false, SPV_ENV_UNIVERSAL_1_0},
{"vulkan1.2", false, SPV_ENV_UNIVERSAL_1_0},

View File

@ -218,7 +218,7 @@ inline std::vector<spv_target_env> AllTargetEnvironments() {
SPV_ENV_OPENGL_4_1, SPV_ENV_OPENGL_4_2,
SPV_ENV_OPENGL_4_3, SPV_ENV_OPENGL_4_5,
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3,
SPV_ENV_VULKAN_1_1,
SPV_ENV_VULKAN_1_1, SPV_ENV_WEBGPU_0,
};
}

View File

@ -60,7 +60,7 @@ add_spvtools_unittest(TARGET val_ijklmnop
LIBS ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET val_stuv
add_spvtools_unittest(TARGET val_stuvw
SRCS
val_ssa_test.cpp
val_state_test.cpp
@ -68,6 +68,7 @@ add_spvtools_unittest(TARGET val_stuv
val_type_unique_test.cpp
val_validation_state_test.cpp
val_version_test.cpp
val_webgpu_test.cpp
${VAL_TEST_COMMON_SRCS}
LIBS ${SPIRV_TOOLS}
)

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Common validation fixtures for unit tests
// Validation tests for decorations
#include "gmock/gmock.h"
#include "source/val/decoration.h"

View File

@ -34,6 +34,8 @@ class ValidateBase : public ::testing::Test,
// Returns the a spv_const_binary struct
spv_const_binary get_const_binary();
// Checks that 'code' is valid SPIR-V text representation and stores the
// binary version for further method calls.
void CompileSuccessfully(std::string code,
spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
@ -43,8 +45,7 @@ class ValidateBase : public ::testing::Test,
// This function overwrites the word at the given index with a new word.
void OverwriteAssembledBinary(uint32_t index, uint32_t word);
// Performs validation on the SPIR-V code and compares the result of the
// spvValidate function
// Performs validation on the SPIR-V code.
spv_result_t ValidateInstructions(spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
// Performs validation. Returns the status and stores validation state into

View File

@ -63,6 +63,7 @@ std::string version(spv_target_env env) {
return "1.2";
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_VULKAN_1_1:
case SPV_ENV_WEBGPU_0:
return "1.3";
default:
return "0";
@ -100,6 +101,7 @@ INSTANTIATE_TEST_CASE_P(Universal, ValidateVersion,
make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true),
make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true),
make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true),
make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_WEBGPU_0, vulkan_spirv, true),
make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true),
@ -112,6 +114,7 @@ INSTANTIATE_TEST_CASE_P(Universal, ValidateVersion,
make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_WEBGPU_0, vulkan_spirv, true),
make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
@ -124,6 +127,7 @@ INSTANTIATE_TEST_CASE_P(Universal, ValidateVersion,
make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_WEBGPU_0, vulkan_spirv, true),
make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
@ -135,7 +139,8 @@ INSTANTIATE_TEST_CASE_P(Universal, ValidateVersion,
make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false)
make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_WEBGPU_0, vulkan_spirv, true)
)
);

View File

@ -0,0 +1,47 @@
// Copyright (c) 2018 Google 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.
// Validation tests for WebGPU env specific checks
#include <gmock/gmock.h>
#include "val_fixtures.h"
namespace {
using std::string;
using testing::HasSubstr;
using ValidateWebGPU = spvtest::ValidateBase<bool>;
TEST_F(ValidateWebGPU, OpUndefIsDisallowed) {
string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%float = OpTypeFloat 32
%1 = OpUndef %float
)";
CompileSuccessfully(spirv);
// Control case: OpUndef is allowed in SPIR-V 1.3
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
// Control case: OpUndef is disallowed in the WebGPU env
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed"));
}
} // anonymous namespace

View File

@ -50,9 +50,9 @@ Options:
different type with compatible layout and
members.
--version Display validator version information.
--target-env {vulkan1.0|vulkan1.1|opencl2.2|spv1.0|spv1.1|spv1.2|spv1.3}
--target-env {vulkan1.0|vulkan1.1|opencl2.2|spv1.0|spv1.1|spv1.2|spv1.3|webgpu0}
Use Vulkan 1.0, Vulkan 1.1, OpenCL 2.2, SPIR-V 1.0,
SPIR-V 1.1, SPIR-V 1.2 or SPIR-V 1.3 validation rules.
SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3 or WIP WebGPU validation rules.
)",
argv0, argv0);
}
@ -91,14 +91,15 @@ int main(int argc, char** argv) {
}
} else if (0 == strcmp(cur_arg, "--version")) {
printf("%s\n", spvSoftwareVersionDetailsString());
printf("Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n",
printf("Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n",
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0),
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2),
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3),
spvTargetEnvDescription(SPV_ENV_OPENCL_2_2),
spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1));
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1),
spvTargetEnvDescription(SPV_ENV_WEBGPU_0));
continue_processing = false;
return_code = 0;
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {