SPIRV-Tools/test/val/val_barriers_test.cpp
David Neto 63f57d95d6
Support SPIR-V 1.4 (#2550)
* SPIR-V 1.4 headers, add SPV_ENV_UNIVERSAL_1_4

* Support --target-env spv1.4 in help for command line tools

* Support asm/dis of UniformId decoration

* Validate UniformId decoration

* Fix version check on instructions and operands

Also register decorations used with OpDecorateId

* Extension lists can differ between enums that match

Example: SubgroupMaskEq vs SubgroupMaskEqKHR

* Validate scope value for Uniform decoration, for SPIR-V 1.4

* More unioning of exts

* Preserve grammar order within an enum value

* 1.4: Validate OpSelect over composites

* Tools default to 1.4

* Add asm/dis test for OpCopyLogical

* 1.4: asm/dis tests for PtrEqual, PtrNotEqual, PtrDiff

* Basic asm/Dis test for OpCopyMemory

* Test asm/dis OpCopyMemory with 2-memory access

Add asm/dis tests for OpCopyMemorySized

Requires grammar update to add second optional memory access operand
to OpCopyMemory and OpCopyMemorySized

* Validate one or two memory accesses on OpCopyMemory*

* Check av/vis on CopyMemory source and target memory access

This is a proposed rule. See
https://gitlab.khronos.org/spirv/SPIR-V/issues/413

* Validate operation for OpSpecConstantOp

* Validate NonWritable decoration

Also permit NonWritable on members of UBO and SSBO.

* SPIR-V 1.4: NonWrtiable can decorate Function and Private vars

* Update optimizer CLI tests for SPIR-V 1.4

* Testing tools: Give expected SPIR-V version in message

* SPIR-V 1.4 validation for entry point interfaces

* Allow only unique interfaces
* Allow all global variables
* Check that all statically used global variables are listed
* new tests

* Add validation fixture CompileFailure

* Add 1.4 validation for pointer comparisons

* New tests

* Validate with image operands SignExtend, ZeroExtend

Since we don't actually know the image texel format, we can't fully
validate.  We need more context.

But we can make sure we allow the new image operands in known-good
cases.

* Validate OpCopyLogical

* Recursively checks subtypes
* new tests

* Add SPIR-V 1.4 tests for NoSignedWrap, NoUnsignedWrap

* Allow scalar conditions in 1.4 with OpSelect

* Allows scalar conditions with vector operands
* new tests

* Validate uniform id scope as an execution scope

* Validate the values of memory and execution scopes are valid scope
values
* new test

* Remove SPIR-V 1.4 Vulkan 1.0 environment

* SPIR-V 1.4 requires Vulkan 1.1

* FIX: include string for spvLog

* FIX: validate nonwritable

* FIX: test case suite for member decorate string

* FIX: test case for hlsl functionality1

* Validation test fixture: ease debugging

* Use binary version for SPIR-V 1.4 specific features

* Switch checks based on the SPIR-V version from the target environment
to instead use the version from the binary
* Moved header parsing into the ValidationState_t constructor (where
version based features are set)
* Added new versions of tests that assemble a 1.3 binary and validate a
1.4 environment

* Fix test for update to SPIR-V 1.4 headers

* Fix formatting

* Ext inst lookup: Add Vulkan 1.1 env with SPIR-V 1.4

* Update spirv-val help

* Operand version checks should use module version

Use the module version instead of the target environment version.

* Fix comment about two-access form of OpCopyMemory
2019-05-07 12:27:18 -04:00

1367 lines
44 KiB
C++

// Copyright (c) 2018 Google LLC.
//
// 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 "test/unit_spirv.h"
#include "test/val/val_fixtures.h"
namespace spvtools {
namespace val {
namespace {
using ::testing::HasSubstr;
using ::testing::Not;
using ValidateBarriers = spvtest::ValidateBase<bool>;
std::string GenerateShaderCodeImpl(
const std::string& body, const std::string& capabilities_and_extensions,
const std::string& definitions, const std::string& execution_model,
const std::string& memory_model) {
std::ostringstream ss;
ss << R"(
OpCapability Shader
)";
ss << capabilities_and_extensions;
ss << memory_model << std::endl;
ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
if (execution_model == "Fragment") {
ss << "OpExecutionMode %main OriginUpperLeft\n";
} else if (execution_model == "Geometry") {
ss << "OpExecutionMode %main InputPoints\n";
ss << "OpExecutionMode %main OutputPoints\n";
} else if (execution_model == "GLCompute") {
ss << "OpExecutionMode %main LocalSize 1 1 1\n";
}
ss << R"(
%void = OpTypeVoid
%func = OpTypeFunction %void
%bool = OpTypeBool
%f32 = OpTypeFloat 32
%u32 = OpTypeInt 32 0
%f32_0 = OpConstant %f32 0
%f32_1 = OpConstant %f32 1
%u32_0 = OpConstant %u32 0
%u32_1 = OpConstant %u32 1
%u32_4 = OpConstant %u32 4
)";
ss << definitions;
ss << R"(
%cross_device = OpConstant %u32 0
%device = OpConstant %u32 1
%workgroup = OpConstant %u32 2
%subgroup = OpConstant %u32 3
%invocation = OpConstant %u32 4
%queuefamily = OpConstant %u32 5
%none = OpConstant %u32 0
%acquire = OpConstant %u32 2
%release = OpConstant %u32 4
%acquire_release = OpConstant %u32 8
%acquire_and_release = OpConstant %u32 6
%sequentially_consistent = OpConstant %u32 16
%acquire_release_uniform_workgroup = OpConstant %u32 328
%acquire_uniform_workgroup = OpConstant %u32 322
%release_uniform_workgroup = OpConstant %u32 324
%acquire_and_release_uniform = OpConstant %u32 70
%acquire_release_subgroup = OpConstant %u32 136
%uniform = OpConstant %u32 64
%uniform_workgroup = OpConstant %u32 320
%main = OpFunction %void None %func
%main_entry = OpLabel
)";
ss << body;
ss << R"(
OpReturn
OpFunctionEnd)";
return ss.str();
}
std::string GenerateShaderCode(
const std::string& body,
const std::string& capabilities_and_extensions = "",
const std::string& execution_model = "GLCompute") {
const std::string int64_capability = R"(
OpCapability Int64
)";
const std::string int64_declarations = R"(
%u64 = OpTypeInt 64 0
%u64_0 = OpConstant %u64 0
%u64_1 = OpConstant %u64 1
)";
const std::string memory_model = "OpMemoryModel Logical GLSL450";
return GenerateShaderCodeImpl(
body, int64_capability + capabilities_and_extensions, int64_declarations,
execution_model, memory_model);
}
std::string GenerateWebGPUShaderCode(
const std::string& body,
const std::string& capabilities_and_extensions = "",
const std::string& execution_model = "GLCompute") {
const std::string vulkan_memory_capability = R"(
OpCapability VulkanMemoryModelKHR
)";
const std::string vulkan_memory_extension = R"(
OpExtension "SPV_KHR_vulkan_memory_model"
)";
const std::string memory_model = "OpMemoryModel Logical VulkanKHR";
return GenerateShaderCodeImpl(body,
vulkan_memory_capability +
capabilities_and_extensions +
vulkan_memory_extension,
"", execution_model, memory_model);
}
std::string GenerateKernelCode(
const std::string& body,
const std::string& capabilities_and_extensions = "") {
std::ostringstream ss;
ss << R"(
OpCapability Addresses
OpCapability Kernel
OpCapability Linkage
OpCapability Int64
OpCapability NamedBarrier
)";
ss << capabilities_and_extensions;
ss << R"(
OpMemoryModel Physical32 OpenCL
%void = OpTypeVoid
%func = OpTypeFunction %void
%bool = OpTypeBool
%f32 = OpTypeFloat 32
%u32 = OpTypeInt 32 0
%u64 = OpTypeInt 64 0
%f32_0 = OpConstant %f32 0
%f32_1 = OpConstant %f32 1
%f32_4 = OpConstant %f32 4
%u32_0 = OpConstant %u32 0
%u32_1 = OpConstant %u32 1
%u32_4 = OpConstant %u32 4
%u64_0 = OpConstant %u64 0
%u64_1 = OpConstant %u64 1
%u64_4 = OpConstant %u64 4
%cross_device = OpConstant %u32 0
%device = OpConstant %u32 1
%workgroup = OpConstant %u32 2
%subgroup = OpConstant %u32 3
%invocation = OpConstant %u32 4
%none = OpConstant %u32 0
%acquire = OpConstant %u32 2
%release = OpConstant %u32 4
%acquire_release = OpConstant %u32 8
%acquire_and_release = OpConstant %u32 6
%sequentially_consistent = OpConstant %u32 16
%acquire_release_workgroup = OpConstant %u32 264
%named_barrier = OpTypeNamedBarrier
%main = OpFunction %void None %func
%main_entry = OpLabel
)";
ss << body;
ss << R"(
OpReturn
OpFunctionEnd)";
return ss.str();
}
TEST_F(ValidateBarriers, OpControlBarrierGLComputeSuccess) {
const std::string body = R"(
OpControlBarrier %device %device %none
OpControlBarrier %workgroup %workgroup %acquire
OpControlBarrier %workgroup %device %release
OpControlBarrier %cross_device %cross_device %acquire_release
OpControlBarrier %cross_device %cross_device %sequentially_consistent
OpControlBarrier %cross_device %cross_device %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateBarriers, OpControlBarrierKernelSuccess) {
const std::string body = R"(
OpControlBarrier %device %device %none
OpControlBarrier %workgroup %workgroup %acquire
OpControlBarrier %workgroup %device %release
OpControlBarrier %cross_device %cross_device %acquire_release
OpControlBarrier %cross_device %cross_device %sequentially_consistent
OpControlBarrier %cross_device %cross_device %acquire_release_workgroup
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
}
TEST_F(ValidateBarriers, OpControlBarrierTesselationControlSuccess) {
const std::string body = R"(
OpControlBarrier %device %device %none
OpControlBarrier %workgroup %workgroup %acquire
OpControlBarrier %workgroup %device %release
OpControlBarrier %cross_device %cross_device %acquire_release
OpControlBarrier %cross_device %cross_device %sequentially_consistent
OpControlBarrier %cross_device %cross_device %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n",
"TessellationControl"));
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateBarriers, OpControlBarrierVulkanSuccess) {
const std::string body = R"(
OpControlBarrier %workgroup %device %none
OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateBarriers, OpControlBarrierWebGPUAcquireReleaseSuccess) {
const std::string body = R"(
OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
TEST_F(ValidateBarriers, OpControlBarrierWebGPURelaxedFailure) {
const std::string body = R"(
OpControlBarrier %workgroup %workgroup %uniform_workgroup
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("WebGPU spec requires AcquireRelease to set"));
}
TEST_F(ValidateBarriers, OpControlBarrierWebGPUAcquireFailure) {
const std::string body = R"(
OpControlBarrier %workgroup %workgroup %acquire_uniform_workgroup
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("WebGPU spec disallows any bit masks in Memory Semantics"));
}
TEST_F(ValidateBarriers, OpControlBarrierWebGPUReleaseFailure) {
const std::string body = R"(
OpControlBarrier %workgroup %workgroup %release_uniform_workgroup
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("WebGPU spec disallows any bit masks in Memory Semantics"));
}
TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv12) {
const std::string body = R"(
OpControlBarrier %device %device %none
)";
CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"),
SPV_ENV_UNIVERSAL_1_2);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpControlBarrier requires one of the following Execution "
"Models: TessellationControl, GLCompute or Kernel"));
}
TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv13) {
const std::string body = R"(
OpControlBarrier %device %device %none
)";
CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"),
SPV_ENV_UNIVERSAL_1_3);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}
TEST_F(ValidateBarriers, OpControlBarrierFloatExecutionScope) {
const std::string body = R"(
OpControlBarrier %f32_1 %device %none
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("ControlBarrier: expected Execution Scope to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpControlBarrierU64ExecutionScope) {
const std::string body = R"(
OpControlBarrier %u64_1 %device %none
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("ControlBarrier: expected Execution Scope to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpControlBarrierFloatMemoryScope) {
const std::string body = R"(
OpControlBarrier %device %f32_1 %none
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("ControlBarrier: expected Memory Scope to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpControlBarrierU64MemoryScope) {
const std::string body = R"(
OpControlBarrier %device %u64_1 %none
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("ControlBarrier: expected Memory Scope to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpControlBarrierFloatMemorySemantics) {
const std::string body = R"(
OpControlBarrier %device %device %f32_0
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"ControlBarrier: expected Memory Semantics to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpControlBarrierU64MemorySemantics) {
const std::string body = R"(
OpControlBarrier %device %device %u64_0
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"ControlBarrier: expected Memory Semantics to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpControlBarrierVulkanExecutionScopeDevice) {
const std::string body = R"(
OpControlBarrier %device %workgroup %none
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: in Vulkan environment Execution Scope "
"is limited to Workgroup and Subgroup"));
}
TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeDevice) {
const std::string body = R"(
OpControlBarrier %device %workgroup %none
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: in WebGPU environment Execution Scope "
"is limited to Workgroup and Subgroup"));
}
TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroup) {
const std::string body = R"(
OpControlBarrier %subgroup %subgroup %none
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("ControlBarrier: in Vulkan 1.0 environment Memory Scope is "
"limited to Device, Workgroup and Invocation"));
}
TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeSubgroup) {
const std::string body = R"(
OpControlBarrier %subgroup %subgroup %none
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeCrossDevice) {
const std::string body = R"(
OpControlBarrier %subgroup %cross_device %none
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: in Vulkan environment, Memory Scope "
"cannot be CrossDevice"));
}
TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) {
const std::string body = R"(
OpControlBarrier %device %device %acquire_and_release_uniform
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: Memory Semantics can have at most one "
"of the following bits set: Acquire, Release, "
"AcquireRelease or SequentiallyConsistent"));
}
// TODO(atgoo@github.com): the corresponding check fails Vulkan CTS,
// reenable once fixed.
TEST_F(ValidateBarriers, DISABLED_OpControlBarrierVulkanSubgroupStorageClass) {
const std::string body = R"(
OpControlBarrier %workgroup %device %acquire_release_subgroup
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"ControlBarrier: expected Memory Semantics to include a "
"Vulkan-supported storage class if Memory Semantics is not None"));
}
TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p1) {
const std::string body = R"(
OpControlBarrier %subgroup %subgroup %acquire_release_subgroup
)";
CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"),
SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionFragment1p1) {
const std::string body = R"(
OpControlBarrier %workgroup %workgroup %acquire_release
)";
CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"),
SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpControlBarrier execution scope must be Subgroup for "
"Fragment, Vertex, Geometry and TessellationEvaluation "
"execution models"));
}
TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p0) {
const std::string body = R"(
OpControlBarrier %subgroup %workgroup %acquire_release
)";
CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"),
SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpControlBarrier requires one of the following Execution "
"Models: TessellationControl, GLCompute or Kernel"));
}
TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p1) {
const std::string body = R"(
OpControlBarrier %subgroup %subgroup %acquire_release_subgroup
)";
CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"),
SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionVertex1p1) {
const std::string body = R"(
OpControlBarrier %workgroup %workgroup %acquire_release
)";
CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"),
SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpControlBarrier execution scope must be Subgroup for "
"Fragment, Vertex, Geometry and TessellationEvaluation "
"execution models"));
}
TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p0) {
const std::string body = R"(
OpControlBarrier %subgroup %workgroup %acquire_release
)";
CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"),
SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpControlBarrier requires one of the following Execution "
"Models: TessellationControl, GLCompute or Kernel"));
}
TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p1) {
const std::string body = R"(
OpControlBarrier %subgroup %subgroup %acquire_release_subgroup
)";
CompileSuccessfully(
GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"),
SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionGeometry1p1) {
const std::string body = R"(
OpControlBarrier %workgroup %workgroup %acquire_release
)";
CompileSuccessfully(
GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"),
SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpControlBarrier execution scope must be Subgroup for "
"Fragment, Vertex, Geometry and TessellationEvaluation "
"execution models"));
}
TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p0) {
const std::string body = R"(
OpControlBarrier %subgroup %workgroup %acquire_release
)";
CompileSuccessfully(
GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"),
SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpControlBarrier requires one of the following Execution "
"Models: TessellationControl, GLCompute or Kernel"));
}
TEST_F(ValidateBarriers,
OpControlBarrierSubgroupExecutionTessellationEvaluation1p1) {
const std::string body = R"(
OpControlBarrier %subgroup %subgroup %acquire_release_subgroup
)";
CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n",
"TessellationEvaluation"),
SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateBarriers,
OpControlBarrierWorkgroupExecutionTessellationEvaluation1p1) {
const std::string body = R"(
OpControlBarrier %workgroup %workgroup %acquire_release
)";
CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n",
"TessellationEvaluation"),
SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpControlBarrier execution scope must be Subgroup for "
"Fragment, Vertex, Geometry and TessellationEvaluation "
"execution models"));
}
TEST_F(ValidateBarriers,
OpControlBarrierSubgroupExecutionTessellationEvaluation1p0) {
const std::string body = R"(
OpControlBarrier %subgroup %workgroup %acquire_release
)";
CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n",
"TessellationEvaluation"),
SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpControlBarrier requires one of the following Execution "
"Models: TessellationControl, GLCompute or Kernel"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierSuccess) {
const std::string body = R"(
OpMemoryBarrier %cross_device %acquire_release_uniform_workgroup
OpMemoryBarrier %device %uniform
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateBarriers, OpMemoryBarrierKernelSuccess) {
const std::string body = R"(
OpMemoryBarrier %cross_device %acquire_release_workgroup
OpMemoryBarrier %device %none
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
}
TEST_F(ValidateBarriers, OpMemoryBarrierVulkanSuccess) {
const std::string body = R"(
OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireReleaseSuccess) {
const std::string body = R"(
OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPURelaxedFailure) {
const std::string body = R"(
OpMemoryBarrier %workgroup %uniform_workgroup
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("WebGPU spec requires AcquireRelease to set"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireFailure) {
const std::string body = R"(
OpMemoryBarrier %workgroup %acquire_uniform_workgroup
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("WebGPU spec disallows any bit masks in Memory Semantics"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUReleaseFailure) {
const std::string body = R"(
OpMemoryBarrier %workgroup %release_uniform_workgroup
)";
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("WebGPU spec disallows any bit masks in Memory Semantics"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) {
const std::string body = R"(
OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("MemoryBarrier: expected Memory Scope to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierU64MemoryScope) {
const std::string body = R"(
OpMemoryBarrier %u64_1 %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("MemoryBarrier: expected Memory Scope to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemorySemantics) {
const std::string body = R"(
OpMemoryBarrier %device %f32_0
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("MemoryBarrier: expected Memory Semantics to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierU64MemorySemantics) {
const std::string body = R"(
OpMemoryBarrier %device %u64_0
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("MemoryBarrier: expected Memory Semantics to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemoryScopeSubgroup) {
const std::string body = R"(
OpMemoryBarrier %subgroup %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("MemoryBarrier: in Vulkan 1.0 environment Memory Scope is "
"limited to Device, Workgroup and Invocation"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierVulkan1p1MemoryScopeSubgroup) {
const std::string body = R"(
OpMemoryBarrier %subgroup %acquire_release_uniform_workgroup
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateBarriers, OpMemoryBarrierAcquireAndRelease) {
const std::string body = R"(
OpMemoryBarrier %device %acquire_and_release_uniform
)";
CompileSuccessfully(GenerateShaderCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("MemoryBarrier: Memory Semantics can have at most one "
"of the following bits set: Acquire, Release, "
"AcquireRelease or SequentiallyConsistent"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsNone) {
const std::string body = R"(
OpMemoryBarrier %device %none
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("MemoryBarrier: Vulkan specification requires Memory Semantics "
"to have one of the following bits set: Acquire, Release, "
"AcquireRelease or SequentiallyConsistent"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsAcquire) {
const std::string body = R"(
OpMemoryBarrier %device %acquire
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("MemoryBarrier: expected Memory Semantics to include a "
"Vulkan-supported storage class"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierVulkanSubgroupStorageClass) {
const std::string body = R"(
OpMemoryBarrier %device %acquire_release_subgroup
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("MemoryBarrier: expected Memory Semantics to include a "
"Vulkan-supported storage class"));
}
TEST_F(ValidateBarriers, OpNamedBarrierInitializeSuccess) {
const std::string body = R"(
%barrier = OpNamedBarrierInitialize %named_barrier %u32_4
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
}
TEST_F(ValidateBarriers, OpNamedBarrierInitializeWrongResultType) {
const std::string body = R"(
%barrier = OpNamedBarrierInitialize %u32 %u32_4
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("NamedBarrierInitialize: expected Result Type to be "
"OpTypeNamedBarrier"));
}
TEST_F(ValidateBarriers, OpNamedBarrierInitializeFloatSubgroupCount) {
const std::string body = R"(
%barrier = OpNamedBarrierInitialize %named_barrier %f32_4
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("NamedBarrierInitialize: expected Subgroup Count to be "
"a 32-bit int"));
}
TEST_F(ValidateBarriers, OpNamedBarrierInitializeU64SubgroupCount) {
const std::string body = R"(
%barrier = OpNamedBarrierInitialize %named_barrier %u64_4
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("NamedBarrierInitialize: expected Subgroup Count to be "
"a 32-bit int"));
}
TEST_F(ValidateBarriers, OpMemoryNamedBarrierSuccess) {
const std::string body = R"(
%barrier = OpNamedBarrierInitialize %named_barrier %u32_4
OpMemoryNamedBarrier %barrier %workgroup %acquire_release_workgroup
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
}
TEST_F(ValidateBarriers, OpMemoryNamedBarrierNotNamedBarrier) {
const std::string body = R"(
OpMemoryNamedBarrier %u32_1 %workgroup %acquire_release_workgroup
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("MemoryNamedBarrier: expected Named Barrier to be of "
"type OpTypeNamedBarrier"));
}
TEST_F(ValidateBarriers, OpMemoryNamedBarrierFloatMemoryScope) {
const std::string body = R"(
%barrier = OpNamedBarrierInitialize %named_barrier %u32_4
OpMemoryNamedBarrier %barrier %f32_1 %acquire_release_workgroup
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"MemoryNamedBarrier: expected Memory Scope to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpMemoryNamedBarrierFloatMemorySemantics) {
const std::string body = R"(
%barrier = OpNamedBarrierInitialize %named_barrier %u32_4
OpMemoryNamedBarrier %barrier %workgroup %f32_0
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"MemoryNamedBarrier: expected Memory Semantics to be a 32-bit int"));
}
TEST_F(ValidateBarriers, OpMemoryNamedBarrierAcquireAndRelease) {
const std::string body = R"(
%barrier = OpNamedBarrierInitialize %named_barrier %u32_4
OpMemoryNamedBarrier %barrier %workgroup %acquire_and_release
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("MemoryNamedBarrier: Memory Semantics can have at most "
"one of the following bits set: Acquire, Release, "
"AcquireRelease or SequentiallyConsistent"));
}
TEST_F(ValidateBarriers, TypeAsMemoryScope) {
const std::string body = R"(
OpMemoryBarrier %u32 %u32_0
)";
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 5[%uint] cannot be a "
"type"));
}
TEST_F(ValidateBarriers,
OpControlBarrierVulkanMemoryModelBanSequentiallyConsistent) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeInt 32 0
%4 = OpConstant %3 16
%5 = OpTypeFunction %2
%6 = OpConstant %3 5
%1 = OpFunction %2 None %5
%7 = OpLabel
OpControlBarrier %6 %6 %4
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("SequentiallyConsistent memory semantics cannot be "
"used with the VulkanKHR memory model."));
}
TEST_F(ValidateBarriers,
OpMemoryBarrierVulkanMemoryModelBanSequentiallyConsistent) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeInt 32 0
%4 = OpConstant %3 16
%5 = OpTypeFunction %2
%6 = OpConstant %3 5
%1 = OpFunction %2 None %5
%7 = OpLabel
OpMemoryBarrier %6 %4
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("SequentiallyConsistent memory semantics cannot be "
"used with the VulkanKHR memory model."));
}
TEST_F(ValidateBarriers, OutputMemoryKHRRequireVulkanMemoryModelKHR) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeInt 32 0
%semantics = OpConstant %3 4104
%5 = OpTypeFunction %2
%device = OpConstant %3 1
%1 = OpFunction %2 None %5
%7 = OpLabel
OpControlBarrier %device %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: Memory Semantics OutputMemoryKHR "
"requires capability VulkanMemoryModelKHR"));
}
TEST_F(ValidateBarriers, MakeAvailableKHRRequireVulkanMemoryModelKHR) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeInt 32 0
%semantics = OpConstant %3 8264
%5 = OpTypeFunction %2
%device = OpConstant %3 1
%1 = OpFunction %2 None %5
%7 = OpLabel
OpControlBarrier %device %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: Memory Semantics MakeAvailableKHR "
"requires capability VulkanMemoryModelKHR"));
}
TEST_F(ValidateBarriers, MakeVisibleKHRRequireVulkanMemoryModelKHR) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeInt 32 0
%semantics = OpConstant %3 16456
%5 = OpTypeFunction %2
%device = OpConstant %3 1
%1 = OpFunction %2 None %5
%7 = OpLabel
OpControlBarrier %device %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: Memory Semantics MakeVisibleKHR "
"requires capability VulkanMemoryModelKHR"));
}
TEST_F(ValidateBarriers, MakeAvailableKHRRequiresReleaseSemantics) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 0
%workgroup = OpConstant %int 2
%semantics = OpConstant %int 8448
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpControlBarrier %workgroup %workgroup %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("ControlBarrier: MakeAvailableKHR Memory Semantics also "
"requires either Release or AcquireRelease Memory Semantics"));
}
TEST_F(ValidateBarriers, MakeVisibleKHRRequiresAcquireSemantics) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 0
%workgroup = OpConstant %int 2
%semantics = OpConstant %int 16640
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpControlBarrier %workgroup %workgroup %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("ControlBarrier: MakeVisibleKHR Memory Semantics also requires "
"either Acquire or AcquireRelease Memory Semantics"));
}
TEST_F(ValidateBarriers, MakeAvailableKHRRequiresStorageSemantics) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 0
%workgroup = OpConstant %int 2
%semantics = OpConstant %int 8196
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpMemoryBarrier %workgroup %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("MemoryBarrier: expected Memory Semantics to include a "
"storage class"));
}
TEST_F(ValidateBarriers, MakeVisibleKHRRequiresStorageSemantics) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 0
%workgroup = OpConstant %int 2
%semantics = OpConstant %int 16386
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpMemoryBarrier %workgroup %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("MemoryBarrier: expected Memory Semantics to include a "
"storage class"));
}
TEST_F(ValidateBarriers, SemanticsSpecConstantShader) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 0
%ptr_int_workgroup = OpTypePointer Workgroup %int
%var = OpVariable %ptr_int_workgroup Workgroup
%voidfn = OpTypeFunction %void
%spec_const = OpSpecConstant %int 0
%workgroup = OpConstant %int 2
%func = OpFunction %void None %voidfn
%entry = OpLabel
OpMemoryBarrier %workgroup %spec_const
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Memory Semantics ids must be OpConstant when Shader "
"capability is present"));
}
TEST_F(ValidateBarriers, SemanticsSpecConstantKernel) {
const std::string spirv = R"(
OpCapability Kernel
OpCapability Linkage
OpMemoryModel Logical OpenCL
%void = OpTypeVoid
%int = OpTypeInt 32 0
%ptr_int_workgroup = OpTypePointer Workgroup %int
%var = OpVariable %ptr_int_workgroup Workgroup
%voidfn = OpTypeFunction %void
%spec_const = OpSpecConstant %int 0
%workgroup = OpConstant %int 2
%func = OpFunction %void None %voidfn
%entry = OpLabel
OpMemoryBarrier %workgroup %spec_const
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateBarriers, ScopeSpecConstantShader) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 0
%ptr_int_workgroup = OpTypePointer Workgroup %int
%var = OpVariable %ptr_int_workgroup Workgroup
%voidfn = OpTypeFunction %void
%spec_const = OpSpecConstant %int 0
%relaxed = OpConstant %int 0
%func = OpFunction %void None %voidfn
%entry = OpLabel
OpMemoryBarrier %spec_const %relaxed
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Scope ids must be OpConstant when Shader "
"capability is present"));
}
TEST_F(ValidateBarriers, ScopeSpecConstantKernel) {
const std::string spirv = R"(
OpCapability Kernel
OpCapability Linkage
OpMemoryModel Logical OpenCL
%void = OpTypeVoid
%int = OpTypeInt 32 0
%ptr_int_workgroup = OpTypePointer Workgroup %int
%var = OpVariable %ptr_int_workgroup Workgroup
%voidfn = OpTypeFunction %void
%spec_const = OpSpecConstant %int 0
%relaxed = OpConstant %int 0
%func = OpFunction %void None %voidfn
%entry = OpLabel
OpMemoryBarrier %spec_const %relaxed
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateBarriers, VulkanMemoryModelDeviceScopeBad) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%semantics = OpConstant %int 0
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpMemoryBarrier %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Use of device scope with VulkanKHR memory model requires the "
"VulkanMemoryModelDeviceScopeKHR capability"));
}
TEST_F(ValidateBarriers, VulkanMemoryModelDeviceScopeGood) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability VulkanMemoryModelDeviceScopeKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%semantics = OpConstant %int 0
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpMemoryBarrier %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}
} // namespace
} // namespace val
} // namespace spvtools