1// Copyright (c) 2015-2016 The Khronos Group Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Assembler tests for instructions in the "Memory Instructions" section of 16// the SPIR-V spec. 17 18#include <sstream> 19#include <string> 20#include <vector> 21 22#include "gmock/gmock.h" 23#include "test/test_fixture.h" 24#include "test/unit_spirv.h" 25 26namespace spvtools { 27namespace { 28 29using spvtest::EnumCase; 30using spvtest::MakeInstruction; 31using spvtest::TextToBinaryTest; 32using ::testing::Eq; 33using ::testing::HasSubstr; 34 35// Test assembly of Memory Access masks 36 37using MemoryAccessTest = spvtest::TextToBinaryTestBase< 38 ::testing::TestWithParam<EnumCase<spv::MemoryAccessMask>>>; 39 40TEST_P(MemoryAccessTest, AnySingleMemoryAccessMask) { 41 std::stringstream input; 42 input << "OpStore %ptr %value " << GetParam().name(); 43 for (auto operand : GetParam().operands()) input << " " << operand; 44 EXPECT_THAT( 45 CompiledInstructions(input.str()), 46 Eq(MakeInstruction(spv::Op::OpStore, {1, 2, (uint32_t)GetParam().value()}, 47 GetParam().operands()))); 48} 49 50INSTANTIATE_TEST_SUITE_P( 51 TextToBinaryMemoryAccessTest, MemoryAccessTest, 52 ::testing::ValuesIn(std::vector<EnumCase<spv::MemoryAccessMask>>{ 53 {spv::MemoryAccessMask::MaskNone, "None", {}}, 54 {spv::MemoryAccessMask::Volatile, "Volatile", {}}, 55 {spv::MemoryAccessMask::Aligned, "Aligned", {16}}, 56 {spv::MemoryAccessMask::Nontemporal, "Nontemporal", {}}, 57 })); 58 59TEST_F(TextToBinaryTest, CombinedMemoryAccessMask) { 60 const std::string input = "OpStore %ptr %value Volatile|Aligned 16"; 61 const uint32_t expected_mask = uint32_t(spv::MemoryAccessMask::Volatile | 62 spv::MemoryAccessMask::Aligned); 63 EXPECT_THAT(expected_mask, Eq(3u)); 64 EXPECT_THAT(CompiledInstructions(input), 65 Eq(MakeInstruction(spv::Op::OpStore, {1, 2, expected_mask, 16}))); 66} 67 68// Test Storage Class enum values 69 70using StorageClassTest = spvtest::TextToBinaryTestBase< 71 ::testing::TestWithParam<EnumCase<spv::StorageClass>>>; 72 73TEST_P(StorageClassTest, AnyStorageClass) { 74 const std::string input = "%1 = OpVariable %2 " + GetParam().name(); 75 EXPECT_THAT(CompiledInstructions(input), 76 Eq(MakeInstruction(spv::Op::OpVariable, 77 {1, 2, (uint32_t)GetParam().value()}))); 78} 79 80// clang-format off 81#define CASE(NAME) { spv::StorageClass::NAME, #NAME, {} } 82INSTANTIATE_TEST_SUITE_P( 83 TextToBinaryStorageClassTest, StorageClassTest, 84 ::testing::ValuesIn(std::vector<EnumCase<spv::StorageClass>>{ 85 CASE(UniformConstant), 86 CASE(Input), 87 CASE(Uniform), 88 CASE(Output), 89 CASE(Workgroup), 90 CASE(CrossWorkgroup), 91 CASE(Private), 92 CASE(Function), 93 CASE(Generic), 94 CASE(PushConstant), 95 CASE(AtomicCounter), 96 CASE(Image), 97 })); 98#undef CASE 99// clang-format on 100 101using MemoryRoundTripTest = RoundTripTest; 102 103// OpPtrEqual appeared in SPIR-V 1.4 104 105TEST_F(MemoryRoundTripTest, OpPtrEqualGood) { 106 std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n"; 107 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), 108 Eq(MakeInstruction(spv::Op::OpPtrEqual, {1, 2, 3, 4}))); 109 std::string disassembly = EncodeAndDecodeSuccessfully( 110 spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); 111 EXPECT_THAT(disassembly, Eq(spirv)); 112} 113 114TEST_F(MemoryRoundTripTest, OpPtrEqualV13Bad) { 115 std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n"; 116 std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3); 117 EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrEqual'")); 118} 119 120// OpPtrNotEqual appeared in SPIR-V 1.4 121 122TEST_F(MemoryRoundTripTest, OpPtrNotEqualGood) { 123 std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n"; 124 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), 125 Eq(MakeInstruction(spv::Op::OpPtrNotEqual, {1, 2, 3, 4}))); 126 std::string disassembly = EncodeAndDecodeSuccessfully( 127 spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); 128 EXPECT_THAT(disassembly, Eq(spirv)); 129} 130 131TEST_F(MemoryRoundTripTest, OpPtrNotEqualV13Bad) { 132 std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n"; 133 std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3); 134 EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrNotEqual'")); 135} 136 137// OpPtrDiff appeared in SPIR-V 1.4 138 139TEST_F(MemoryRoundTripTest, OpPtrDiffGood) { 140 std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n"; 141 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), 142 Eq(MakeInstruction(spv::Op::OpPtrDiff, {1, 2, 3, 4}))); 143 std::string disassembly = EncodeAndDecodeSuccessfully( 144 spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); 145 EXPECT_THAT(disassembly, Eq(spirv)); 146} 147 148TEST_F(MemoryRoundTripTest, OpPtrDiffV13Good) { 149 // OpPtrDiff is enabled by a capability as well, so we can assemble 150 // it even in older SPIR-V environments. We do that so we can 151 // write tests. 152 std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n"; 153 std::string disassembly = EncodeAndDecodeSuccessfully( 154 spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); 155} 156 157// OpCopyMemory 158 159TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) { 160 std::string spirv = "OpCopyMemory %1 %2\n"; 161 EXPECT_THAT(CompiledInstructions(spirv), 162 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2}))); 163 std::string disassembly = 164 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 165 EXPECT_THAT(disassembly, Eq(spirv)); 166} 167 168TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) { 169 std::string spirv = "OpCopyMemory %1\n"; 170 std::string err = CompileFailure(spirv); 171 EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemory instruction, " 172 "but found the end of the stream.")); 173} 174 175TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) { 176 std::string spirv = "OpCopyMemory %1 %2 %3\n"; 177 std::string err = CompileFailure(spirv); 178 EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%3'")); 179} 180 181TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNoneGood) { 182 std::string spirv = "OpCopyMemory %1 %2 None\n"; 183 EXPECT_THAT(CompiledInstructions(spirv), 184 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 0}))); 185 std::string disassembly = 186 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 187 EXPECT_THAT(disassembly, Eq(spirv)); 188} 189 190TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVolatileGood) { 191 std::string spirv = "OpCopyMemory %1 %2 Volatile\n"; 192 EXPECT_THAT(CompiledInstructions(spirv), 193 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1}))); 194 std::string disassembly = 195 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 196 EXPECT_THAT(disassembly, Eq(spirv)); 197} 198 199TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAligned8Good) { 200 std::string spirv = "OpCopyMemory %1 %2 Aligned 8\n"; 201 EXPECT_THAT(CompiledInstructions(spirv), 202 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 2, 8}))); 203 std::string disassembly = 204 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 205 EXPECT_THAT(disassembly, Eq(spirv)); 206} 207 208TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNontemporalGood) { 209 std::string spirv = "OpCopyMemory %1 %2 Nontemporal\n"; 210 EXPECT_THAT(CompiledInstructions(spirv), 211 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 4}))); 212 std::string disassembly = 213 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 214 EXPECT_THAT(disassembly, Eq(spirv)); 215} 216 217TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAvGood) { 218 std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailable %3\n"; 219 EXPECT_THAT(CompiledInstructions(spirv), 220 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 8, 3}))); 221 std::string disassembly = 222 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 223 EXPECT_THAT(disassembly, Eq(spirv)); 224} 225 226TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVisGood) { 227 std::string spirv = "OpCopyMemory %1 %2 MakePointerVisible %3\n"; 228 EXPECT_THAT(CompiledInstructions(spirv), 229 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 16, 3}))); 230 std::string disassembly = 231 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 232 EXPECT_THAT(disassembly, Eq(spirv)); 233} 234 235TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNonPrivateGood) { 236 std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointer\n"; 237 EXPECT_THAT(CompiledInstructions(spirv), 238 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 32}))); 239 std::string disassembly = 240 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 241 EXPECT_THAT(disassembly, Eq(spirv)); 242} 243 244TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessMixedGood) { 245 std::string spirv = 246 "OpCopyMemory %1 %2 " 247 "Volatile|Aligned|Nontemporal|MakePointerAvailable|" 248 "MakePointerVisible|NonPrivatePointer 16 %3 %4\n"; 249 EXPECT_THAT(CompiledInstructions(spirv), 250 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 63, 16, 3, 4}))); 251 std::string disassembly = 252 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 253 EXPECT_THAT(disassembly, Eq(spirv)); 254} 255 256TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV13Good) { 257 std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n"; 258 // Note: This will assemble but should not validate for SPIR-V 1.3 259 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3), 260 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1, 1}))); 261 std::string disassembly = 262 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 263 EXPECT_THAT(disassembly, Eq(spirv)); 264} 265 266TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV14Good) { 267 std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n"; 268 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), 269 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1, 1}))); 270 std::string disassembly = 271 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 272 EXPECT_THAT(disassembly, Eq(spirv)); 273} 274 275TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessMixedV14Good) { 276 std::string spirv = 277 "OpCopyMemory %1 %2 Volatile|Nontemporal|" 278 "MakePointerVisible %3 " 279 "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4\n"; 280 EXPECT_THAT( 281 CompiledInstructions(spirv), 282 Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 21, 3, 42, 16, 4}))); 283 std::string disassembly = 284 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 285 EXPECT_THAT(disassembly, Eq(spirv)); 286} 287 288// OpCopyMemorySized 289 290TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) { 291 std::string spirv = "OpCopyMemorySized %1 %2 %3\n"; 292 EXPECT_THAT(CompiledInstructions(spirv), 293 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3}))); 294 std::string disassembly = 295 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 296 EXPECT_THAT(disassembly, Eq(spirv)); 297} 298 299TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) { 300 std::string spirv = "OpCopyMemorySized %1 %2\n"; 301 std::string err = CompileFailure(spirv); 302 EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemorySized " 303 "instruction, but found the end of the stream.")); 304} 305 306TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) { 307 std::string spirv = "OpCopyMemorySized %1 %2 %3 %4\n"; 308 std::string err = CompileFailure(spirv); 309 EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%4'")); 310} 311 312TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNoneGood) { 313 std::string spirv = "OpCopyMemorySized %1 %2 %3 None\n"; 314 EXPECT_THAT(CompiledInstructions(spirv), 315 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 0}))); 316 std::string disassembly = 317 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 318 EXPECT_THAT(disassembly, Eq(spirv)); 319} 320 321TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVolatileGood) { 322 std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile\n"; 323 EXPECT_THAT(CompiledInstructions(spirv), 324 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1}))); 325 std::string disassembly = 326 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 327 EXPECT_THAT(disassembly, Eq(spirv)); 328} 329 330TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAligned8Good) { 331 std::string spirv = "OpCopyMemorySized %1 %2 %3 Aligned 8\n"; 332 EXPECT_THAT(CompiledInstructions(spirv), 333 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 2, 8}))); 334 std::string disassembly = 335 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 336 EXPECT_THAT(disassembly, Eq(spirv)); 337} 338 339TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNontemporalGood) { 340 std::string spirv = "OpCopyMemorySized %1 %2 %3 Nontemporal\n"; 341 EXPECT_THAT(CompiledInstructions(spirv), 342 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 4}))); 343 std::string disassembly = 344 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 345 EXPECT_THAT(disassembly, Eq(spirv)); 346} 347 348TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAvGood) { 349 std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailable %4\n"; 350 EXPECT_THAT(CompiledInstructions(spirv), 351 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 8, 4}))); 352 std::string disassembly = 353 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 354 EXPECT_THAT(disassembly, Eq(spirv)); 355} 356 357TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVisGood) { 358 std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisible %4\n"; 359 EXPECT_THAT( 360 CompiledInstructions(spirv), 361 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 16, 4}))); 362 std::string disassembly = 363 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 364 EXPECT_THAT(disassembly, Eq(spirv)); 365} 366 367TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNonPrivateGood) { 368 std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointer\n"; 369 EXPECT_THAT(CompiledInstructions(spirv), 370 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 32}))); 371 std::string disassembly = 372 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 373 EXPECT_THAT(disassembly, Eq(spirv)); 374} 375 376TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessMixedGood) { 377 std::string spirv = 378 "OpCopyMemorySized %1 %2 %3 " 379 "Volatile|Aligned|Nontemporal|MakePointerAvailable|" 380 "MakePointerVisible|NonPrivatePointer 16 %4 %5\n"; 381 EXPECT_THAT( 382 CompiledInstructions(spirv), 383 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5}))); 384 std::string disassembly = 385 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 386 EXPECT_THAT(disassembly, Eq(spirv)); 387} 388 389TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV13Good) { 390 std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n"; 391 // Note: This will assemble but should not validate for SPIR-V 1.3 392 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3), 393 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1, 1}))); 394 std::string disassembly = 395 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 396 EXPECT_THAT(disassembly, Eq(spirv)); 397} 398 399TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV14Good) { 400 std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n"; 401 EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), 402 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1, 1}))); 403 std::string disassembly = 404 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 405 EXPECT_THAT(disassembly, Eq(spirv)); 406} 407 408TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessMixedV14Good) { 409 std::string spirv = 410 "OpCopyMemorySized %1 %2 %3 Volatile|Nontemporal|" 411 "MakePointerVisible %4 " 412 "Aligned|MakePointerAvailable|NonPrivatePointer 16 %5\n"; 413 EXPECT_THAT(CompiledInstructions(spirv), 414 Eq(MakeInstruction(spv::Op::OpCopyMemorySized, 415 {1, 2, 3, 21, 4, 42, 16, 5}))); 416 std::string disassembly = 417 EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); 418 EXPECT_THAT(disassembly, Eq(spirv)); 419} 420 421// TODO(dneto): OpVariable with initializers 422// TODO(dneto): OpImageTexelPointer 423// TODO(dneto): OpLoad 424// TODO(dneto): OpStore 425// TODO(dneto): OpAccessChain 426// TODO(dneto): OpInBoundsAccessChain 427// TODO(dneto): OpPtrAccessChain 428// TODO(dneto): OpArrayLength 429// TODO(dneto): OpGenercPtrMemSemantics 430 431} // namespace 432} // namespace spvtools 433