| // 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 |