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