SPIRV-Tools/test/val/val_ssa_test.cpp
Dan Sinclair f28ed82fd9 Make sure all instructions are in the ordered list.
Currently, some instructions will be missing from the list of
ordered_instructions. This will cause issues due to the debug change
which passed the last instruction into subsequent passes.

This CL moves the addition to the ordered list out of the
RegisterInstruction method into AddOrderedInstruction. This method is
called first in ProcessInstruction and the CapabilitiesPass and IdPass
are updated to take an Instruction parameter.
2018-07-31 09:55:57 -04:00

1448 lines
46 KiB
C++

// 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 "unit_spirv.h"
#include "val_fixtures.h"
namespace spvtools {
namespace val {
namespace {
using ::testing::HasSubstr;
using ::testing::MatchesRegex;
using std::pair;
using std::string;
using std::stringstream;
using ValidateSSA = spvtest::ValidateBase<pair<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("size"));
}
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 string kHeader = R"(
OpCapability Int8
OpCapability DeviceEnqueue
OpCapability Linkage
OpMemoryModel Logical OpenCL
)";
const 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 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 string kKernelSetup = R"(
%dqueue = OpGetDefaultQueue %queuet
%ndval = OpBuildNDRange %ndt %gl %local %offset
%revent = OpUndef %eventt
)";
const string kKernelDefinition = R"(
%kfunc = OpFunction %voidt None %kfunct
%iparam = OpFunctionParameter %intptrt
%kfuncl = OpLabel
OpNop
OpReturn
OpFunctionEnd
)";
TEST_F(ValidateSSA, EnqueueKernelGood) {
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) {
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) {
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"));
}
string forwardKernelNonDominantParameterBaseCode(string name = string()) {
string op_name;
if (name.empty()) {
op_name = "";
} else {
op_name = "\nOpName %" + name + " \"" + name + "\"\n";
}
string out = kHeader + op_name + kBasicTypes + kKernelTypesAndConstants +
kKernelDefinition +
R"(
%main = OpFunction %voidt None %vfunct
%mainl = OpLabel
)" +
kKernelSetup;
return out;
}
TEST_F(ValidateSSA, ForwardEnqueueKernelMissingParameter1Bad) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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;
pair<string, bool> cases[] = {
{"OpGetKernelNDrangeSubGroupCount", kWithNDrange},
{"OpGetKernelNDrangeMaxSubGroupSize", kWithNDrange},
{"OpGetKernelWorkGroupSize", kNoNDrange},
{"OpGetKernelPreferredWorkGroupSizeMultiple", kNoNDrange}};
INSTANTIATE_TEST_CASE_P(KernelArgs, ValidateSSA, ::testing::ValuesIn(cases), );
static const string return_instructions = R"(
OpReturn
OpFunctionEnd
)";
TEST_P(ValidateSSA, GetKernelGood) {
string instruction = GetParam().first;
bool with_ndrange = GetParam().second;
string ndrange_param = with_ndrange ? " %ndval " : " ";
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) {
string instruction = GetParam().first;
bool with_ndrange = GetParam().second;
string ndrange_param = with_ndrange ? " %ndval " : " ";
// clang-format off
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) {
string instruction = GetParam().first;
bool with_ndrange = GetParam().second;
string ndrange_param = with_ndrange ? " %ndval " : " ";
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) {
string instruction = GetParam().first;
bool with_ndrange = GetParam().second;
string ndrange_param = with_ndrange ? " %ndval " : " ";
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) {
string instruction = GetParam().first;
bool with_ndrange = GetParam().second;
string ndrange_param = with_ndrange ? " %ndval2 " : " ";
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) {
string instruction = GetParam().first;
bool with_ndrange = GetParam().second;
string ndrange_param = with_ndrange ? " %ndval " : " ";
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) {
string instruction = GetParam().first;
bool with_ndrange = GetParam().second;
string ndrange_param = with_ndrange ? " %ndval " : " ";
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) {
string instruction = GetParam().first;
bool with_ndrange = GetParam().second;
string ndrange_param = with_ndrange ? " %ndval " : " ";
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) {
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) {
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) {
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) {
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) {
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) {
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"
" OpFunctionEnd\n"));
}
TEST_F(ValidateSSA, PhiUseDoesntDominateDefinitionGood) {
string str = kHeader + 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
%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) {
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) {
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.
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) {
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"
" OpFunctionEnd\n"));
}
TEST_F(ValidateSSA, PhiVariableDefDominatesButNotDefinedInParentBlock) {
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) {
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) {
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) {
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) {
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"
" OpFunctionEnd\n"));
}
TEST_F(ValidateSSA, TypeForwardPointerForwardReference) {
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/429
//
// ForwardPointers can references instructions that have not been defined
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) {
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