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.
This commit is contained in:
David Neto 2015-11-06 18:08:49 -05:00
parent 229b90f6f4
commit 9e545d7968
4 changed files with 82 additions and 14 deletions

View File

@ -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<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;

View 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;

View 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>{

View File

@ -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 <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;