blob: e10abd7ab249b3d0bedb6105c068d4591805e350 [file] [log] [blame]
// 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_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
%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