mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-26 01:31:06 +00:00
1a0334edee
Note that we are more strict than Google style for one aspect: pointer/reference indicators are adjacent to their types, not their variables. find . -name "*.h" -exec clang-format -i {} \; find . -name "*.cpp" -exec clang-format -i {} \;
408 lines
15 KiB
C++
408 lines
15 KiB
C++
// Copyright (c) 2015 The Khronos Group Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and/or associated documentation files (the
|
|
// "Materials"), to deal in the Materials without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Materials, and to
|
|
// permit persons to whom the Materials are furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Materials.
|
|
//
|
|
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
|
|
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
|
|
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
|
|
// https://www.khronos.org/registry/
|
|
//
|
|
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
|
|
|
#include "UnitSPIRV.h"
|
|
|
|
#include <gmock/gmock.h>
|
|
#include "TestFixture.h"
|
|
|
|
namespace {
|
|
|
|
using spvtest::Concatenate;
|
|
using spvtest::MakeInstruction;
|
|
using spvtest::MakeVector;
|
|
using spvtest::TextToBinaryTest;
|
|
using testing::Eq;
|
|
|
|
struct InstructionCase {
|
|
uint32_t opcode;
|
|
std::string name;
|
|
std::string operands;
|
|
std::vector<uint32_t> expected_operands;
|
|
};
|
|
|
|
using ExtInstOpenCLStdRoundTripTest =
|
|
spvtest::TextToBinaryTestBase<::testing::TestWithParam<InstructionCase>>;
|
|
|
|
TEST_P(ExtInstOpenCLStdRoundTripTest, ParameterizedExtInst) {
|
|
// This example should not validate.
|
|
const std::string input =
|
|
"%1 = OpExtInstImport \"OpenCL.std\"\n"
|
|
"%3 = OpExtInst %2 %1 " +
|
|
GetParam().name + " " + GetParam().operands + "\n";
|
|
// First make sure it assembles correctly.
|
|
EXPECT_THAT(
|
|
CompiledInstructions(input),
|
|
Eq(Concatenate(
|
|
{MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("OpenCL.std")),
|
|
MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
|
|
GetParam().expected_operands)})))
|
|
<< input;
|
|
// Now check the round trip through the disassembler.
|
|
EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
|
|
}
|
|
|
|
#define CASE1(Enum, Name) \
|
|
{ \
|
|
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4", { 4 } \
|
|
}
|
|
#define CASE2(Enum, Name) \
|
|
{ \
|
|
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5", { 4, 5 } \
|
|
}
|
|
#define CASE3(Enum, Name) \
|
|
{ \
|
|
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6", { 4, 5, 6 } \
|
|
}
|
|
#define CASE4(Enum, Name) \
|
|
{ \
|
|
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6 %7", { \
|
|
4, 5, 6, 7 \
|
|
} \
|
|
}
|
|
#define CASE2Lit(Enum, Name, LiteralNumber) \
|
|
{ \
|
|
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 " #LiteralNumber, { \
|
|
4, 5, LiteralNumber \
|
|
} \
|
|
}
|
|
#define CASE3Round(Enum, Name, Mode) \
|
|
{ \
|
|
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6 " #Mode, { \
|
|
4, 5, 6, uint32_t(SpvFPRoundingMode##Mode) \
|
|
} \
|
|
}
|
|
|
|
// clang-format off
|
|
// OpenCL.std: 2.1 Math extended instructions
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLMath, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
// We are only testing the correctness of encoding and decoding here.
|
|
// Semantic correctness should be the responsibility of validator.
|
|
CASE1(Acos, acos), // enum value 0
|
|
CASE1(Acosh, acosh),
|
|
CASE1(Acospi, acospi),
|
|
CASE1(Asin, asin),
|
|
CASE1(Asinh, asinh),
|
|
CASE1(Asinh, asinh),
|
|
CASE1(Asinpi, asinpi),
|
|
CASE1(Atan, atan),
|
|
CASE2(Atan2, atan2),
|
|
CASE1(Atanh, atanh),
|
|
CASE1(Atanpi, atanpi),
|
|
CASE2(Atan2pi, atan2pi),
|
|
CASE1(Cbrt, cbrt),
|
|
CASE1(Ceil, ceil),
|
|
CASE1(Ceil, ceil),
|
|
CASE2(Copysign, copysign),
|
|
CASE1(Cos, cos),
|
|
CASE1(Cosh, cosh),
|
|
CASE1(Cospi, cospi),
|
|
CASE1(Erfc, erfc),
|
|
CASE1(Erf, erf),
|
|
CASE1(Exp, exp),
|
|
CASE1(Exp2, exp2),
|
|
CASE1(Exp10, exp10),
|
|
CASE1(Expm1, expm1),
|
|
CASE1(Fabs, fabs),
|
|
CASE2(Fdim, fdim),
|
|
CASE1(Floor, floor),
|
|
CASE3(Fma, fma),
|
|
CASE2(Fmax, fmax),
|
|
CASE2(Fmin, fmin),
|
|
CASE2(Fmod, fmod),
|
|
CASE2(Fract, fract),
|
|
CASE2(Frexp, frexp),
|
|
CASE2(Hypot, hypot),
|
|
CASE1(Ilogb, ilogb),
|
|
CASE2(Ldexp, ldexp),
|
|
CASE1(Lgamma, lgamma),
|
|
CASE2(Lgamma_r, lgamma_r),
|
|
CASE1(Log, log),
|
|
CASE1(Log2, log2),
|
|
CASE1(Log10, log10),
|
|
CASE1(Log1p, log1p),
|
|
CASE3(Mad, mad),
|
|
CASE2(Maxmag, maxmag),
|
|
CASE2(Minmag, minmag),
|
|
CASE2(Modf, modf),
|
|
CASE1(Nan, nan),
|
|
CASE2(Nextafter, nextafter),
|
|
CASE3(Pow, pow),
|
|
CASE1(Pown, pown),
|
|
CASE2(Powr, powr),
|
|
CASE2(Remainder, remainder),
|
|
CASE3(Remquo, remquo),
|
|
CASE1(Rint, rint),
|
|
CASE2(Rootn, rootn),
|
|
CASE1(Round, round),
|
|
CASE1(Rsqrt, rsqrt),
|
|
CASE1(Sin, sin),
|
|
CASE2(Sincos, sincos),
|
|
CASE1(Sinh, sinh),
|
|
CASE1(Sinpi, sinpi),
|
|
CASE1(Sqrt, sqrt),
|
|
CASE1(Tan, tan),
|
|
CASE1(Tanh, tanh),
|
|
CASE1(Tanpi, tanpi),
|
|
CASE1(Tgamma, tgamma),
|
|
CASE1(Trunc, trunc),
|
|
CASE1(Half_cos, half_cos),
|
|
CASE2(Half_divide, half_divide),
|
|
CASE1(Half_exp, half_exp),
|
|
CASE1(Half_exp2, half_exp2),
|
|
CASE1(Half_exp10, half_exp10),
|
|
CASE1(Half_log, half_log),
|
|
CASE1(Half_log2, half_log2),
|
|
CASE1(Half_log10, half_log10),
|
|
CASE2(Half_powr, half_powr),
|
|
CASE1(Half_recip, half_recip),
|
|
CASE1(Half_rsqrt, half_rsqrt),
|
|
CASE1(Half_sin, half_sin),
|
|
CASE1(Half_sqrt, half_sqrt),
|
|
CASE1(Half_tan, half_tan),
|
|
CASE1(Native_cos, native_cos),
|
|
CASE2(Native_divide, native_divide),
|
|
CASE1(Native_exp, native_exp),
|
|
CASE1(Native_exp2, native_exp2),
|
|
CASE1(Native_exp10, native_exp10),
|
|
CASE1(Native_log, native_log),
|
|
CASE1(Native_log10, native_log10),
|
|
CASE2(Native_powr, native_powr),
|
|
CASE1(Native_recip, native_recip),
|
|
CASE1(Native_rsqrt, native_rsqrt),
|
|
CASE1(Native_sin, native_sin),
|
|
CASE1(Native_sqrt, native_sqrt),
|
|
CASE1(Native_tan, native_tan), // enum value 94
|
|
})));
|
|
|
|
// OpenCL.std: 2.1 Integer instructions
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLInteger, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
CASE1(SAbs, s_abs), // enum value 141
|
|
CASE2(SAbs_diff, s_abs_diff),
|
|
CASE2(SAdd_sat, s_add_sat),
|
|
CASE2(UAdd_sat, u_add_sat),
|
|
CASE2(SHadd, s_hadd),
|
|
CASE2(UHadd, u_hadd),
|
|
CASE2(SRhadd, s_rhadd),
|
|
CASE2(SRhadd, s_rhadd),
|
|
CASE3(SClamp, s_clamp),
|
|
CASE3(UClamp, u_clamp),
|
|
CASE1(Clz, clz),
|
|
CASE1(Ctz, ctz),
|
|
CASE3(SMad_hi, s_mad_hi),
|
|
CASE3(UMad_sat, u_mad_sat),
|
|
CASE3(SMad_sat, s_mad_sat),
|
|
CASE2(SMax, s_max),
|
|
CASE2(UMax, u_max),
|
|
CASE2(SMin, s_min),
|
|
CASE2(UMin, u_min),
|
|
CASE2(SMul_hi, s_mul_hi),
|
|
CASE2(Rotate, rotate),
|
|
CASE2(SSub_sat, s_sub_sat),
|
|
CASE2(USub_sat, u_sub_sat),
|
|
CASE2(U_Upsample, u_upsample),
|
|
CASE2(S_Upsample, s_upsample),
|
|
CASE1(Popcount, popcount),
|
|
CASE3(SMad24, s_mad24),
|
|
CASE3(UMad24, u_mad24),
|
|
CASE3(SMul24, s_mul24),
|
|
CASE3(UMul24, u_mul24), // enum value 170
|
|
CASE1(UAbs, u_abs), // enum value 201
|
|
CASE2(UAbs_diff, u_abs_diff),
|
|
CASE2(UMul_hi, u_mul_hi),
|
|
CASE3(UMad_hi, u_mad_hi), // enum value 204
|
|
})));
|
|
|
|
// OpenCL.std: 2.3 Common instrucitons
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLCommon, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
CASE3(FClamp, fclamp), // enum value 95
|
|
CASE1(Degrees, degrees),
|
|
CASE2(FMax_common, fmax_common),
|
|
CASE2(FMin_common, fmin_common),
|
|
CASE3(Mix, mix),
|
|
CASE1(Radians, radians),
|
|
CASE2(Step, step),
|
|
CASE3(Smoothstep, smoothstep),
|
|
CASE1(Sign, sign), // enum value 103
|
|
})));
|
|
|
|
// OpenCL.std: 2.4 Geometric instructions
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLGeometric, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
CASE2(Cross, cross), // enum value 104
|
|
CASE2(Distance, distance),
|
|
CASE1(Length, length),
|
|
CASE1(Normalize, normalize),
|
|
CASE2(Fast_distance, fast_distance),
|
|
CASE1(Fast_length, fast_length),
|
|
CASE1(Fast_normalize, fast_normalize), // enum value 110
|
|
})));
|
|
|
|
// OpenCL.std: 2.5 Relational instructions
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLRelational, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
CASE3(Bitselect, bitselect), // enum value 186
|
|
CASE3(Select, select), // enum value 187
|
|
})));
|
|
|
|
// OpenCL.std: 2.6 Vector data load and store instructions
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLVectorLoadStore, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
// The last argument to Vloadn must be one of 2, 3, 4, 8, 16.
|
|
CASE2Lit(Vloadn, vloadn, 2),
|
|
CASE2Lit(Vloadn, vloadn, 3),
|
|
CASE2Lit(Vloadn, vloadn, 4),
|
|
CASE2Lit(Vloadn, vloadn, 8),
|
|
CASE2Lit(Vloadn, vloadn, 16),
|
|
CASE3(Vstoren, vstoren),
|
|
CASE2(Vload_half, vload_half),
|
|
CASE2Lit(Vload_halfn, vload_halfn, 2),
|
|
CASE2Lit(Vload_halfn, vload_halfn, 3),
|
|
CASE2Lit(Vload_halfn, vload_halfn, 4),
|
|
CASE2Lit(Vload_halfn, vload_halfn, 8),
|
|
CASE2Lit(Vload_halfn, vload_halfn, 16),
|
|
CASE3(Vstore_half, vstore_half),
|
|
// Try all the rounding modes.
|
|
CASE3Round(Vstore_half_r, vstore_half_r, RTE),
|
|
CASE3Round(Vstore_half_r, vstore_half_r, RTZ),
|
|
CASE3Round(Vstore_half_r, vstore_half_r, RTP),
|
|
CASE3Round(Vstore_half_r, vstore_half_r, RTN),
|
|
CASE3(Vstore_halfn, vstore_halfn),
|
|
CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTE),
|
|
CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTZ),
|
|
CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTP),
|
|
CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTN),
|
|
CASE2Lit(Vloada_halfn, vloada_halfn, 2),
|
|
CASE2Lit(Vloada_halfn, vloada_halfn, 3),
|
|
CASE2Lit(Vloada_halfn, vloada_halfn, 4),
|
|
CASE2Lit(Vloada_halfn, vloada_halfn, 8),
|
|
CASE2Lit(Vloada_halfn, vloada_halfn, 16),
|
|
CASE3(Vstorea_halfn, vstorea_halfn),
|
|
CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTE),
|
|
CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTZ),
|
|
CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTP),
|
|
CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTN),
|
|
})));
|
|
|
|
// OpenCL.std: 2.7 Miscellaneous vector instructions
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLMiscellaneousVector, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
CASE2(Shuffle, shuffle),
|
|
CASE3(Shuffle2, shuffle2),
|
|
})));
|
|
|
|
// OpenCL.std: 2.8 Miscellaneous instructions
|
|
|
|
#define PREFIX uint32_t(OpenCLLIB::Entrypoints::Printf), "printf"
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLMiscPrintf, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
// Printf is interesting becuase it takes a variable number of arguments.
|
|
// Start with zero optional arguments.
|
|
{PREFIX, "%4", {4}},
|
|
{PREFIX, "%4 %5", {4, 5}},
|
|
{PREFIX, "%4 %5 %6", {4, 5, 6}},
|
|
{PREFIX, "%4 %5 %6 %7", {4, 5, 6, 7}},
|
|
{PREFIX, "%4 %5 %6 %7 %8", {4, 5, 6, 7, 8}},
|
|
{PREFIX, "%4 %5 %6 %7 %8 %9", {4, 5, 6, 7, 8, 9}},
|
|
{PREFIX, "%4 %5 %6 %7 %8 %9 %10", {4, 5, 6, 7, 8, 9, 10}},
|
|
{PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11", {4, 5, 6, 7, 8, 9, 10, 11}},
|
|
{PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12",
|
|
{4, 5, 6, 7, 8, 9, 10, 11, 12}},
|
|
{PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12 %13",
|
|
{4, 5, 6, 7, 8, 9, 10, 11, 12, 13}},
|
|
{PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12 %13 %14",
|
|
{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}},
|
|
})));
|
|
#undef PREFIX
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLMiscPrefetch, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
CASE2(Prefetch, prefetch),
|
|
})));
|
|
|
|
// OpenCL.std: 2.9.1 Image encoding
|
|
// No new instructions defined in this section.
|
|
|
|
// OpenCL.std: 2.9.2 Sampler encoding
|
|
// No new instructions defined in this section.
|
|
|
|
// OpenCL.std: 2.9.3 Image read
|
|
// No new instructions defined in this section.
|
|
// Use core instruction OpImageSampleExplicitLod instead.
|
|
|
|
// OpenCL.std: 2.9.4 Image write
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpenCLImageWrite, ExtInstOpenCLStdRoundTripTest,
|
|
::testing::ValuesIn(std::vector<InstructionCase>({
|
|
CASE4(Write_imagef_mipmap_lod, write_imagef_mipmap_lod),
|
|
CASE4(Write_imagei_mipmap_lod, write_imagei_mipmap_lod),
|
|
CASE4(Write_imageui_mipmap_lod, write_imageui_mipmap_lod),
|
|
})));
|
|
|
|
// clang-format on
|
|
|
|
#undef CASE1
|
|
#undef CASE2
|
|
#undef CASE3
|
|
#undef CASE4
|
|
#undef CASE2Lit
|
|
#undef CASE3Round
|
|
|
|
// TODO(dneto): Fix this functionality.
|
|
TEST_F(TextToBinaryTest, DISABLED_ExtInstFromTwoDifferentImports) {
|
|
const std::string input =
|
|
R"(%1 = OpExtInstImport "OpenCL.std"
|
|
%2 = OpExtInstImport "GLSL.std.450"
|
|
%4 = OpExtInst %3 %1 native_sqrt %5
|
|
%7 = OpExtInst %6 %2 MatrixInverse %8
|
|
)";
|
|
EXPECT_THAT(
|
|
CompiledInstructions(input),
|
|
Eq(Concatenate({
|
|
MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("OpenCL.std")),
|
|
MakeInstruction(SpvOpExtInstImport, {2}, MakeVector("GLSL.std.450")),
|
|
MakeInstruction(
|
|
SpvOpExtInst,
|
|
{3, 4, 1, uint32_t(OpenCLLIB::Entrypoints::Native_sqrt), 5}),
|
|
MakeInstruction(SpvOpExtInst,
|
|
{6, 7, 2, uint32_t(GLSLstd450MatrixInverse), 8}),
|
|
})));
|
|
}
|
|
|
|
} // anonymous namespace
|