Assembler supports hex float constants.
authorDavid Neto <dneto@google.com>
Fri, 6 Nov 2015 23:08:49 +0000 (18:08 -0500)
committerDavid Neto <dneto@google.com>
Tue, 10 Nov 2015 20:58:03 +0000 (15:58 -0500)
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
source/text_handler.h
test/TextToBinary.Constant.cpp
test/TextToBinary.cpp

index b46fa01..6909764 100644 (file)
 #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<FloatProxy<float>> fVal(0.0f);
       if (auto error = parseNumber(val, error_code, &fVal,
                                    "Invalid 32-bit float literal: "))
         return error;
       return binaryEncodeU32(BitwiseCast<uint32_t>(fVal), pInst);
     } break;
     case 64: {
-      double dVal;
+      spvutils::HexFloat<FloatProxy<double>> dVal(0.0);
       if (auto error = parseNumber(val, error_code, &dVal,
                                    "Invalid 64-bit float literal: "))
         return error;
index 2662846..fd4900c 100644 (file)
@@ -28,7 +28,6 @@
 #define LIBSPIRV_TEXT_HANDLER_H_
 
 #include <iomanip>
-#include <limits>
 #include <sstream>
 #include <type_traits>
 #include <unordered_map>
@@ -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 <typename T, typename = void>
+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 <typename T>
+class ClampToZeroIfUnsignedType<
+    T, typename std::enable_if<std::is_unsigned<T>::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<T>::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<T>::Clamp(value_pointer);
 
     if (ok) return SPV_SUCCESS;
     return diagnostic(error_code) << error_message_fragment << text;
index c14247e..63f6e05 100644 (file)
@@ -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<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_CASE_P(
     OpSpecConstantRoundTrip, RoundTripTest,
     ::testing::ValuesIn(std::vector<std::string>{
index c988a98..739c5f6 100644 (file)
 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 
+#include <algorithm>
+#include <utility>
+#include <vector>
+
 #include "TestFixture.h"
 #include "gmock/gmock.h"
 #include "UnitSPIRV.h"
 #include "util/bitutils.h"
 
-#include <algorithm>
-#include <utility>
-#include <vector>
-
 namespace {
 
 using libspirv::AssemblyContext;