mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 00:40:14 +00:00
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:
parent
229b90f6f4
commit
9e545d7968
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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>{
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user