| // 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. | |
| // Validation tests for SSA | |
| #include <sstream> | |
| #include <string> | |
| #include <utility> | |
| #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::MatchesRegex; | |
| using ValidateSSA = spvtest::ValidateBase<std::pair<std::string, bool>>; | |
| TEST_F(ValidateSSA, Default) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint GLCompute %3 "" | |
| OpExecutionMode %3 LocalSize 1 1 1 | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpFunction %1 None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, IdUndefinedBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %missing "missing" | |
| %voidt = OpTypeVoid | |
| %vfunct = OpTypeFunction %voidt | |
| %func = OpFunction %vfunct None %missing | |
| %flabel = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, IdRedefinedBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %2 "redefined" | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %2 = OpFunction %1 None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, DominateUsageBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %1 "not_dominant" | |
| %2 = OpTypeFunction %1 ; uses %1 before it's definition | |
| %1 = OpTypeVoid | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("not_dominant")); | |
| } | |
| TEST_F(ValidateSSA, DominateUsageWithinBlockBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %bad "bad" | |
| %voidt = OpTypeVoid | |
| %funct = OpTypeFunction %voidt | |
| %uintt = OpTypeInt 32 0 | |
| %one = OpConstant %uintt 1 | |
| %func = OpFunction %voidt None %funct | |
| %entry = OpLabel | |
| %sum = OpIAdd %uintt %one %bad | |
| %bad = OpCopyObject %uintt %sum | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| MatchesRegex("ID '.\\[%bad\\]' has not been defined\n" | |
| " %8 = OpIAdd %uint %uint_1 %bad\n")); | |
| } | |
| TEST_F(ValidateSSA, DominateUsageSameInstructionBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %sum "sum" | |
| %voidt = OpTypeVoid | |
| %funct = OpTypeFunction %voidt | |
| %uintt = OpTypeInt 32 0 | |
| %one = OpConstant %uintt 1 | |
| %func = OpFunction %voidt None %funct | |
| %entry = OpLabel | |
| %sum = OpIAdd %uintt %one %sum | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| MatchesRegex("ID '.\\[%sum\\]' has not been defined\n" | |
| " %sum = OpIAdd %uint %uint_1 %sum\n")); | |
| } | |
| TEST_F(ValidateSSA, ForwardNameGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %3 "main" | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpFunction %1 None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardNameMissingTargetBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %5 "main" ; Target never defined | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("main")); | |
| } | |
| TEST_F(ValidateSSA, ForwardMemberNameGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpMemberName %struct 0 "value" | |
| OpMemberName %struct 1 "size" | |
| %intt = OpTypeInt 32 1 | |
| %uintt = OpTypeInt 32 0 | |
| %struct = OpTypeStruct %intt %uintt | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardMemberNameMissingTargetBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpMemberName %struct 0 "value" | |
| OpMemberName %bad 1 "size" ; Target is not defined | |
| %intt = OpTypeInt 32 1 | |
| %uintt = OpTypeInt 32 0 | |
| %struct = OpTypeStruct %intt %uintt | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr("The following forward referenced IDs have not been " | |
| "defined:\n'2[%2]'")); | |
| } | |
| TEST_F(ValidateSSA, ForwardDecorateGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpDecorate %var Restrict | |
| %intt = OpTypeInt 32 1 | |
| %ptrt = OpTypePointer UniformConstant %intt | |
| %var = OpVariable %ptrt UniformConstant | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardDecorateInvalidIDBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %missing "missing" | |
| OpDecorate %missing Restrict ;Missing ID | |
| %voidt = OpTypeVoid | |
| %intt = OpTypeInt 32 1 | |
| %ptrt = OpTypePointer UniformConstant %intt | |
| %var = OpVariable %ptrt UniformConstant | |
| %2 = OpTypeFunction %voidt | |
| %3 = OpFunction %voidt None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, ForwardMemberDecorateGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpMemberDecorate %struct 1 RowMajor | |
| %intt = OpTypeInt 32 1 | |
| %f32 = OpTypeFloat 32 | |
| %vec3 = OpTypeVector %f32 3 | |
| %mat33 = OpTypeMatrix %vec3 3 | |
| %struct = OpTypeStruct %intt %mat33 | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardMemberDecorateInvalidIdBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %missing "missing" | |
| OpMemberDecorate %missing 1 RowMajor ; Target not defined | |
| %intt = OpTypeInt 32 1 | |
| %f32 = OpTypeFloat 32 | |
| %vec3 = OpTypeVector %f32 3 | |
| %mat33 = OpTypeMatrix %vec3 3 | |
| %struct = OpTypeStruct %intt %mat33 | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, ForwardGroupDecorateGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpDecorate %dgrp RowMajor | |
| %dgrp = OpDecorationGroup | |
| OpGroupDecorate %dgrp %mat33 %mat44 | |
| %f32 = OpTypeFloat 32 | |
| %vec3 = OpTypeVector %f32 3 | |
| %vec4 = OpTypeVector %f32 4 | |
| %mat33 = OpTypeMatrix %vec3 3 | |
| %mat44 = OpTypeMatrix %vec4 4 | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardGroupDecorateMissingGroupBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %missing "missing" | |
| OpDecorate %dgrp RowMajor | |
| %dgrp = OpDecorationGroup | |
| OpGroupDecorate %missing %mat33 %mat44 ; Target not defined | |
| %intt = OpTypeInt 32 1 | |
| %vec3 = OpTypeVector %intt 3 | |
| %vec4 = OpTypeVector %intt 4 | |
| %mat33 = OpTypeMatrix %vec3 3 | |
| %mat44 = OpTypeMatrix %vec4 4 | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, ForwardGroupDecorateMissingTargetBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %missing "missing" | |
| OpDecorate %dgrp RowMajor | |
| %dgrp = OpDecorationGroup | |
| OpGroupDecorate %dgrp %missing %mat44 ; Target not defined | |
| %f32 = OpTypeFloat 32 | |
| %vec3 = OpTypeVector %f32 3 | |
| %vec4 = OpTypeVector %f32 4 | |
| %mat33 = OpTypeMatrix %vec3 3 | |
| %mat44 = OpTypeMatrix %vec4 4 | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, ForwardGroupDecorateDecorationGroupDominateBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %dgrp "group" | |
| OpDecorate %dgrp RowMajor | |
| OpGroupDecorate %dgrp %mat33 %mat44 ; Decoration group does not dominate usage | |
| %dgrp = OpDecorationGroup | |
| %intt = OpTypeInt 32 1 | |
| %vec3 = OpTypeVector %intt 3 | |
| %vec4 = OpTypeVector %intt 4 | |
| %mat33 = OpTypeMatrix %vec3 3 | |
| %mat44 = OpTypeMatrix %vec4 4 | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("group")); | |
| } | |
| TEST_F(ValidateSSA, ForwardDecorateInvalidIdBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %missing "missing" | |
| OpDecorate %missing Restrict ; Missing target | |
| %voidt = OpTypeVoid | |
| %intt = OpTypeInt 32 1 | |
| %ptrt = OpTypePointer UniformConstant %intt | |
| %var = OpVariable %ptrt UniformConstant | |
| %2 = OpTypeFunction %voidt | |
| %3 = OpFunction %voidt None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, FunctionCallGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 1 | |
| %3 = OpTypeInt 32 0 | |
| %4 = OpTypeFunction %1 | |
| %8 = OpTypeFunction %1 %2 %3 | |
| %four = OpConstant %2 4 | |
| %five = OpConstant %3 5 | |
| %9 = OpFunction %1 None %8 | |
| %10 = OpFunctionParameter %2 | |
| %11 = OpFunctionParameter %3 | |
| %12 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %5 = OpFunction %1 None %4 | |
| %6 = OpLabel | |
| %7 = OpFunctionCall %1 %9 %four %five | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardFunctionCallGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 1 | |
| %3 = OpTypeInt 32 0 | |
| %four = OpConstant %2 4 | |
| %five = OpConstant %3 5 | |
| %8 = OpTypeFunction %1 %2 %3 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpFunction %1 None %4 | |
| %6 = OpLabel | |
| %7 = OpFunctionCall %1 %9 %four %five | |
| OpReturn | |
| OpFunctionEnd | |
| %9 = OpFunction %1 None %8 | |
| %10 = OpFunctionParameter %2 | |
| %11 = OpFunctionParameter %3 | |
| %12 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardBranchConditionalGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| %voidt = OpTypeVoid | |
| %boolt = OpTypeBool | |
| %vfunct = OpTypeFunction %voidt | |
| %true = OpConstantTrue %boolt | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| OpSelectionMerge %endl None | |
| OpBranchConditional %true %truel %falsel | |
| %truel = OpLabel | |
| OpNop | |
| OpBranch %endl | |
| %falsel = OpLabel | |
| OpNop | |
| OpBranch %endl | |
| %endl = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardBranchConditionalWithWeightsGood) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| %voidt = OpTypeVoid | |
| %boolt = OpTypeBool | |
| %vfunct = OpTypeFunction %voidt | |
| %true = OpConstantTrue %boolt | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| OpSelectionMerge %endl None | |
| OpBranchConditional %true %truel %falsel 1 9 | |
| %truel = OpLabel | |
| OpNop | |
| OpBranch %endl | |
| %falsel = OpLabel | |
| OpNop | |
| OpBranch %endl | |
| %endl = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardBranchConditionalNonDominantConditionBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %tcpy "conditional" | |
| %voidt = OpTypeVoid | |
| %boolt = OpTypeBool | |
| %vfunct = OpTypeFunction %voidt | |
| %true = OpConstantTrue %boolt | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| OpSelectionMerge %endl None | |
| OpBranchConditional %tcpy %truel %falsel ; | |
| %truel = OpLabel | |
| OpNop | |
| OpBranch %endl | |
| %falsel = OpLabel | |
| OpNop | |
| OpBranch %endl | |
| %endl = OpLabel | |
| %tcpy = OpCopyObject %boolt %true | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("conditional")); | |
| } | |
| TEST_F(ValidateSSA, ForwardBranchConditionalMissingTargetBad) { | |
| char str[] = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| OpName %missing "missing" | |
| %voidt = OpTypeVoid | |
| %boolt = OpTypeBool | |
| %vfunct = OpTypeFunction %voidt | |
| %true = OpConstantTrue %boolt | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| OpSelectionMerge %endl None | |
| OpBranchConditional %true %missing %falsel | |
| %truel = OpLabel | |
| OpNop | |
| OpBranch %endl | |
| %falsel = OpLabel | |
| OpNop | |
| OpBranch %endl | |
| %endl = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| // Since Int8 requires the Kernel capability, the signedness of int types may | |
| // not be "1". | |
| const std::string kHeader = R"( | |
| OpCapability Int8 | |
| OpCapability DeviceEnqueue | |
| OpCapability Linkage | |
| OpMemoryModel Logical OpenCL | |
| )"; | |
| const std::string kBasicTypes = R"( | |
| %voidt = OpTypeVoid | |
| %boolt = OpTypeBool | |
| %int8t = OpTypeInt 8 0 | |
| %uintt = OpTypeInt 32 0 | |
| %vfunct = OpTypeFunction %voidt | |
| %intptrt = OpTypePointer UniformConstant %uintt | |
| %zero = OpConstant %uintt 0 | |
| %one = OpConstant %uintt 1 | |
| %ten = OpConstant %uintt 10 | |
| %false = OpConstantFalse %boolt | |
| )"; | |
| const std::string kKernelTypesAndConstants = R"( | |
| %queuet = OpTypeQueue | |
| %three = OpConstant %uintt 3 | |
| %arr3t = OpTypeArray %uintt %three | |
| %ndt = OpTypeStruct %uintt %arr3t %arr3t %arr3t | |
| %eventt = OpTypeEvent | |
| %offset = OpConstant %uintt 0 | |
| %local = OpConstant %uintt 1 | |
| %gl = OpConstant %uintt 1 | |
| %nevent = OpConstant %uintt 0 | |
| %event = OpConstantNull %eventt | |
| %firstp = OpConstant %int8t 0 | |
| %psize = OpConstant %uintt 0 | |
| %palign = OpConstant %uintt 32 | |
| %lsize = OpConstant %uintt 1 | |
| %flags = OpConstant %uintt 0 ; NoWait | |
| %kfunct = OpTypeFunction %voidt %intptrt | |
| )"; | |
| const std::string kKernelSetup = R"( | |
| %dqueue = OpGetDefaultQueue %queuet | |
| %ndval = OpBuildNDRange %ndt %gl %local %offset | |
| %revent = OpUndef %eventt | |
| )"; | |
| const std::string kKernelDefinition = R"( | |
| %kfunc = OpFunction %voidt None %kfunct | |
| %iparam = OpFunctionParameter %intptrt | |
| %kfuncl = OpLabel | |
| OpNop | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| TEST_F(ValidateSSA, EnqueueKernelGood) { | |
| std::string str = kHeader + kBasicTypes + kKernelTypesAndConstants + | |
| kKernelDefinition + R"( | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| )" + kKernelSetup + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event %revent %kfunc %firstp %psize | |
| %palign %lsize | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelGood) { | |
| std::string str = kHeader + kBasicTypes + kKernelTypesAndConstants + R"( | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| )" + | |
| kKernelSetup + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event %revent %kfunc %firstp %psize | |
| %palign %lsize | |
| OpReturn | |
| OpFunctionEnd | |
| )" + kKernelDefinition; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, EnqueueMissingFunctionBad) { | |
| std::string str = kHeader + "OpName %kfunc \"kfunc\"" + kBasicTypes + | |
| kKernelTypesAndConstants + R"( | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| )" + kKernelSetup + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event %revent %kfunc %firstp %psize | |
| %palign %lsize | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("kfunc")); | |
| } | |
| std::string forwardKernelNonDominantParameterBaseCode( | |
| std::string name = std::string()) { | |
| std::string op_name; | |
| if (name.empty()) { | |
| op_name = ""; | |
| } else { | |
| op_name = "\nOpName %" + name + " \"" + name + "\"\n"; | |
| } | |
| std::string out = kHeader + op_name + kBasicTypes + kKernelTypesAndConstants + | |
| kKernelDefinition + | |
| R"( | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| )" + kKernelSetup; | |
| return out; | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelMissingParameter1Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("missing") + R"( | |
| %err = OpEnqueueKernel %missing %dqueue %flags %ndval | |
| %nevent %event %revent %kfunc %firstp | |
| %psize %palign %lsize | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter2Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("dqueue2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue2 %flags %ndval | |
| %nevent %event %revent %kfunc | |
| %firstp %psize %palign %lsize | |
| %dqueue2 = OpGetDefaultQueue %queuet | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("dqueue2")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter3Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("ndval2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval2 | |
| %nevent %event %revent %kfunc %firstp | |
| %psize %palign %lsize | |
| %ndval2 = OpBuildNDRange %ndt %gl %local %offset | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("ndval2")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter4Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("nevent2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent2 | |
| %event %revent %kfunc %firstp %psize | |
| %palign %lsize | |
| %nevent2 = OpCopyObject %uintt %nevent | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("nevent2")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter5Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("event2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event2 %revent %kfunc %firstp %psize | |
| %palign %lsize | |
| %event2 = OpCopyObject %eventt %event | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("event2")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter6Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("revent2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event %revent2 %kfunc %firstp %psize | |
| %palign %lsize | |
| %revent2 = OpCopyObject %eventt %revent | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("revent2")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter8Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("firstp2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event %revent %kfunc %firstp2 %psize | |
| %palign %lsize | |
| %firstp2 = OpCopyObject %int8t %firstp | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("firstp2")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter9Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("psize2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event %revent %kfunc %firstp %psize2 | |
| %palign %lsize | |
| %psize2 = OpCopyObject %uintt %psize | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("psize2")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter10Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("palign2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event %revent %kfunc %firstp %psize | |
| %palign2 %lsize | |
| %palign2 = OpCopyObject %uintt %palign | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("palign2")); | |
| } | |
| TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter11Bad) { | |
| std::string str = forwardKernelNonDominantParameterBaseCode("lsize2") + R"( | |
| %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent | |
| %event %revent %kfunc %firstp %psize | |
| %palign %lsize2 | |
| %lsize2 = OpCopyObject %uintt %lsize | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("lsize2")); | |
| } | |
| static const bool kWithNDrange = true; | |
| static const bool kNoNDrange = false; | |
| std::pair<std::string, bool> cases[] = { | |
| {"OpGetKernelNDrangeSubGroupCount", kWithNDrange}, | |
| {"OpGetKernelNDrangeMaxSubGroupSize", kWithNDrange}, | |
| {"OpGetKernelWorkGroupSize", kNoNDrange}, | |
| {"OpGetKernelPreferredWorkGroupSizeMultiple", kNoNDrange}}; | |
| INSTANTIATE_TEST_SUITE_P(KernelArgs, ValidateSSA, ::testing::ValuesIn(cases)); | |
| static const std::string return_instructions = R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| TEST_P(ValidateSSA, GetKernelGood) { | |
| std::string instruction = GetParam().first; | |
| bool with_ndrange = GetParam().second; | |
| std::string ndrange_param = with_ndrange ? " %ndval " : " "; | |
| std::stringstream ss; | |
| // clang-format off | |
| ss << forwardKernelNonDominantParameterBaseCode() + " %numsg = " | |
| << instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize %palign" | |
| << return_instructions; | |
| // clang-format on | |
| CompileSuccessfully(ss.str()); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateSSA, ForwardGetKernelGood) { | |
| std::string instruction = GetParam().first; | |
| bool with_ndrange = GetParam().second; | |
| std::string ndrange_param = with_ndrange ? " %ndval " : " "; | |
| // clang-format off | |
| std::string str = kHeader + kBasicTypes + kKernelTypesAndConstants + | |
| R"( | |
| %main = OpFunction %voidt None %vfunct | |
| %mainl = OpLabel | |
| )" | |
| + kKernelSetup + " %numsg = " | |
| + instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize %palign" | |
| + return_instructions + kKernelDefinition; | |
| // clang-format on | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateSSA, ForwardGetKernelMissingDefinitionBad) { | |
| std::string instruction = GetParam().first; | |
| bool with_ndrange = GetParam().second; | |
| std::string ndrange_param = with_ndrange ? " %ndval " : " "; | |
| std::stringstream ss; | |
| // clang-format off | |
| ss << forwardKernelNonDominantParameterBaseCode("missing") + " %numsg = " | |
| << instruction + " %uintt" + ndrange_param + "%missing %firstp %psize %palign" | |
| << return_instructions; | |
| // clang-format on | |
| CompileSuccessfully(ss.str()); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_P(ValidateSSA, ForwardGetKernelNDrangeSubGroupCountMissingParameter1Bad) { | |
| std::string instruction = GetParam().first; | |
| bool with_ndrange = GetParam().second; | |
| std::string ndrange_param = with_ndrange ? " %ndval " : " "; | |
| std::stringstream ss; | |
| // clang-format off | |
| ss << forwardKernelNonDominantParameterBaseCode("missing") + " %numsg = " | |
| << instruction + " %missing" + ndrange_param + "%kfunc %firstp %psize %palign" | |
| << return_instructions; | |
| // clang-format on | |
| CompileSuccessfully(ss.str()); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_P(ValidateSSA, | |
| ForwardGetKernelNDrangeSubGroupCountNonDominantParameter2Bad) { | |
| std::string instruction = GetParam().first; | |
| bool with_ndrange = GetParam().second; | |
| std::string ndrange_param = with_ndrange ? " %ndval2 " : " "; | |
| std::stringstream ss; | |
| // clang-format off | |
| ss << forwardKernelNonDominantParameterBaseCode("ndval2") + " %numsg = " | |
| << instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize %palign" | |
| << "\n %ndval2 = OpBuildNDRange %ndt %gl %local %offset" | |
| << return_instructions; | |
| // clang-format on | |
| if (GetParam().second) { | |
| CompileSuccessfully(ss.str()); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("ndval2")); | |
| } | |
| } | |
| TEST_P(ValidateSSA, | |
| ForwardGetKernelNDrangeSubGroupCountNonDominantParameter4Bad) { | |
| std::string instruction = GetParam().first; | |
| bool with_ndrange = GetParam().second; | |
| std::string ndrange_param = with_ndrange ? " %ndval " : " "; | |
| std::stringstream ss; | |
| // clang-format off | |
| ss << forwardKernelNonDominantParameterBaseCode("firstp2") + " %numsg = " | |
| << instruction + " %uintt" + ndrange_param + "%kfunc %firstp2 %psize %palign" | |
| << "\n %firstp2 = OpCopyObject %int8t %firstp" | |
| << return_instructions; | |
| // clang-format on | |
| CompileSuccessfully(ss.str()); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("firstp2")); | |
| } | |
| TEST_P(ValidateSSA, | |
| ForwardGetKernelNDrangeSubGroupCountNonDominantParameter5Bad) { | |
| std::string instruction = GetParam().first; | |
| bool with_ndrange = GetParam().second; | |
| std::string ndrange_param = with_ndrange ? " %ndval " : " "; | |
| std::stringstream ss; | |
| // clang-format off | |
| ss << forwardKernelNonDominantParameterBaseCode("psize2") + " %numsg = " | |
| << instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize2 %palign" | |
| << "\n %psize2 = OpCopyObject %uintt %psize" | |
| << return_instructions; | |
| // clang-format on | |
| CompileSuccessfully(ss.str()); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("psize2")); | |
| } | |
| TEST_P(ValidateSSA, | |
| ForwardGetKernelNDrangeSubGroupCountNonDominantParameter6Bad) { | |
| std::string instruction = GetParam().first; | |
| bool with_ndrange = GetParam().second; | |
| std::string ndrange_param = with_ndrange ? " %ndval " : " "; | |
| std::stringstream ss; | |
| // clang-format off | |
| ss << forwardKernelNonDominantParameterBaseCode("palign2") + " %numsg = " | |
| << instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize %palign2" | |
| << "\n %palign2 = OpCopyObject %uintt %palign" | |
| << return_instructions; | |
| // clang-format on | |
| if (GetParam().second) { | |
| CompileSuccessfully(ss.str()); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("palign2")); | |
| } | |
| } | |
| TEST_F(ValidateSSA, PhiGood) { | |
| std::string str = kHeader + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %preheader = OpLabel | |
| %init = OpCopyObject %uintt %zero | |
| OpBranch %loop | |
| %loop = OpLabel | |
| %i = OpPhi %uintt %init %preheader %loopi %loop | |
| %loopi = OpIAdd %uintt %i %one | |
| OpNop | |
| %cond = OpSLessThan %boolt %i %ten | |
| OpLoopMerge %endl %loop None | |
| OpBranchConditional %cond %loop %endl | |
| %endl = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, PhiMissingTypeBad) { | |
| std::string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %preheader = OpLabel | |
| %init = OpCopyObject %uintt %zero | |
| OpBranch %loop | |
| %loop = OpLabel | |
| %i = OpPhi %missing %init %preheader %loopi %loop | |
| %loopi = OpIAdd %uintt %i %one | |
| OpNop | |
| %cond = OpSLessThan %boolt %i %ten | |
| OpLoopMerge %endl %loop None | |
| OpBranchConditional %cond %loop %endl | |
| %endl = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, PhiMissingIdBad) { | |
| std::string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %preheader = OpLabel | |
| %init = OpCopyObject %uintt %zero | |
| OpBranch %loop | |
| %loop = OpLabel | |
| %i = OpPhi %uintt %missing %preheader %loopi %loop | |
| %loopi = OpIAdd %uintt %i %one | |
| OpNop | |
| %cond = OpSLessThan %boolt %i %ten | |
| OpLoopMerge %endl %loop None | |
| OpBranchConditional %cond %loop %endl | |
| %endl = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, PhiMissingLabelBad) { | |
| std::string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %preheader = OpLabel | |
| %init = OpCopyObject %uintt %zero | |
| OpBranch %loop | |
| %loop = OpLabel | |
| %i = OpPhi %uintt %init %missing %loopi %loop | |
| %loopi = OpIAdd %uintt %i %one | |
| OpNop | |
| %cond = OpSLessThan %boolt %i %ten | |
| OpLoopMerge %endl %loop None | |
| OpBranchConditional %cond %loop %endl | |
| %endl = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); | |
| } | |
| TEST_F(ValidateSSA, IdDominatesItsUseGood) { | |
| std::string str = kHeader + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| %cond = OpSLessThan %boolt %one %ten | |
| %eleven = OpIAdd %uintt %one %ten | |
| OpSelectionMerge %merge None | |
| OpBranchConditional %cond %t %f | |
| %t = OpLabel | |
| %twelve = OpIAdd %uintt %eleven %one | |
| OpBranch %merge | |
| %f = OpLabel | |
| %twentytwo = OpIAdd %uintt %eleven %ten | |
| OpBranch %merge | |
| %merge = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, IdDoesNotDominateItsUseBad) { | |
| std::string str = kHeader + | |
| "OpName %eleven \"eleven\"\n" | |
| "OpName %true_block \"true_block\"\n" | |
| "OpName %false_block \"false_block\"" + | |
| kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| %cond = OpSLessThan %boolt %one %ten | |
| OpSelectionMerge %merge None | |
| OpBranchConditional %cond %true_block %false_block | |
| %true_block = OpLabel | |
| %eleven = OpIAdd %uintt %one %ten | |
| %twelve = OpIAdd %uintt %eleven %one | |
| OpBranch %merge | |
| %false_block = OpLabel | |
| %twentytwo = OpIAdd %uintt %eleven %ten | |
| OpBranch %merge | |
| %merge = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| MatchesRegex("ID '.\\[%eleven\\]' defined in block '.\\[%true_block\\]' " | |
| "does not dominate its use in block '.\\[%false_block\\]'\n" | |
| " %false_block = OpLabel\n")); | |
| } | |
| TEST_F(ValidateSSA, PhiUseDoesntDominateDefinitionGood) { | |
| std::string str = kHeader + kBasicTypes + | |
| R"( | |
| %funcintptrt = OpTypePointer Function %uintt | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| %var_one = OpVariable %funcintptrt Function %one | |
| %one_val = OpLoad %uintt %var_one | |
| OpBranch %loop | |
| %loop = OpLabel | |
| %i = OpPhi %uintt %one_val %entry %inew %cont | |
| %cond = OpSLessThan %boolt %one %ten | |
| OpLoopMerge %merge %cont None | |
| OpBranchConditional %cond %body %merge | |
| %body = OpLabel | |
| OpBranch %cont | |
| %cont = OpLabel | |
| %inew = OpIAdd %uintt %i %one | |
| OpBranch %loop | |
| %merge = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, | |
| PhiUseDoesntDominateUseOfPhiOperandUsedBeforeDefinitionBad) { | |
| std::string str = kHeader + "OpName %inew \"inew\"" + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| %var_one = OpVariable %intptrt Function %one | |
| %one_val = OpLoad %uintt %var_one | |
| OpBranch %loop | |
| %loop = OpLabel | |
| %i = OpPhi %uintt %one_val %entry %inew %cont | |
| %bad = OpIAdd %uintt %inew %one | |
| %cond = OpSLessThan %boolt %one %ten | |
| OpLoopMerge %merge %cont None | |
| OpBranchConditional %cond %body %merge | |
| %body = OpLabel | |
| OpBranch %cont | |
| %cont = OpLabel | |
| %inew = OpIAdd %uintt %i %one | |
| OpBranch %loop | |
| %merge = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| MatchesRegex("ID '.\\[%inew\\]' has not been defined\n" | |
| " %19 = OpIAdd %uint %inew %uint_1\n")); | |
| } | |
| TEST_F(ValidateSSA, PhiUseMayComeFromNonDominatingBlockGood) { | |
| std::string str = kHeader + "OpName %if_true \"if_true\"\n" + | |
| "OpName %exit \"exit\"\n" + "OpName %copy \"copy\"\n" + | |
| kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| OpBranchConditional %false %if_true %exit | |
| %if_true = OpLabel | |
| %copy = OpCopyObject %boolt %false | |
| OpBranch %exit | |
| ; The use of %copy here is ok, even though it was defined | |
| ; in a block that does not dominate %exit. That's the point | |
| ; of an OpPhi. | |
| %exit = OpLabel | |
| %value = OpPhi %boolt %false %entry %copy %if_true | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); | |
| } | |
| TEST_F(ValidateSSA, PhiUsesItsOwnDefinitionGood) { | |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/415 | |
| // | |
| // Non-phi instructions can't use their own definitions, as | |
| // already checked in test DominateUsageSameInstructionBad. | |
| std::string str = kHeader + "OpName %loop \"loop\"\n" + | |
| "OpName %value \"value\"\n" + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| OpBranch %loop | |
| %loop = OpLabel | |
| %value = OpPhi %boolt %false %entry %value %loop | |
| OpBranch %loop | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); | |
| } | |
| TEST_F(ValidateSSA, PhiVariableDefNotDominatedByParentBlockBad) { | |
| std::string str = kHeader + "OpName %if_true \"if_true\"\n" + | |
| "OpName %if_false \"if_false\"\n" + | |
| "OpName %exit \"exit\"\n" + "OpName %value \"phi\"\n" + | |
| "OpName %true_copy \"true_copy\"\n" + | |
| "OpName %false_copy \"false_copy\"\n" + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| OpBranchConditional %false %if_true %if_false | |
| %if_true = OpLabel | |
| %true_copy = OpCopyObject %boolt %false | |
| OpBranch %exit | |
| %if_false = OpLabel | |
| %false_copy = OpCopyObject %boolt %false | |
| OpBranch %exit | |
| ; The (variable,Id) pairs are swapped. | |
| %exit = OpLabel | |
| %value = OpPhi %boolt %true_copy %if_false %false_copy %if_true | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| MatchesRegex( | |
| "In OpPhi instruction '.\\[%phi\\]', ID '.\\[%true_copy\\]' " | |
| "definition does not dominate its parent '.\\[%if_false\\]'\n" | |
| " %phi = OpPhi %bool %true_copy %if_false %false_copy " | |
| "%if_true\n")); | |
| } | |
| TEST_F(ValidateSSA, PhiVariableDefDominatesButNotDefinedInParentBlock) { | |
| std::string str = kHeader + "OpName %if_true \"if_true\"\n" + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| OpBranchConditional %false %if_true %if_false | |
| %if_true = OpLabel | |
| %true_copy = OpCopyObject %boolt %false | |
| OpBranch %if_tnext | |
| %if_tnext = OpLabel | |
| OpBranch %exit | |
| %if_false = OpLabel | |
| %false_copy = OpCopyObject %boolt %false | |
| OpBranch %if_fnext | |
| %if_fnext = OpLabel | |
| OpBranch %exit | |
| %exit = OpLabel | |
| %value = OpPhi %boolt %true_copy %if_tnext %false_copy %if_fnext | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, | |
| DominanceCheckIgnoresUsesInUnreachableBlocksDefInBlockGood) { | |
| std::string str = kHeader + kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| %def = OpCopyObject %boolt %false | |
| OpReturn | |
| %unreach = OpLabel | |
| %use = OpCopyObject %boolt %def | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); | |
| } | |
| TEST_F(ValidateSSA, PhiVariableUnreachableDefNotInParentBlock) { | |
| std::string str = kHeader + "OpName %unreachable \"unreachable\"\n" + | |
| kBasicTypes + | |
| R"( | |
| %func = OpFunction %voidt None %vfunct | |
| %entry = OpLabel | |
| OpBranch %if_false | |
| %unreachable = OpLabel | |
| %copy = OpCopyObject %boolt %false | |
| OpBranch %if_tnext | |
| %if_tnext = OpLabel | |
| OpBranch %exit | |
| %if_false = OpLabel | |
| %false_copy = OpCopyObject %boolt %false | |
| OpBranch %if_fnext | |
| %if_fnext = OpLabel | |
| OpBranch %exit | |
| %exit = OpLabel | |
| %value = OpPhi %boolt %copy %if_tnext %false_copy %if_fnext | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, | |
| DominanceCheckIgnoresUsesInUnreachableBlocksDefIsParamGood) { | |
| std::string str = kHeader + kBasicTypes + | |
| R"( | |
| %void_fn_int = OpTypeFunction %voidt %uintt | |
| %func = OpFunction %voidt None %void_fn_int | |
| %int_param = OpFunctionParameter %uintt | |
| %entry = OpLabel | |
| OpReturn | |
| %unreach = OpLabel | |
| %use = OpCopyObject %uintt %int_param | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); | |
| } | |
| TEST_F(ValidateSSA, UseFunctionParameterFromOtherFunctionBad) { | |
| std::string str = kHeader + | |
| "OpName %first \"first\"\n" | |
| "OpName %func \"func\"\n" + | |
| "OpName %func2 \"func2\"\n" + kBasicTypes + | |
| R"( | |
| %viifunct = OpTypeFunction %voidt %uintt %uintt | |
| %func = OpFunction %voidt None %viifunct | |
| %first = OpFunctionParameter %uintt | |
| %second = OpFunctionParameter %uintt | |
| OpFunctionEnd | |
| %func2 = OpFunction %voidt None %viifunct | |
| %first2 = OpFunctionParameter %uintt | |
| %second2 = OpFunctionParameter %uintt | |
| %entry2 = OpLabel | |
| %baduse = OpIAdd %uintt %first %first2 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| MatchesRegex( | |
| "ID '.\\[%first\\]' used in function '.\\[%func2\\]' is used " | |
| "outside of it's defining function '.\\[%func\\]'\n" | |
| " %func = OpFunction %void None %14\n")); | |
| } | |
| TEST_F(ValidateSSA, TypeForwardPointerForwardReference) { | |
| // See https://github.com/KhronosGroup/SPIRV-Tools/issues/429 | |
| // | |
| // ForwardPointers can references instructions that have not been defined | |
| std::string str = R"( | |
| OpCapability Kernel | |
| OpCapability Addresses | |
| OpCapability Linkage | |
| OpMemoryModel Logical OpenCL | |
| OpName %intptrt "intptrt" | |
| OpTypeForwardPointer %intptrt UniformConstant | |
| %uint = OpTypeInt 32 0 | |
| %struct = OpTypeStruct %uint | |
| %intptrt = OpTypePointer UniformConstant %struct | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_F(ValidateSSA, TypeStructForwardReference) { | |
| std::string str = R"( | |
| OpCapability Kernel | |
| OpCapability Addresses | |
| OpCapability Linkage | |
| OpMemoryModel Logical OpenCL | |
| OpName %structptr "structptr" | |
| OpTypeForwardPointer %structptr UniformConstant | |
| %uint = OpTypeInt 32 0 | |
| %structt1 = OpTypeStruct %structptr %uint | |
| %structt2 = OpTypeStruct %uint %structptr | |
| %structt3 = OpTypeStruct %uint %uint %structptr | |
| %structt4 = OpTypeStruct %uint %uint %uint %structptr | |
| %structptr = OpTypePointer UniformConstant %structt1 | |
| )"; | |
| CompileSuccessfully(str); | |
| ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // TODO(umar): OpGroupMemberDecorate | |
| } // namespace | |
| } // namespace val | |
| } // namespace spvtools |