// Copyright (c) 2015-2016 The Khronos Group 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. // Assembler tests for instructions in the "Memory Instructions" section of // the SPIR-V spec. #include #include #include #include "gmock/gmock.h" #include "test/test_fixture.h" #include "test/unit_spirv.h" namespace spvtools { namespace { using spvtest::EnumCase; using spvtest::MakeInstruction; using spvtest::TextToBinaryTest; using ::testing::Eq; using ::testing::HasSubstr; // Test assembly of Memory Access masks using MemoryAccessTest = spvtest::TextToBinaryTestBase< ::testing::TestWithParam>>; TEST_P(MemoryAccessTest, AnySingleMemoryAccessMask) { std::stringstream input; input << "OpStore %ptr %value " << GetParam().name(); for (auto operand : GetParam().operands()) input << " " << operand; EXPECT_THAT( CompiledInstructions(input.str()), Eq(MakeInstruction(spv::Op::OpStore, {1, 2, (uint32_t)GetParam().value()}, GetParam().operands()))); } INSTANTIATE_TEST_SUITE_P( TextToBinaryMemoryAccessTest, MemoryAccessTest, ::testing::ValuesIn(std::vector>{ {spv::MemoryAccessMask::MaskNone, "None", {}}, {spv::MemoryAccessMask::Volatile, "Volatile", {}}, {spv::MemoryAccessMask::Aligned, "Aligned", {16}}, {spv::MemoryAccessMask::Nontemporal, "Nontemporal", {}}, })); TEST_F(TextToBinaryTest, CombinedMemoryAccessMask) { const std::string input = "OpStore %ptr %value Volatile|Aligned 16"; const uint32_t expected_mask = uint32_t(spv::MemoryAccessMask::Volatile | spv::MemoryAccessMask::Aligned); EXPECT_THAT(expected_mask, Eq(3u)); EXPECT_THAT(CompiledInstructions(input), Eq(MakeInstruction(spv::Op::OpStore, {1, 2, expected_mask, 16}))); } // Test Storage Class enum values using StorageClassTest = spvtest::TextToBinaryTestBase< ::testing::TestWithParam>>; TEST_P(StorageClassTest, AnyStorageClass) { const std::string input = "%1 = OpVariable %2 " + GetParam().name(); EXPECT_THAT(CompiledInstructions(input), Eq(MakeInstruction(spv::Op::OpVariable, {1, 2, (uint32_t)GetParam().value()}))); } // clang-format off #define CASE(NAME) { spv::StorageClass::NAME, #NAME, {} } INSTANTIATE_TEST_SUITE_P( TextToBinaryStorageClassTest, StorageClassTest, ::testing::ValuesIn(std::vector>{ CASE(UniformConstant), CASE(Input), CASE(Uniform), CASE(Output), CASE(Workgroup), CASE(CrossWorkgroup), CASE(Private), CASE(Function), CASE(Generic), CASE(PushConstant), CASE(AtomicCounter), CASE(Image), })); #undef CASE // clang-format on using MemoryRoundTripTest = RoundTripTest; // OpPtrEqual appeared in SPIR-V 1.4 TEST_F(MemoryRoundTripTest, OpPtrEqualGood) { std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n"; EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), Eq(MakeInstruction(spv::Op::OpPtrEqual, {1, 2, 3, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully( spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpPtrEqualV13Bad) { std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n"; std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrEqual'")); } // OpPtrNotEqual appeared in SPIR-V 1.4 TEST_F(MemoryRoundTripTest, OpPtrNotEqualGood) { std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n"; EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), Eq(MakeInstruction(spv::Op::OpPtrNotEqual, {1, 2, 3, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully( spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpPtrNotEqualV13Bad) { std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n"; std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrNotEqual'")); } // OpPtrDiff appeared in SPIR-V 1.4 TEST_F(MemoryRoundTripTest, OpPtrDiffGood) { std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n"; EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), Eq(MakeInstruction(spv::Op::OpPtrDiff, {1, 2, 3, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully( spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpPtrDiffV13Good) { // OpPtrDiff is enabled by a capability as well, so we can assemble // it even in older SPIR-V environments. We do that so we can // write tests. std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n"; std::string disassembly = EncodeAndDecodeSuccessfully( spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); } // OpCopyMemory TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) { std::string spirv = "OpCopyMemory %1 %2\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) { std::string spirv = "OpCopyMemory %1\n"; std::string err = CompileFailure(spirv); EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemory instruction, " "but found the end of the stream.")); } TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) { std::string spirv = "OpCopyMemory %1 %2 %3\n"; std::string err = CompileFailure(spirv); EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%3'")); } TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNoneGood) { std::string spirv = "OpCopyMemory %1 %2 None\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 0}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVolatileGood) { std::string spirv = "OpCopyMemory %1 %2 Volatile\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAligned8Good) { std::string spirv = "OpCopyMemory %1 %2 Aligned 8\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 2, 8}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNontemporalGood) { std::string spirv = "OpCopyMemory %1 %2 Nontemporal\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAvGood) { std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailable %3\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 8, 3}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVisGood) { std::string spirv = "OpCopyMemory %1 %2 MakePointerVisible %3\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 16, 3}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNonPrivateGood) { std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointer\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 32}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessMixedGood) { std::string spirv = "OpCopyMemory %1 %2 " "Volatile|Aligned|Nontemporal|MakePointerAvailable|" "MakePointerVisible|NonPrivatePointer 16 %3 %4\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 63, 16, 3, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV13Good) { std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n"; // Note: This will assemble but should not validate for SPIR-V 1.3 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1, 1}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV14Good) { std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n"; EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1, 1}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessMixedV14Good) { std::string spirv = "OpCopyMemory %1 %2 Volatile|Nontemporal|" "MakePointerVisible %3 " "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4\n"; EXPECT_THAT( CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 21, 3, 42, 16, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } // OpCopyMemorySized TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) { std::string spirv = "OpCopyMemorySized %1 %2 %3\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) { std::string spirv = "OpCopyMemorySized %1 %2\n"; std::string err = CompileFailure(spirv); EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemorySized " "instruction, but found the end of the stream.")); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) { std::string spirv = "OpCopyMemorySized %1 %2 %3 %4\n"; std::string err = CompileFailure(spirv); EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%4'")); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNoneGood) { std::string spirv = "OpCopyMemorySized %1 %2 %3 None\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 0}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVolatileGood) { std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAligned8Good) { std::string spirv = "OpCopyMemorySized %1 %2 %3 Aligned 8\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 2, 8}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNontemporalGood) { std::string spirv = "OpCopyMemorySized %1 %2 %3 Nontemporal\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAvGood) { std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailable %4\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 8, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVisGood) { std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisible %4\n"; EXPECT_THAT( CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 16, 4}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNonPrivateGood) { std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointer\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 32}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessMixedGood) { std::string spirv = "OpCopyMemorySized %1 %2 %3 " "Volatile|Aligned|Nontemporal|MakePointerAvailable|" "MakePointerVisible|NonPrivatePointer 16 %4 %5\n"; EXPECT_THAT( CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV13Good) { std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n"; // Note: This will assemble but should not validate for SPIR-V 1.3 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1, 1}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV14Good) { std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n"; EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1, 1}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessMixedV14Good) { std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile|Nontemporal|" "MakePointerVisible %4 " "Aligned|MakePointerAvailable|NonPrivatePointer 16 %5\n"; EXPECT_THAT(CompiledInstructions(spirv), Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 21, 4, 42, 16, 5}))); std::string disassembly = EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); EXPECT_THAT(disassembly, Eq(spirv)); } // TODO(dneto): OpVariable with initializers // TODO(dneto): OpImageTexelPointer // TODO(dneto): OpLoad // TODO(dneto): OpStore // TODO(dneto): OpAccessChain // TODO(dneto): OpInBoundsAccessChain // TODO(dneto): OpPtrAccessChain // TODO(dneto): OpArrayLength // TODO(dneto): OpGenercPtrMemSemantics } // namespace } // namespace spvtools