From 9e545d79680b310451aaa0ccca9a70588a808b64 Mon Sep 17 00:00:00 2001 From: David Neto Date: Fri, 6 Nov 2015 18:08:49 -0500 Subject: [PATCH] Assembler supports hex float constants. The bit pattern for a hex float is preserved through assembly and disassembly. You can use a hex float to express Inf and any kind of NaN in a portable way. --- source/text_handler.cpp | 7 +++++-- source/text_handler.h | 37 +++++++++++++++++++++++++++-------- test/TextToBinary.Constant.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++ test/TextToBinary.cpp | 8 ++++---- 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/source/text_handler.cpp b/source/text_handler.cpp index b46fa01..6909764 100644 --- a/source/text_handler.cpp +++ b/source/text_handler.cpp @@ -38,10 +38,13 @@ #include "opcode.h" #include "text.h" #include "util/bitutils.h" +#include "util/hex_float.h" namespace { using spvutils::BitwiseCast; +using spvutils::FloatProxy; +using spvutils::HexFloat; /// @brief Advance text to the start of the next line /// @@ -370,14 +373,14 @@ spv_result_t AssemblyContext::binaryEncodeFloatingPointLiteral( return diagnostic(SPV_ERROR_INTERNAL) << "Unsupported yet: 16-bit float constants."; case 32: { - float fVal; + spvutils::HexFloat> fVal(0.0f); if (auto error = parseNumber(val, error_code, &fVal, "Invalid 32-bit float literal: ")) return error; return binaryEncodeU32(BitwiseCast(fVal), pInst); } break; case 64: { - double dVal; + spvutils::HexFloat> dVal(0.0); if (auto error = parseNumber(val, error_code, &dVal, "Invalid 64-bit float literal: ")) return error; diff --git a/source/text_handler.h b/source/text_handler.h index 2662846..fd4900c 100644 --- a/source/text_handler.h +++ b/source/text_handler.h @@ -28,7 +28,6 @@ #define LIBSPIRV_TEXT_HANDLER_H_ #include -#include #include #include #include @@ -100,6 +99,32 @@ inline int assumedBitWidth(const IdType& type) { return 0; } +// A templated class with a static member function Clamp, where Clamp +// sets a referenced value of type T to 0 if T is an unsigned +// integer type, and returns true if it modified the referenced +// value. +template +class ClampToZeroIfUnsignedType { + public: + // The default specialization does not clamp the value. + static bool Clamp(T*) { return false; } +}; + +// The specialization of ClampToZeroIfUnsignedType for unsigned integer +// types. +template +class ClampToZeroIfUnsignedType< + T, typename std::enable_if::value>::type> { + public: + static bool Clamp(T* value_pointer) { + if (*value_pointer) { + *value_pointer = 0; + return true; + } + return false; + } +}; + // Encapsulates the data used during the assembly of a SPIR-V module. class AssemblyContext { public: @@ -237,15 +262,11 @@ class AssemblyContext { ok = ok && text_stream.eof(); // It should have been in range. ok = ok && !text_stream.fail(); + // Work around a bug in the GNU C++11 library. It will happily parse // "-1" for uint16_t as 65535. - if (ok && !std::is_signed::value && (text[0] == '-') && - *value_pointer != 0) { - ok = false; - // Match expected error behaviour of std::istringstream::operator>> - // on failure to parse. - *value_pointer = 0; - } + if (ok && text[0] == '-') + ok = !ClampToZeroIfUnsignedType::Clamp(value_pointer); if (ok) return SPV_SUCCESS; return diagnostic(error_code) << error_message_fragment << text; diff --git a/test/TextToBinary.Constant.cpp b/test/TextToBinary.Constant.cpp index c14247e..63f6e05 100644 --- a/test/TextToBinary.Constant.cpp +++ b/test/TextToBinary.Constant.cpp @@ -152,6 +152,18 @@ INSTANTIATE_TEST_CASE_P( {"OpTypeFloat 32", "10.0", Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), MakeInstruction(SpvOpConstant, {1, 2, 0x41200000})})}, + {"OpTypeFloat 32", "-0x1p+128", // -infinity + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0xFF800000})})}, + {"OpTypeFloat 32", "0x1p+128", // +infinity + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0x7F800000})})}, + {"OpTypeFloat 32", "-0x1.8p+128", // A -NaN + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0xFFC00000})})}, + {"OpTypeFloat 32", "-0x1.0002p+128", // A +NaN + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0xFF800100})})}, // Check 48 bits {"OpTypeInt 48 0", "0x1234", Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}), @@ -384,6 +396,38 @@ INSTANTIATE_TEST_CASE_P( "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -1.79769e+308\n", })); +// clang-format off +// (Clang-format really wants to break up these strings across lines. +INSTANTIATE_TEST_CASE_P( + OpConstantRoundTripNonFinite, RoundTripTest, + ::testing::ValuesIn(std::vector{ + "%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_CASE_P( OpSpecConstantRoundTrip, RoundTripTest, ::testing::ValuesIn(std::vector{ diff --git a/test/TextToBinary.cpp b/test/TextToBinary.cpp index c988a98..739c5f6 100644 --- a/test/TextToBinary.cpp +++ b/test/TextToBinary.cpp @@ -24,15 +24,15 @@ // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +#include +#include +#include + #include "TestFixture.h" #include "gmock/gmock.h" #include "UnitSPIRV.h" #include "util/bitutils.h" -#include -#include -#include - namespace { using libspirv::AssemblyContext; -- 2.7.4