mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-25 21:10:04 +00:00
57a2441791
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.
347 lines
15 KiB
C++
347 lines
15 KiB
C++
// Copyright (c) 2016 Google 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.
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "test_fixture.h"
|
|
#include "unit_spirv.h"
|
|
|
|
#include "source/name_mapper.h"
|
|
|
|
using libspirv::FriendlyNameMapper;
|
|
using libspirv::NameMapper;
|
|
using spvtest::ScopedContext;
|
|
using ::testing::Eq;
|
|
|
|
namespace {
|
|
|
|
TEST(TrivialNameTest, Samples) {
|
|
auto mapper = libspirv::GetTrivialNameMapper();
|
|
EXPECT_EQ(mapper(1), "1");
|
|
EXPECT_EQ(mapper(1999), "1999");
|
|
EXPECT_EQ(mapper(1024), "1024");
|
|
}
|
|
|
|
// A test case for the name mappers that actually look at an assembled module.
|
|
struct NameIdCase {
|
|
std::string assembly; // Input assembly text
|
|
uint32_t id;
|
|
std::string expected_name;
|
|
};
|
|
|
|
using FriendlyNameTest =
|
|
spvtest::TextToBinaryTestBase<::testing::TestWithParam<NameIdCase>>;
|
|
|
|
TEST_P(FriendlyNameTest, SingleMapping) {
|
|
ScopedContext context(SPV_ENV_UNIVERSAL_1_1);
|
|
auto words = CompileSuccessfully(GetParam().assembly, SPV_ENV_UNIVERSAL_1_1);
|
|
auto friendly_mapper =
|
|
FriendlyNameMapper(context.context, words.data(), words.size());
|
|
NameMapper mapper = friendly_mapper.GetNameMapper();
|
|
EXPECT_THAT(mapper(GetParam().id), Eq(GetParam().expected_name))
|
|
<< GetParam().assembly << std::endl
|
|
<< " for id " << GetParam().id;
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(ScalarType, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeVoid", 1, "void"},
|
|
{"%1 = OpTypeBool", 1, "bool"},
|
|
{"%1 = OpTypeInt 8 0", 1, "uchar"},
|
|
{"%1 = OpTypeInt 8 1", 1, "char"},
|
|
{"%1 = OpTypeInt 16 0", 1, "ushort"},
|
|
{"%1 = OpTypeInt 16 1", 1, "short"},
|
|
{"%1 = OpTypeInt 32 0", 1, "uint"},
|
|
{"%1 = OpTypeInt 32 1", 1, "int"},
|
|
{"%1 = OpTypeInt 64 0", 1, "ulong"},
|
|
{"%1 = OpTypeInt 64 1", 1, "long"},
|
|
{"%1 = OpTypeInt 1 0", 1, "u1"},
|
|
{"%1 = OpTypeInt 1 1", 1, "i1"},
|
|
{"%1 = OpTypeInt 33 0", 1, "u33"},
|
|
{"%1 = OpTypeInt 33 1", 1, "i33"},
|
|
|
|
{"%1 = OpTypeFloat 16", 1, "half"},
|
|
{"%1 = OpTypeFloat 32", 1, "float"},
|
|
{"%1 = OpTypeFloat 64", 1, "double"},
|
|
{"%1 = OpTypeFloat 10", 1, "fp10"},
|
|
{"%1 = OpTypeFloat 55", 1, "fp55"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
VectorType, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeBool %2 = OpTypeVector %1 1", 2, "v1bool"},
|
|
{"%1 = OpTypeBool %2 = OpTypeVector %1 2", 2, "v2bool"},
|
|
{"%1 = OpTypeBool %2 = OpTypeVector %1 3", 2, "v3bool"},
|
|
{"%1 = OpTypeBool %2 = OpTypeVector %1 4", 2, "v4bool"},
|
|
|
|
{"%1 = OpTypeInt 8 0 %2 = OpTypeVector %1 2", 2, "v2uchar"},
|
|
{"%1 = OpTypeInt 16 1 %2 = OpTypeVector %1 3", 2, "v3short"},
|
|
{"%1 = OpTypeInt 32 0 %2 = OpTypeVector %1 4", 2, "v4uint"},
|
|
{"%1 = OpTypeInt 64 1 %2 = OpTypeVector %1 3", 2, "v3long"},
|
|
{"%1 = OpTypeInt 20 0 %2 = OpTypeVector %1 4", 2, "v4u20"},
|
|
{"%1 = OpTypeInt 21 1 %2 = OpTypeVector %1 3", 2, "v3i21"},
|
|
|
|
{"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2", 2, "v2float"},
|
|
// OpName overrides the element name.
|
|
{"OpName %1 \"time\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2", 2,
|
|
"v2time"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
MatrixType, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeBool %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 2", 3,
|
|
"mat2v2bool"},
|
|
{"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 3", 3,
|
|
"mat3v2float"},
|
|
{"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 4", 3,
|
|
"mat4v2float"},
|
|
{"OpName %1 \"time\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = "
|
|
"OpTypeMatrix %2 4",
|
|
3, "mat4v2time"},
|
|
{"OpName %2 \"lat_long\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 "
|
|
"= OpTypeMatrix %2 4",
|
|
3, "mat4lat_long"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
OpName, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"OpName %1 \"abcdefg\"", 1, "abcdefg"},
|
|
{"OpName %1 \"Hello world!\"", 1, "Hello_world_"},
|
|
{"OpName %1 \"0123456789\"", 1, "0123456789"},
|
|
{"OpName %1 \"_\"", 1, "_"},
|
|
// An empty string is not valid for SPIR-V assembly IDs.
|
|
{"OpName %1 \"\"", 1, "_"},
|
|
// Test uniqueness when presented with things mapping to "_"
|
|
{"OpName %1 \"\" OpName %2 \"\"", 1, "_"},
|
|
{"OpName %1 \"\" OpName %2 \"\"", 2, "__0"},
|
|
{"OpName %1 \"\" OpName %2 \"\" OpName %3 \"_\"", 3, "__1"},
|
|
// Test uniqueness of names that are forced to be
|
|
// numbers.
|
|
{"OpName %1 \"2\" OpName %2 \"2\"", 1, "2"},
|
|
{"OpName %1 \"2\" OpName %2 \"2\"", 2, "2_0"},
|
|
// Test uniqueness in the face of forward references
|
|
// for Ids that don't already have friendly names.
|
|
// In particular, the first OpDecorate assigns the name, and
|
|
// the second one can't override it.
|
|
{"OpDecorate %1 Volatile OpDecorate %1 Restrict", 1, "1"},
|
|
// But a forced name can override the name that
|
|
// would have been assigned via the OpDecorate
|
|
// forward reference.
|
|
{"OpName %1 \"mememe\" OpDecorate %1 Volatile OpDecorate %1 Restrict",
|
|
1, "mememe"},
|
|
// OpName can override other inferences. We assume valid instruction
|
|
// ordering, where OpName precedes type definitions.
|
|
{"OpName %1 \"myfloat\" %1 = OpTypeFloat 32", 1, "myfloat"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
UniquenessHeuristic, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 1, "void"},
|
|
{"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 2, "void_0"},
|
|
{"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 3, "void_1"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(Arrays, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"OpName %2 \"FortyTwo\" %1 = OpTypeFloat 32 "
|
|
"%2 = OpConstant %1 42 %3 = OpTypeArray %1 %2",
|
|
3, "_arr_float_FortyTwo"},
|
|
{"%1 = OpTypeInt 32 0 "
|
|
"%2 = OpTypeRuntimeArray %1",
|
|
2, "_runtimearr_uint"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(Structs, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeBool "
|
|
"%2 = OpTypeStruct %1 %1 %1",
|
|
2, "_struct_2"},
|
|
{"%1 = OpTypeBool "
|
|
"%2 = OpTypeStruct %1 %1 %1 "
|
|
"%3 = OpTypeStruct %2 %2",
|
|
3, "_struct_3"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
Pointer, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeFloat 32 %2 = OpTypePointer Workgroup %1", 2,
|
|
"_ptr_Workgroup_float"},
|
|
{"%1 = OpTypeBool %2 = OpTypePointer Private %1", 2,
|
|
"_ptr_Private_bool"},
|
|
// OpTypeForwardPointer doesn't force generation of the name for its
|
|
// target type.
|
|
{"%1 = OpTypeBool OpTypeForwardPointer %2 Private %2 = OpTypePointer "
|
|
"Private %1",
|
|
2, "_ptr_Private_bool"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(ExoticTypes, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeEvent", 1, "Event"},
|
|
{"%1 = OpTypeDeviceEvent", 1, "DeviceEvent"},
|
|
{"%1 = OpTypeReserveId", 1, "ReserveId"},
|
|
{"%1 = OpTypeQueue", 1, "Queue"},
|
|
{"%1 = OpTypeOpaque \"hello world!\"", 1,
|
|
"Opaque_hello_world_"},
|
|
{"%1 = OpTypePipe ReadOnly", 1, "PipeReadOnly"},
|
|
{"%1 = OpTypePipe WriteOnly", 1, "PipeWriteOnly"},
|
|
{"%1 = OpTypePipe ReadWrite", 1, "PipeReadWrite"},
|
|
{"%1 = OpTypePipeStorage", 1, "PipeStorage"},
|
|
{"%1 = OpTypeNamedBarrier", 1, "NamedBarrier"},
|
|
}), );
|
|
|
|
// Makes a test case for a BuiltIn variable declaration.
|
|
NameIdCase BuiltInCase(std::string assembly_name, std::string expected) {
|
|
return NameIdCase{std::string("OpDecorate %1 BuiltIn ") + assembly_name +
|
|
" %1 = OpVariable %2 Input",
|
|
1, expected};
|
|
}
|
|
|
|
// Makes a test case for a BuiltIn variable declaration. In this overload,
|
|
// the expected result is the same as the assembly name.
|
|
NameIdCase BuiltInCase(std::string assembly_name) {
|
|
return BuiltInCase(assembly_name, assembly_name);
|
|
}
|
|
|
|
// Makes a test case for a BuiltIn variable declaration. In this overload,
|
|
// the expected result is the same as the assembly name, but with a "gl_"
|
|
// prefix.
|
|
NameIdCase BuiltInGLCase(std::string assembly_name) {
|
|
return BuiltInCase(assembly_name, std::string("gl_") + assembly_name);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
BuiltIns, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
BuiltInGLCase("Position"),
|
|
BuiltInGLCase("PointSize"),
|
|
BuiltInGLCase("ClipDistance"),
|
|
BuiltInGLCase("CullDistance"),
|
|
BuiltInCase("VertexId", "gl_VertexID"),
|
|
BuiltInCase("InstanceId", "gl_InstanceID"),
|
|
BuiltInCase("PrimitiveId", "gl_PrimitiveID"),
|
|
BuiltInCase("InvocationId", "gl_InvocationID"),
|
|
BuiltInGLCase("Layer"),
|
|
BuiltInGLCase("ViewportIndex"),
|
|
BuiltInGLCase("TessLevelOuter"),
|
|
BuiltInGLCase("TessLevelInner"),
|
|
BuiltInGLCase("TessCoord"),
|
|
BuiltInGLCase("PatchVertices"),
|
|
BuiltInGLCase("FragCoord"),
|
|
BuiltInGLCase("PointCoord"),
|
|
BuiltInGLCase("FrontFacing"),
|
|
BuiltInCase("SampleId", "gl_SampleID"),
|
|
BuiltInGLCase("SamplePosition"),
|
|
BuiltInGLCase("SampleMask"),
|
|
BuiltInGLCase("FragDepth"),
|
|
BuiltInGLCase("HelperInvocation"),
|
|
BuiltInCase("NumWorkgroups", "gl_NumWorkGroups"),
|
|
BuiltInCase("WorkgroupSize", "gl_WorkGroupSize"),
|
|
BuiltInCase("WorkgroupId", "gl_WorkGroupID"),
|
|
BuiltInCase("LocalInvocationId", "gl_LocalInvocationID"),
|
|
BuiltInCase("GlobalInvocationId", "gl_GlobalInvocationID"),
|
|
BuiltInGLCase("LocalInvocationIndex"),
|
|
BuiltInCase("WorkDim"),
|
|
BuiltInCase("GlobalSize"),
|
|
BuiltInCase("EnqueuedWorkgroupSize"),
|
|
BuiltInCase("GlobalOffset"),
|
|
BuiltInCase("GlobalLinearId"),
|
|
BuiltInCase("SubgroupSize"),
|
|
BuiltInCase("SubgroupMaxSize"),
|
|
BuiltInCase("NumSubgroups"),
|
|
BuiltInCase("NumEnqueuedSubgroups"),
|
|
BuiltInCase("SubgroupId"),
|
|
BuiltInCase("SubgroupLocalInvocationId"),
|
|
BuiltInGLCase("VertexIndex"),
|
|
BuiltInGLCase("InstanceIndex"),
|
|
BuiltInCase("SubgroupEqMaskKHR"),
|
|
BuiltInCase("SubgroupGeMaskKHR"),
|
|
BuiltInCase("SubgroupGtMaskKHR"),
|
|
BuiltInCase("SubgroupLeMaskKHR"),
|
|
BuiltInCase("SubgroupLtMaskKHR"),
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(DebugNameOverridesBuiltin, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"OpName %1 \"foo\" OpDecorate %1 BuiltIn WorkDim "
|
|
"%1 = OpVariable %2 Input",
|
|
1, "foo"}}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
SimpleIntegralConstants, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 0", 2, "uint_0"},
|
|
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 1", 2, "uint_1"},
|
|
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 2", 2, "uint_2"},
|
|
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 9", 2, "uint_9"},
|
|
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 42", 2, "uint_42"},
|
|
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 0", 2, "int_0"},
|
|
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 1", 2, "int_1"},
|
|
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 2", 2, "int_2"},
|
|
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 9", 2, "int_9"},
|
|
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 42", 2, "int_42"},
|
|
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 -42", 2, "int_n42"},
|
|
// Exotic bit widths
|
|
{"%1 = OpTypeInt 33 0 %2 = OpConstant %1 0", 2, "u33_0"},
|
|
{"%1 = OpTypeInt 33 1 %2 = OpConstant %1 10", 2, "i33_10"},
|
|
{"%1 = OpTypeInt 33 1 %2 = OpConstant %1 -19", 2, "i33_n19"},
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
SimpleFloatConstants, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ff4p+16", 2,
|
|
"half_0x1_ff4p_16"},
|
|
{"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.d2cp-10", 2,
|
|
"half_n0x1_d2cpn10"},
|
|
// 32-bit floats
|
|
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -3.125", 2, "float_n3_125"},
|
|
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128", 2,
|
|
"float_0x1_8p_128"}, // NaN
|
|
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128", 2,
|
|
"float_n0x1_0002p_128"}, // NaN
|
|
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128", 2,
|
|
"float_0x1p_128"}, // Inf
|
|
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128", 2,
|
|
"float_n0x1p_128"}, // -Inf
|
|
// 64-bit floats
|
|
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -3.125", 2, "double_n3_125"},
|
|
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.ffffffffffffap-1023", 2,
|
|
"double_0x1_ffffffffffffapn1023"}, // small normal
|
|
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.ffffffffffffap-1023", 2,
|
|
"double_n0x1_ffffffffffffapn1023"},
|
|
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024", 2,
|
|
"double_0x1_8p_1024"}, // NaN
|
|
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0002p+1024", 2,
|
|
"double_n0x1_0002p_1024"}, // NaN
|
|
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024", 2,
|
|
"double_0x1p_1024"}, // Inf
|
|
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024", 2,
|
|
"double_n0x1p_1024"}, // -Inf
|
|
}), );
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
BooleanConstants, FriendlyNameTest,
|
|
::testing::ValuesIn(std::vector<NameIdCase>{
|
|
{"%1 = OpTypeBool\n%2 = OpConstantTrue %1", 2, "true"},
|
|
{"%1 = OpTypeBool\n%2 = OpConstantFalse %1", 2, "false"},
|
|
}), );
|
|
|
|
} // anonymous namespace
|