SPIRV-Tools/test/text_to_binary.constant_test.cpp

842 lines
35 KiB
C++
Raw Normal View History

2016-01-07 18:44:22 +00:00
// 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.
// Assembler tests for instructions in the "Group Instrucions" section of the
// SPIR-V spec.
#include <cstdint>
#include <limits>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using spvtest::Concatenate;
using spvtest::EnumCase;
using spvtest::MakeInstruction;
using ::testing::Eq;
// Test Sampler Addressing Mode enum values
using SamplerAddressingModeTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<EnumCase<spv::SamplerAddressingMode>>>;
TEST_P(SamplerAddressingModeTest, AnySamplerAddressingMode) {
const std::string input =
"%result = OpConstantSampler %type " + GetParam().name() + " 0 Nearest";
EXPECT_THAT(CompiledInstructions(input),
Eq(MakeInstruction(spv::Op::OpConstantSampler,
{1, 2, uint32_t(GetParam().value()), 0, 0})));
}
// clang-format off
#define CASE(NAME) { spv::SamplerAddressingMode::NAME, #NAME }
INSTANTIATE_TEST_SUITE_P(
TextToBinarySamplerAddressingMode, SamplerAddressingModeTest,
::testing::ValuesIn(std::vector<EnumCase<spv::SamplerAddressingMode>>{
CASE(None),
CASE(ClampToEdge),
CASE(Clamp),
CASE(Repeat),
CASE(RepeatMirrored),
}));
#undef CASE
// clang-format on
TEST_F(SamplerAddressingModeTest, WrongMode) {
EXPECT_THAT(CompileFailure("%r = OpConstantSampler %t xxyyzz 0 Nearest"),
Eq("Invalid sampler addressing mode 'xxyyzz'."));
}
// Test Sampler Filter Mode enum values
using SamplerFilterModeTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<EnumCase<spv::SamplerFilterMode>>>;
TEST_P(SamplerFilterModeTest, AnySamplerFilterMode) {
const std::string input =
"%result = OpConstantSampler %type Clamp 0 " + GetParam().name();
EXPECT_THAT(CompiledInstructions(input),
Eq(MakeInstruction(spv::Op::OpConstantSampler,
{1, 2, 2, 0, uint32_t(GetParam().value())})));
}
// clang-format off
#define CASE(NAME) { spv::SamplerFilterMode::NAME, #NAME}
INSTANTIATE_TEST_SUITE_P(
TextToBinarySamplerFilterMode, SamplerFilterModeTest,
::testing::ValuesIn(std::vector<EnumCase<spv::SamplerFilterMode>>{
CASE(Nearest),
CASE(Linear),
}));
#undef CASE
// clang-format on
2015-10-13 16:38:20 +00:00
TEST_F(SamplerFilterModeTest, WrongMode) {
EXPECT_THAT(CompileFailure("%r = OpConstantSampler %t Clamp 0 xxyyzz"),
Eq("Invalid sampler filter mode 'xxyyzz'."));
}
struct ConstantTestCase {
std::string constant_type;
std::string constant_value;
std::vector<uint32_t> expected_instructions;
};
using OpConstantValidTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<ConstantTestCase>>;
TEST_P(OpConstantValidTest, ValidTypes) {
const std::string input = "%1 = " + GetParam().constant_type +
"\n"
"%2 = OpConstant %1 " +
GetParam().constant_value + "\n";
std::vector<uint32_t> instructions;
EXPECT_THAT(CompiledInstructions(input), Eq(GetParam().expected_instructions))
<< " type: " << GetParam().constant_type
<< " literal: " << GetParam().constant_value;
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
TextToBinaryOpConstantValid, OpConstantValidTest,
::testing::ValuesIn(std::vector<ConstantTestCase>{
// Check 16 bits
{"OpTypeInt 16 0", "0x1234",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x1234})})},
{"OpTypeInt 16 0", "0x8000",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x8000})})},
{"OpTypeInt 16 0", "0",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
{"OpTypeInt 16 0", "65535",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 65535})})},
{"OpTypeInt 16 0", "0xffff",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 65535})})},
{"OpTypeInt 16 1", "0x8000", // Test sign extension.
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffff8000})})},
{"OpTypeInt 16 1", "-32",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-32)})})},
{"OpTypeInt 16 1", "0",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
{"OpTypeInt 16 1", "-0",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
{"OpTypeInt 16 1", "-0x0",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
{"OpTypeInt 16 1", "-32768",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-32768)})})},
// Check 32 bits
{"OpTypeInt 32 0", "42",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 42})})},
{"OpTypeInt 32 1", "-32",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-32)})})},
{"OpTypeInt 32 1", "0",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
{"OpTypeInt 32 1", "-0",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
{"OpTypeInt 32 1", "-0x0",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0})})},
{"OpTypeInt 32 1", "-0x001",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-1)})})},
{"OpTypeInt 32 1", "2147483647",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x7fffffffu})})},
{"OpTypeInt 32 1", "-2147483648",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x80000000u})})},
{"OpTypeFloat 32", "1.0",
Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x3f800000})})},
{"OpTypeFloat 32", "10.0",
Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x41200000})})},
{"OpTypeFloat 32", "-0x1p+128", // -infinity
Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0xFF800000})})},
{"OpTypeFloat 32", "0x1p+128", // +infinity
Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x7F800000})})},
{"OpTypeFloat 32", "-0x1.8p+128", // A -NaN
Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0xFFC00000})})},
{"OpTypeFloat 32", "-0x1.0002p+128", // A +NaN
Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0xFF800100})})},
// Check 48 bits
{"OpTypeInt 48 0", "0x1234",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x1234, 0})})},
{"OpTypeInt 48 0", "0x800000000001",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 1, 0x00008000})})},
{"OpTypeInt 48 1", "0x800000000000", // Test sign extension.
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0, 0xffff8000})})},
{"OpTypeInt 48 1", "-32",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-32), uint32_t(-1)})})},
// Check 64 bits
{"OpTypeInt 64 0", "0x1234",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x1234, 0})})},
{"OpTypeInt 64 0", "18446744073709551615",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
{"OpTypeInt 64 0", "0xffffffffffffffff",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
{"OpTypeInt 64 1", "0x1234",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x1234, 0})})},
{"OpTypeInt 64 1", "-42",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, uint32_t(-42), uint32_t(-1)})})},
{"OpTypeInt 64 1", "-0x01",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})},
{"OpTypeInt 64 1", "9223372036854775807",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0xffffffffu, 0x7fffffffu})})},
{"OpTypeInt 64 1", "0x7fffffff",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
MakeInstruction(spv::Op::OpConstant, {1, 2, 0x7fffffffu, 0})})},
}));
// clang-format on
// A test case for checking OpConstant with invalid literals with a leading
// minus.
struct InvalidLeadingMinusCase {
std::string type;
std::string literal;
};
using OpConstantInvalidLeadingMinusTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<InvalidLeadingMinusCase>>;
TEST_P(OpConstantInvalidLeadingMinusTest, InvalidCase) {
const std::string input = "%1 = " + GetParam().type +
"\n"
"%2 = OpConstant %1 " +
GetParam().literal;
EXPECT_THAT(CompileFailure(input),
Eq("Cannot put a negative number in an unsigned literal"));
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
TextToBinaryOpConstantInvalidLeadingMinus, OpConstantInvalidLeadingMinusTest,
::testing::ValuesIn(std::vector<InvalidLeadingMinusCase>{
{"OpTypeInt 16 0", "-0"},
{"OpTypeInt 16 0", "-0x0"},
{"OpTypeInt 16 0", "-1"},
{"OpTypeInt 32 0", "-0"},
{"OpTypeInt 32 0", "-0x0"},
{"OpTypeInt 32 0", "-1"},
{"OpTypeInt 64 0", "-0"},
{"OpTypeInt 64 0", "-0x0"},
{"OpTypeInt 64 0", "-1"},
}));
// clang-format on
// A test case for invalid floating point literals.
struct InvalidFloatConstantCase {
uint32_t width;
std::string literal;
};
using OpConstantInvalidFloatConstant = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<InvalidFloatConstantCase>>;
TEST_P(OpConstantInvalidFloatConstant, Samples) {
// Check both kinds of instructions that take literal floats.
for (const auto& instruction : {"OpConstant", "OpSpecConstant"}) {
std::stringstream input;
input << "%1 = OpTypeFloat " << GetParam().width << "\n"
<< "%2 = " << instruction << " %1 " << GetParam().literal;
std::stringstream expected_error;
expected_error << "Invalid " << GetParam().width
<< "-bit float literal: " << GetParam().literal;
EXPECT_THAT(CompileFailure(input.str()), Eq(expected_error.str()));
}
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
TextToBinaryInvalidFloatConstant, OpConstantInvalidFloatConstant,
::testing::ValuesIn(std::vector<InvalidFloatConstantCase>{
{16, "abc"},
{16, "--1"},
{16, "-+1"},
{16, "+-1"},
{16, "++1"},
{16, "1e30"}, // Overflow is an error for 16-bit floats.
{16, "-1e30"},
{16, "1e40"},
{16, "-1e40"},
{16, "1e400"},
{16, "-1e400"},
{32, "abc"},
{32, "--1"},
{32, "-+1"},
{32, "+-1"},
{32, "++1"},
{32, "1e40"}, // Overflow is an error for 32-bit floats.
{32, "-1e40"},
{32, "1e400"},
{32, "-1e400"},
{64, "abc"},
{64, "--1"},
{64, "-+1"},
{64, "+-1"},
{64, "++1"},
{32, "1e400"}, // Overflow is an error for 64-bit floats.
{32, "-1e400"},
}));
// clang-format on
using OpConstantInvalidTypeTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<std::string>>;
TEST_P(OpConstantInvalidTypeTest, InvalidTypes) {
const std::string input = "%1 = " + GetParam() +
"\n"
"%2 = OpConstant %1 0\n";
EXPECT_THAT(
CompileFailure(input),
Eq("Type for Constant must be a scalar floating point or integer type"));
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
TextToBinaryOpConstantInvalidValidType, OpConstantInvalidTypeTest,
::testing::ValuesIn(std::vector<std::string>{
{"OpTypeVoid",
"OpTypeBool",
"OpTypeVector %a 32",
"OpTypeMatrix %a 32",
"OpTypeImage %a 1D 0 0 0 0 Unknown",
"OpTypeSampler",
"OpTypeSampledImage %a",
"OpTypeArray %a %b",
"OpTypeRuntimeArray %a",
"OpTypeStruct %a",
"OpTypeOpaque \"Foo\"",
"OpTypePointer UniformConstant %a",
"OpTypeFunction %a %b",
"OpTypeEvent",
"OpTypeDeviceEvent",
"OpTypeReserveId",
"OpTypeQueue",
"OpTypePipe ReadOnly",
// Skip OpTypeForwardPointer doesn't even produce a result ID.
// The assembler errors out if we try to check it in this scenario.
// Try at least one thing that isn't a type at all
"OpNot %a %b"
},
}));
// clang-format on
using OpSpecConstantValidTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<ConstantTestCase>>;
TEST_P(OpSpecConstantValidTest, ValidTypes) {
const std::string input = "%1 = " + GetParam().constant_type +
"\n"
"%2 = OpSpecConstant %1 " +
GetParam().constant_value + "\n";
std::vector<uint32_t> instructions;
EXPECT_THAT(CompiledInstructions(input),
Eq(GetParam().expected_instructions));
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
TextToBinaryOpSpecConstantValid, OpSpecConstantValidTest,
::testing::ValuesIn(std::vector<ConstantTestCase>{
// Check 16 bits
{"OpTypeInt 16 0", "0x1234",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x1234})})},
{"OpTypeInt 16 0", "0x8000",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 0}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x8000})})},
{"OpTypeInt 16 1", "0x8000", // Test sign extension.
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0xffff8000})})},
{"OpTypeInt 16 1", "-32",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 16, 1}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, uint32_t(-32)})})},
// Check 32 bits
{"OpTypeInt 32 0", "42",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 0}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 42})})},
{"OpTypeInt 32 1", "-32",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 32, 1}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, uint32_t(-32)})})},
{"OpTypeFloat 32", "1.0",
Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x3f800000})})},
{"OpTypeFloat 32", "10.0",
Concatenate({MakeInstruction(spv::Op::OpTypeFloat, {1, 32}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x41200000})})},
// Check 48 bits
{"OpTypeInt 48 0", "0x1234",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 0}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x1234, 0})})},
{"OpTypeInt 48 0", "0x800000000001",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 0}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 1, 0x00008000})})},
{"OpTypeInt 48 1", "0x800000000000", // Test sign extension.
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 1}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0, 0xffff8000})})},
{"OpTypeInt 48 1", "-32",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 48, 1}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, uint32_t(-32), uint32_t(-1)})})},
// Check 64 bits
{"OpTypeInt 64 0", "0x1234",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 0}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x1234, 0})})},
{"OpTypeInt 64 1", "0x1234",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, 0x1234, 0})})},
{"OpTypeInt 64 1", "-42",
Concatenate({MakeInstruction(spv::Op::OpTypeInt, {1, 64, 1}),
MakeInstruction(spv::Op::OpSpecConstant, {1, 2, uint32_t(-42), uint32_t(-1)})})},
}));
// clang-format on
using OpSpecConstantInvalidTypeTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<std::string>>;
TEST_P(OpSpecConstantInvalidTypeTest, InvalidTypes) {
const std::string input = "%1 = " + GetParam() +
"\n"
"%2 = OpSpecConstant %1 0\n";
EXPECT_THAT(CompileFailure(input),
Eq("Type for SpecConstant must be a scalar floating point or "
"integer type"));
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
TextToBinaryOpSpecConstantInvalidValidType, OpSpecConstantInvalidTypeTest,
::testing::ValuesIn(std::vector<std::string>{
{"OpTypeVoid",
"OpTypeBool",
"OpTypeVector %a 32",
"OpTypeMatrix %a 32",
"OpTypeImage %a 1D 0 0 0 0 Unknown",
"OpTypeSampler",
"OpTypeSampledImage %a",
"OpTypeArray %a %b",
"OpTypeRuntimeArray %a",
"OpTypeStruct %a",
"OpTypeOpaque \"Foo\"",
"OpTypePointer UniformConstant %a",
"OpTypeFunction %a %b",
"OpTypeEvent",
"OpTypeDeviceEvent",
"OpTypeReserveId",
"OpTypeQueue",
"OpTypePipe ReadOnly",
// Skip testing OpTypeForwardPointer because it doesn't even produce a result ID.
// Try at least one thing that isn't a type at all
"OpNot %a %b"
},
}));
// clang-format on
const int64_t kMaxUnsigned48Bit = (int64_t(1) << 48) - 1;
const int64_t kMaxSigned48Bit = (int64_t(1) << 47) - 1;
const int64_t kMinSigned48Bit = -kMaxSigned48Bit - 1;
using ConstantRoundTripTest = RoundTripTest;
TEST_P(ConstantRoundTripTest, DisassemblyEqualsAssemblyInput) {
const std::string assembly = GetParam();
EXPECT_THAT(EncodeAndDecodeSuccessfully(assembly), Eq(assembly)) << assembly;
}
INSTANTIATE_TEST_SUITE_P(
OpConstantRoundTrip, ConstantRoundTripTest,
::testing::ValuesIn(std::vector<std::string>{
// 16 bit
"%1 = OpTypeInt 16 0\n%2 = OpConstant %1 0\n",
"%1 = OpTypeInt 16 0\n%2 = OpConstant %1 65535\n",
"%1 = OpTypeInt 16 1\n%2 = OpConstant %1 -32768\n",
"%1 = OpTypeInt 16 1\n%2 = OpConstant %1 32767\n",
"%1 = OpTypeInt 32 0\n%2 = OpConstant %1 0\n",
// 32 bit
std::string("%1 = OpTypeInt 32 0\n%2 = OpConstant %1 0\n"),
std::string("%1 = OpTypeInt 32 0\n%2 = OpConstant %1 ") +
std::to_string(std::numeric_limits<uint32_t>::max()) + "\n",
std::string("%1 = OpTypeInt 32 1\n%2 = OpConstant %1 ") +
std::to_string(std::numeric_limits<int32_t>::max()) + "\n",
std::string("%1 = OpTypeInt 32 1\n%2 = OpConstant %1 ") +
std::to_string(std::numeric_limits<int32_t>::min()) + "\n",
// 48 bit
std::string("%1 = OpTypeInt 48 0\n%2 = OpConstant %1 0\n"),
std::string("%1 = OpTypeInt 48 0\n%2 = OpConstant %1 ") +
std::to_string(kMaxUnsigned48Bit) + "\n",
std::string("%1 = OpTypeInt 48 1\n%2 = OpConstant %1 ") +
std::to_string(kMaxSigned48Bit) + "\n",
std::string("%1 = OpTypeInt 48 1\n%2 = OpConstant %1 ") +
std::to_string(kMinSigned48Bit) + "\n",
// 64 bit
std::string("%1 = OpTypeInt 64 0\n%2 = OpConstant %1 0\n"),
std::string("%1 = OpTypeInt 64 0\n%2 = OpConstant %1 ") +
std::to_string(std::numeric_limits<uint64_t>::max()) + "\n",
std::string("%1 = OpTypeInt 64 1\n%2 = OpConstant %1 ") +
std::to_string(std::numeric_limits<int64_t>::max()) + "\n",
std::string("%1 = OpTypeInt 64 1\n%2 = OpConstant %1 ") +
std::to_string(std::numeric_limits<int64_t>::min()) + "\n",
// 32-bit float
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0\n",
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 13.5\n",
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -12.5\n",
// 64-bit float
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0\n",
hex_float: Use max_digits10 for the float precision CPPreference.com has this description of digits10: “The value of std::numeric_limits<T>::digits10 is the number of base-10 digits that can be represented by the type T without change, that is, any number with this many significant decimal digits can be converted to a value of type T and back to decimal form, without change due to rounding or overflow.” This means that any number with this many digits can be represented accurately in the corresponding type. A change in any digit in a number after that may or may not cause it a different bitwise representation. Therefore this isn’t necessarily enough precision to accurately represent the value in text. Instead we need max_digits10 which has the following description: “The value of std::numeric_limits<T>::max_digits10 is the number of base-10 digits that are necessary to uniquely represent all distinct values of the type T, such as necessary for serialization/deserialization to text.” The patch includes a test case in hex_float_test which tries to do a round-robin conversion of a number that requires more than 6 decimal places to be accurately represented. This would fail without the patch. Sadly this also breaks a bunch of other tests. Some of the tests in hex_float_test use ldexp and then compare it with a value which is not the same as the one returned by ldexp but instead is the value rounded to 6 decimals. Others use values that are not evenly representable as a binary floating fraction but then happened to generate the same value when rounded to 6 decimals. Where the actual value didn’t seem to matter these have been changed with different values that can be represented as a binary fraction.
2018-03-30 23:35:45 +00:00
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 1.79767e+308\n",
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -1.79767e+308\n",
}));
INSTANTIATE_TEST_SUITE_P(
OpConstantHalfRoundTrip, ConstantRoundTripTest,
::testing::ValuesIn(std::vector<std::string>{
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x0p+0\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x0p+0\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1p+0\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.1p+0\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.01p-1\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.8p+1\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ffcp+1\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1p+0\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.1p+0\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.01p-1\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.8p+1\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.ffcp+1\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1p-16\n", // some denorms
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1p-24\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1p-24\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1p+16\n", // +inf
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1p+16\n", // -inf
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.01p+16\n", // -inf
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.01p+16\n", // nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.11p+16\n", // nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ffp+16\n", // nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ffcp+16\n", // nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.004p+16\n", // nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.01p+16\n", // -nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.11p+16\n", // -nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.ffp+16\n", // -nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.ffcp+16\n", // -nan
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.004p+16\n", // -nan
}));
// clang-format off
// (Clang-format really wants to break up these strings across lines.
INSTANTIATE_TEST_SUITE_P(
OpConstantRoundTripNonFinite, ConstantRoundTripTest,
::testing::ValuesIn(std::vector<std::string>{
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128\n", // -inf
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128\n", // inf
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.8p+128\n", // -nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128\n", // -nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0018p+128\n", // -nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.01ep+128\n", // -nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.fffffep+128\n", // -nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128\n", // +nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.0002p+128\n", // +nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.0018p+128\n", // +nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.01ep+128\n", // +nan
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.fffffep+128\n", // +nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024\n", // -inf
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024\n", // +inf
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.8p+1024\n", // -nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0fp+1024\n", // -nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0000000000001p+1024\n", // -nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.00003p+1024\n", // -nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.fffffffffffffp+1024\n", // -nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024\n", // +nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.0fp+1024\n", // +nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.0000000000001p+1024\n", // -nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.00003p+1024\n", // -nan
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.fffffffffffffp+1024\n", // -nan
}));
// clang-format on
INSTANTIATE_TEST_SUITE_P(
OpSpecConstantRoundTrip, ConstantRoundTripTest,
::testing::ValuesIn(std::vector<std::string>{
// 16 bit
"%1 = OpTypeInt 16 0\n%2 = OpSpecConstant %1 0\n",
"%1 = OpTypeInt 16 0\n%2 = OpSpecConstant %1 65535\n",
"%1 = OpTypeInt 16 1\n%2 = OpSpecConstant %1 -32768\n",
"%1 = OpTypeInt 16 1\n%2 = OpSpecConstant %1 32767\n",
"%1 = OpTypeInt 32 0\n%2 = OpSpecConstant %1 0\n",
// 32 bit
std::string("%1 = OpTypeInt 32 0\n%2 = OpSpecConstant %1 0\n"),
std::string("%1 = OpTypeInt 32 0\n%2 = OpSpecConstant %1 ") +
std::to_string(std::numeric_limits<uint32_t>::max()) + "\n",
std::string("%1 = OpTypeInt 32 1\n%2 = OpSpecConstant %1 ") +
std::to_string(std::numeric_limits<int32_t>::max()) + "\n",
std::string("%1 = OpTypeInt 32 1\n%2 = OpSpecConstant %1 ") +
std::to_string(std::numeric_limits<int32_t>::min()) + "\n",
// 48 bit
std::string("%1 = OpTypeInt 48 0\n%2 = OpSpecConstant %1 0\n"),
std::string("%1 = OpTypeInt 48 0\n%2 = OpSpecConstant %1 ") +
std::to_string(kMaxUnsigned48Bit) + "\n",
std::string("%1 = OpTypeInt 48 1\n%2 = OpSpecConstant %1 ") +
std::to_string(kMaxSigned48Bit) + "\n",
std::string("%1 = OpTypeInt 48 1\n%2 = OpSpecConstant %1 ") +
std::to_string(kMinSigned48Bit) + "\n",
// 64 bit
std::string("%1 = OpTypeInt 64 0\n%2 = OpSpecConstant %1 0\n"),
std::string("%1 = OpTypeInt 64 0\n%2 = OpSpecConstant %1 ") +
std::to_string(std::numeric_limits<uint64_t>::max()) + "\n",
std::string("%1 = OpTypeInt 64 1\n%2 = OpSpecConstant %1 ") +
std::to_string(std::numeric_limits<int64_t>::max()) + "\n",
std::string("%1 = OpTypeInt 64 1\n%2 = OpSpecConstant %1 ") +
std::to_string(std::numeric_limits<int64_t>::min()) + "\n",
// 32-bit float
"%1 = OpTypeFloat 32\n%2 = OpSpecConstant %1 0\n",
"%1 = OpTypeFloat 32\n%2 = OpSpecConstant %1 13.5\n",
"%1 = OpTypeFloat 32\n%2 = OpSpecConstant %1 -12.5\n",
// 64-bit float
"%1 = OpTypeFloat 64\n%2 = OpSpecConstant %1 0\n",
hex_float: Use max_digits10 for the float precision CPPreference.com has this description of digits10: “The value of std::numeric_limits<T>::digits10 is the number of base-10 digits that can be represented by the type T without change, that is, any number with this many significant decimal digits can be converted to a value of type T and back to decimal form, without change due to rounding or overflow.” This means that any number with this many digits can be represented accurately in the corresponding type. A change in any digit in a number after that may or may not cause it a different bitwise representation. Therefore this isn’t necessarily enough precision to accurately represent the value in text. Instead we need max_digits10 which has the following description: “The value of std::numeric_limits<T>::max_digits10 is the number of base-10 digits that are necessary to uniquely represent all distinct values of the type T, such as necessary for serialization/deserialization to text.” The patch includes a test case in hex_float_test which tries to do a round-robin conversion of a number that requires more than 6 decimal places to be accurately represented. This would fail without the patch. Sadly this also breaks a bunch of other tests. Some of the tests in hex_float_test use ldexp and then compare it with a value which is not the same as the one returned by ldexp but instead is the value rounded to 6 decimals. Others use values that are not evenly representable as a binary floating fraction but then happened to generate the same value when rounded to 6 decimals. Where the actual value didn’t seem to matter these have been changed with different values that can be represented as a binary fraction.
2018-03-30 23:35:45 +00:00
"%1 = OpTypeFloat 64\n%2 = OpSpecConstant %1 1.79767e+308\n",
"%1 = OpTypeFloat 64\n%2 = OpSpecConstant %1 -1.79767e+308\n",
}));
// Test OpSpecConstantOp
using OpSpecConstantOpTestWithIds =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<spv::Op>>>;
// The operands to the OpSpecConstantOp opcode are all Ids.
TEST_P(OpSpecConstantOpTestWithIds, Assembly) {
std::stringstream input;
input << "%2 = OpSpecConstantOp %1 " << GetParam().name();
for (auto id : GetParam().operands()) input << " %" << id;
input << "\n";
EXPECT_THAT(CompiledInstructions(input.str()),
Eq(MakeInstruction(spv::Op::OpSpecConstantOp,
{1, 2, uint32_t(GetParam().value())},
GetParam().operands())));
// Check the disassembler as well.
EXPECT_THAT(EncodeAndDecodeSuccessfully(input.str()), input.str());
}
// clang-format off
#define CASE1(NAME) { spv::Op::Op##NAME, #NAME, {3} }
#define CASE2(NAME) { spv::Op::Op##NAME, #NAME, {3, 4} }
#define CASE3(NAME) { spv::Op::Op##NAME, #NAME, {3, 4, 5} }
#define CASE4(NAME) { spv::Op::Op##NAME, #NAME, {3, 4, 5, 6} }
#define CASE5(NAME) { spv::Op::Op##NAME, #NAME, {3, 4, 5, 6, 7} }
#define CASE6(NAME) { spv::Op::Op##NAME, #NAME, {3, 4, 5, 6, 7, 8} }
INSTANTIATE_TEST_SUITE_P(
TextToBinaryOpSpecConstantOp, OpSpecConstantOpTestWithIds,
::testing::ValuesIn(std::vector<EnumCase<spv::Op>>{
// Conversion
CASE1(SConvert),
CASE1(FConvert),
CASE1(ConvertFToS),
CASE1(ConvertSToF),
CASE1(ConvertFToU),
CASE1(ConvertUToF),
CASE1(UConvert),
CASE1(ConvertPtrToU),
CASE1(ConvertUToPtr),
CASE1(GenericCastToPtr),
CASE1(PtrCastToGeneric),
CASE1(Bitcast),
CASE1(QuantizeToF16),
// Arithmetic
CASE1(SNegate),
CASE1(Not),
CASE2(IAdd),
CASE2(ISub),
CASE2(IMul),
CASE2(UDiv),
CASE2(SDiv),
CASE2(UMod),
CASE2(SRem),
CASE2(SMod),
CASE2(ShiftRightLogical),
CASE2(ShiftRightArithmetic),
CASE2(ShiftLeftLogical),
CASE2(BitwiseOr),
CASE2(BitwiseAnd),
CASE2(BitwiseXor),
CASE1(FNegate),
CASE2(FAdd),
CASE2(FSub),
CASE2(FMul),
CASE2(FDiv),
CASE2(FRem),
CASE2(FMod),
// Composite operations use literal numbers. So they're in another test.
// Logical
CASE2(LogicalOr),
CASE2(LogicalAnd),
CASE1(LogicalNot),
CASE2(LogicalEqual),
CASE2(LogicalNotEqual),
CASE3(Select),
// Comparison
CASE2(IEqual),
CASE2(INotEqual), // Allowed in 1.0 Rev 7
CASE2(ULessThan),
CASE2(SLessThan),
CASE2(UGreaterThan),
CASE2(SGreaterThan),
CASE2(ULessThanEqual),
CASE2(SLessThanEqual),
CASE2(UGreaterThanEqual),
CASE2(SGreaterThanEqual),
// Memory
// For AccessChain, there is a base Id, then a sequence of index Ids.
// Having no index Ids is a corner case.
CASE1(AccessChain),
CASE2(AccessChain),
CASE6(AccessChain),
CASE1(InBoundsAccessChain),
CASE2(InBoundsAccessChain),
CASE6(InBoundsAccessChain),
// PtrAccessChain also has an element Id.
CASE2(PtrAccessChain),
CASE3(PtrAccessChain),
CASE6(PtrAccessChain),
CASE2(InBoundsPtrAccessChain),
CASE3(InBoundsPtrAccessChain),
CASE6(InBoundsPtrAccessChain),
}));
#undef CASE1
#undef CASE2
#undef CASE3
#undef CASE4
#undef CASE5
#undef CASE6
// clang-format on
using OpSpecConstantOpTestWithTwoIdsThenLiteralNumbers =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<spv::Op>>>;
// The operands to the OpSpecConstantOp opcode are two Ids followed by a
// sequence of literal numbers.
TEST_P(OpSpecConstantOpTestWithTwoIdsThenLiteralNumbers, Assembly) {
std::stringstream input;
input << "%2 = OpSpecConstantOp %1 " << GetParam().name() << " %3 %4";
for (auto number : GetParam().operands()) input << " " << number;
input << "\n";
EXPECT_THAT(CompiledInstructions(input.str()),
Eq(MakeInstruction(spv::Op::OpSpecConstantOp,
{1, 2, uint32_t(GetParam().value()), 3, 4},
GetParam().operands())));
// Check the disassembler as well.
EXPECT_THAT(EncodeAndDecodeSuccessfully(input.str()), input.str());
}
#define CASE(NAME) spv::Op::Op##NAME, #NAME
INSTANTIATE_TEST_SUITE_P(
TextToBinaryOpSpecConstantOp,
OpSpecConstantOpTestWithTwoIdsThenLiteralNumbers,
::testing::ValuesIn(std::vector<EnumCase<spv::Op>>{
// For VectorShuffle, there are two vector operands, and at least
// two selector Ids. OpenCL can have up to 16-element vectors.
{CASE(VectorShuffle), {0, 0}},
{CASE(VectorShuffle), {4, 3, 2, 1}},
{CASE(VectorShuffle), {0, 2, 4, 6, 1, 3, 5, 7}},
{CASE(VectorShuffle),
{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}},
// For CompositeInsert, there is an object to insert, the target
// composite, and then literal indices.
{CASE(CompositeInsert), {0}},
{CASE(CompositeInsert), {4, 3, 99, 1}},
}));
using OpSpecConstantOpTestWithOneIdThenLiteralNumbers =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<EnumCase<spv::Op>>>;
// The operands to the OpSpecConstantOp opcode are one Id followed by a
// sequence of literal numbers.
TEST_P(OpSpecConstantOpTestWithOneIdThenLiteralNumbers, Assembly) {
std::stringstream input;
input << "%2 = OpSpecConstantOp %1 " << GetParam().name() << " %3";
for (auto number : GetParam().operands()) input << " " << number;
input << "\n";
EXPECT_THAT(CompiledInstructions(input.str()),
Eq(MakeInstruction(spv::Op::OpSpecConstantOp,
{1, 2, uint32_t(GetParam().value()), 3},
GetParam().operands())));
// Check the disassembler as well.
EXPECT_THAT(EncodeAndDecodeSuccessfully(input.str()), input.str());
}
#define CASE(NAME) spv::Op::Op##NAME, #NAME
INSTANTIATE_TEST_SUITE_P(
TextToBinaryOpSpecConstantOp,
OpSpecConstantOpTestWithOneIdThenLiteralNumbers,
::testing::ValuesIn(std::vector<EnumCase<spv::Op>>{
// For CompositeExtract, the universal limit permits up to 255 literal
// indices. Let's only test a few.
{CASE(CompositeExtract), {0}},
{CASE(CompositeExtract), {0, 99, 42, 16, 17, 12, 19}},
}));
// TODO(dneto): OpConstantTrue
// TODO(dneto): OpConstantFalse
// TODO(dneto): OpConstantComposite
// TODO(dneto): OpConstantSampler: other variations Param is 0 or 1
// TODO(dneto): OpConstantNull
// TODO(dneto): OpSpecConstantTrue
// TODO(dneto): OpSpecConstantFalse
// TODO(dneto): OpSpecConstantComposite
// TODO(dneto): Negative tests for OpSpecConstantOp
} // namespace
} // namespace spvtools