// 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:\n2[%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_CASE_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 %intptrt = OpTypePointer UniformConstant %uint )"; 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