SPIRV-Tools/test/text_to_binary.memory_test.cpp
Shahbaz Youssefi 7564e142d6
spirv-dis: Add --nested-indent and --reorder-blocks (#5671)
With --nested-indent, the SPIR-V blocks are nested according to the
structured control flow.  Each OpLabel is nested that much with the
contents of the block nested a little more.  The blocks are separated by
a blank line for better visualization.

With --reorder-blocks, the SPIR-V blocks are reordered according to the
structured control flow.  This is particularly useful with
--nested-indent.

Note that with --nested-indent, the disassembly does not exactly show
the binary as-is, and the instructions may be reordered.
2024-06-17 09:54:18 -04:00

413 lines
16 KiB
C++

// 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 <sstream>
#include <string>
#include <vector>
#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<EnumCase<spv::MemoryAccessMask>>>;
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<EnumCase<spv::MemoryAccessMask>>{
{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<EnumCase<spv::StorageClass>>>;
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<EnumCase<spv::StorageClass>>{
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_TEXT_TO_BINARY_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_TEXT_TO_BINARY_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_TEXT_TO_BINARY_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_TEXT_TO_BINARY_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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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