// Copyright 2021 The Tint Authors. // // 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 "gmock/gmock.h" #include "src/ast/call_statement.h" #include "src/reader/spirv/function.h" #include "src/reader/spirv/parser_impl_test_helper.h" #include "src/reader/spirv/spirv_tools_helpers_test.h" #include "src/sem/call.h" namespace tint { namespace reader { namespace spirv { namespace { using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Not; using ::testing::StartsWith; Program ParseAndBuild(std::string spirv) { const char* preamble = R"(OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpName %main "main" )"; auto p = std::make_unique(test::Assemble(preamble + spirv)); if (!p->BuildAndParseInternalModule()) { ProgramBuilder builder; builder.Diagnostics().add_error(diag::System::Reader, p->error()); return Program(std::move(builder)); } return p->program(); } TEST_F(SpvParserTest, WorkgroupBarrier) { auto program = ParseAndBuild(R"( OpName %helper "helper" %void = OpTypeVoid %1 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_2 = OpConstant %uint 2 %uint_264 = OpConstant %uint 264 %helper = OpFunction %void None %1 %4 = OpLabel OpControlBarrier %uint_2 %uint_2 %uint_264 OpReturn OpFunctionEnd %main = OpFunction %void None %1 %5 = OpLabel OpReturn OpFunctionEnd )"); ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str(); auto* helper = program.AST().Functions().Find(program.Symbols().Get("helper")); ASSERT_NE(helper, nullptr); ASSERT_GT(helper->body->statements.size(), 0u); auto* call = helper->body->statements[0]->As(); ASSERT_NE(call, nullptr); EXPECT_EQ(call->expr->args.size(), 0u); auto* sem_call = program.Sem().Get(call->expr); ASSERT_NE(sem_call, nullptr); auto* builtin = sem_call->Target()->As(); ASSERT_NE(builtin, nullptr); EXPECT_EQ(builtin->Type(), sem::BuiltinType::kWorkgroupBarrier); } TEST_F(SpvParserTest, StorageBarrier) { auto program = ParseAndBuild(R"( OpName %helper "helper" %void = OpTypeVoid %1 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_2 = OpConstant %uint 2 %uint_1 = OpConstant %uint 1 %uint_72 = OpConstant %uint 72 %helper = OpFunction %void None %1 %4 = OpLabel OpControlBarrier %uint_2 %uint_1 %uint_72 OpReturn OpFunctionEnd %main = OpFunction %void None %1 %5 = OpLabel OpReturn OpFunctionEnd )"); ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str(); auto* helper = program.AST().Functions().Find(program.Symbols().Get("helper")); ASSERT_NE(helper, nullptr); ASSERT_GT(helper->body->statements.size(), 0u); auto* call = helper->body->statements[0]->As(); ASSERT_NE(call, nullptr); EXPECT_EQ(call->expr->args.size(), 0u); auto* sem_call = program.Sem().Get(call->expr); ASSERT_NE(sem_call, nullptr); auto* builtin = sem_call->Target()->As(); ASSERT_NE(builtin, nullptr); EXPECT_EQ(builtin->Type(), sem::BuiltinType::kStorageBarrier); } TEST_F(SpvParserTest, ErrBarrierInvalidExecution) { auto program = ParseAndBuild(R"( %void = OpTypeVoid %1 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_2 = OpConstant %uint 2 %uint_264 = OpConstant %uint 264 %main = OpFunction %void None %1 %4 = OpLabel OpControlBarrier %uint_0 %uint_2 %uint_264 OpReturn OpFunctionEnd )"); EXPECT_FALSE(program.IsValid()); EXPECT_THAT(program.Diagnostics().str(), HasSubstr("unsupported control barrier execution scope")); } TEST_F(SpvParserTest, ErrBarrierSemanticsMissingAcquireRelease) { auto program = ParseAndBuild(R"( %void = OpTypeVoid %1 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_2 = OpConstant %uint 2 %uint_0 = OpConstant %uint 0 %main = OpFunction %void None %1 %4 = OpLabel OpControlBarrier %uint_2 %uint_2 %uint_0 OpReturn OpFunctionEnd )"); EXPECT_FALSE(program.IsValid()); EXPECT_THAT( program.Diagnostics().str(), HasSubstr("control barrier semantics requires acquire and release")); } TEST_F(SpvParserTest, ErrBarrierInvalidSemantics) { auto program = ParseAndBuild(R"( %void = OpTypeVoid %1 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_2 = OpConstant %uint 2 %uint_9 = OpConstant %uint 9 %main = OpFunction %void None %1 %4 = OpLabel OpControlBarrier %uint_2 %uint_2 %uint_9 OpReturn OpFunctionEnd )"); EXPECT_FALSE(program.IsValid()); EXPECT_THAT(program.Diagnostics().str(), HasSubstr("unsupported control barrier semantics")); } TEST_F(SpvParserTest, ErrWorkgroupBarrierInvalidMemory) { auto program = ParseAndBuild(R"( %void = OpTypeVoid %1 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_2 = OpConstant %uint 2 %uint_8 = OpConstant %uint 8 %uint_264 = OpConstant %uint 264 %main = OpFunction %void None %1 %4 = OpLabel OpControlBarrier %uint_2 %uint_8 %uint_264 OpReturn OpFunctionEnd )"); EXPECT_FALSE(program.IsValid()); EXPECT_THAT(program.Diagnostics().str(), HasSubstr("workgroupBarrier requires workgroup memory scope")); } TEST_F(SpvParserTest, ErrStorageBarrierInvalidMemory) { auto program = ParseAndBuild(R"( %void = OpTypeVoid %1 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_2 = OpConstant %uint 2 %uint_8 = OpConstant %uint 8 %uint_72 = OpConstant %uint 72 %main = OpFunction %void None %1 %4 = OpLabel OpControlBarrier %uint_2 %uint_8 %uint_72 OpReturn OpFunctionEnd )"); EXPECT_FALSE(program.IsValid()); EXPECT_THAT(program.Diagnostics().str(), HasSubstr("storageBarrier requires device memory scope")); } } // namespace } // namespace spirv } // namespace reader } // namespace tint