SPIRV-Tools/source/text_handler.h

341 lines
14 KiB
C++

// Copyright (c) 2015 The Khronos Group Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#ifndef LIBSPIRV_TEXT_HANDLER_H_
#define LIBSPIRV_TEXT_HANDLER_H_
#include <iomanip>
#include <sstream>
#include <type_traits>
#include <unordered_map>
#include "diagnostic.h"
#include "instruction.h"
#include "libspirv/libspirv.h"
#include "text.h"
namespace libspirv {
// Structures
// This is a lattice for tracking types.
enum class IdTypeClass {
kBottom = 0, // We have no information yet.
kScalarIntegerType,
kScalarFloatType,
kOtherType
};
// Contains ID type information that needs to be tracked across all Ids.
// Bitwidth is only valid when type_class is kScalarIntegerType or
// kScalarFloatType.
struct IdType {
uint32_t bitwidth; // Safe to assume that we will not have > 2^32 bits.
bool isSigned; // This is only significant if type_class is integral.
IdTypeClass type_class;
};
// Default equality operator for IdType. Tests if all members are the same.
inline bool operator==(const IdType& first, const IdType& second) {
return (first.bitwidth == second.bitwidth) &&
(first.isSigned == second.isSigned) &&
(first.type_class == second.type_class);
}
// Tests whether any member of the IdTypes do not match.
inline bool operator!=(const IdType& first, const IdType& second) {
return !(first == second);
}
// A value representing an unknown type.
extern const IdType kUnknownType;
// Returns true if the type is a scalar integer type.
inline bool isScalarIntegral(const IdType& type) {
return type.type_class == IdTypeClass::kScalarIntegerType;
}
// Returns true if the type is a scalar floating point type.
inline bool isScalarFloating(const IdType& type) {
return type.type_class == IdTypeClass::kScalarFloatType;
}
// Returns the number of bits in the type.
// This is only valid for bottom, scalar integer, and scalar floating
// classes. For bottom, assume 32 bits.
inline int assumedBitWidth(const IdType& type) {
switch (type.type_class) {
case IdTypeClass::kBottom:
return 32;
case IdTypeClass::kScalarIntegerType:
case IdTypeClass::kScalarFloatType:
return type.bitwidth;
default:
break;
}
// We don't care about this case.
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:
AssemblyContext(spv_text text, spv_diagnostic* diagnostic)
: current_position_({}),
pDiagnostic_(diagnostic),
text_(text),
bound_(1) {}
// Assigns a new integer value to the given text ID, or returns the previously
// assigned integer value if the ID has been seen before.
uint32_t spvNamedIdAssignOrGet(const char* textValue);
// Returns the largest largest numeric ID that has been assigned.
uint32_t getBound() const;
// Advances position to point to the next word in the input stream.
// Returns SPV_SUCCESS on success.
spv_result_t advance();
// Sets word to the next word in the input text. Fills endPosition with
// the next location past the end of the word.
spv_result_t getWord(std::string& word, spv_position endPosition);
// Returns the next word in the input stream. It is invalid to call this
// method if position has been set to a location in the stream that does not
// exist. If there are no subsequent words, the empty string will be returend.
std::string getWord() const;
// Returns true if the next word in the input is the start of a new Opcode.
bool startsWithOp();
// Returns true if the next word in the input is the start of a new
// instruction.
bool isStartOfNewInst();
// Returns a diagnostic object initialized with current position in the input
// stream, and for the given error code. Any data written to this object will
// show up in pDiagnsotic on destruction.
DiagnosticStream diagnostic(spv_result_t error) {
return DiagnosticStream(current_position_, pDiagnostic_, error);
}
// Returns a diagnostic object with the default assembly error code.
DiagnosticStream diagnostic() {
// The default failure for assembly is invalid text.
return diagnostic(SPV_ERROR_INVALID_TEXT);
}
// Returns then next characted in the input stream.
char peek() const;
// Returns true if there is more text in the input stream.
bool hasText() const;
// Seeks the input stream forward by 'size' characters.
void seekForward(uint32_t size);
// Sets the current position in the input stream to the given position.
void setPosition(const spv_position_t& newPosition) {
current_position_ = newPosition;
}
// Returns the current position in the input stream.
const spv_position_t& position() const { return current_position_; }
// Appends the given 32-bit value to the given instruction.
// Returns SPV_SUCCESS if the value could be correctly inserted in the
// instruction.
spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t* pInst);
// Appends the given string to the given instruction.
// Returns SPV_SUCCESS if the value could be correctly inserted in the
// instruction.
spv_result_t binaryEncodeString(const char* value, spv_instruction_t* pInst);
// Appends the given numeric literal to the given instruction.
// Validates and respects the bitwidth supplied in the IdType argument.
// If the type is of class kBottom the value will be encoded as a
// 32-bit integer.
// Returns SPV_SUCCESS if the value could be correctly added to the
// instruction. Returns the given error code on failure, and emits
// a diagnotic if that error code is not SPV_FAILED_MATCH.
spv_result_t binaryEncodeNumericLiteral(const char* numeric_literal,
spv_result_t error_code,
const IdType& type,
spv_instruction_t* pInst);
// Returns the IdType associated with this type-generating value.
// If the type has not been previously recorded with recordTypeDefinition,
// kUnknownType will be returned.
IdType getTypeOfTypeGeneratingValue(uint32_t value) const;
// Returns the IdType that represents the return value of this Value
// generating instruction.
// If the value has not been recorded with recordTypeIdForValue, or the type
// could not be determined kUnknownType will be returned.
IdType getTypeOfValueInstruction(uint32_t value) const;
// Tracks the type-defining instruction. The result of the tracking can
// later be queried using getValueType.
// pInst is expected to be completely filled in by the time this instruction
// is called.
// Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error.
spv_result_t recordTypeDefinition(const spv_instruction_t* pInst);
// Tracks the relationship between the value and its type.
spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type);
// Records the given Id as being the import of the given extended instruction
// type.
spv_result_t recordIdAsExtInstImport(uint32_t id, spv_ext_inst_type_t type);
// Returns the extended instruction type corresponding to the import with
// the given Id, if it exists. Returns SPV_EXT_INST_TYPE_NONE if the
// id is not the id for an extended instruction type.
spv_ext_inst_type_t getExtInstTypeForId(uint32_t id) const;
// Parses a numeric value of a given type from the given text. The number
// should take up the entire string, and should be within bounds for the
// target type. On success, returns SPV_SUCCESS and populates the object
// referenced by value_pointer. On failure, returns the given error code,
// and emits a diagnostic if that error code is not SPV_FAILED_MATCH.
template <typename T>
spv_result_t parseNumber(const char* text, spv_result_t error_code,
T* value_pointer,
const char* error_message_fragment) {
// C++11 doesn't define std::istringstream(int8_t&), so calling this method
// with a single-byte type leads to implementation-defined behaviour.
// Similarly for uint8_t.
static_assert(sizeof(T) > 1,
"Don't use a single-byte type this parse method");
std::istringstream text_stream(text);
// Allow both decimal and hex input for integers.
// It also allows octal input, but we don't care about that case.
text_stream >> std::setbase(0);
text_stream >> *value_pointer;
bool ok = true;
// We should have read something.
ok = (text[0] != 0) && !text_stream.bad();
// It should have been all the text.
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 && text[0] == '-')
ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer);
if (ok) return SPV_SUCCESS;
return diagnostic(error_code) << error_message_fragment << text;
}
private:
// Appends the given floating point literal to the given instruction.
// Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
// returns the given error code, and emits a diagnostic if that error
// code is not SPV_FAILED_MATCH.
// Only 32 and 64 bit floating point numbers are supported.
spv_result_t binaryEncodeFloatingPointLiteral(const char* numeric_literal,
spv_result_t error_code,
const IdType& type,
spv_instruction_t* pInst);
// Appends the given integer literal to the given instruction.
// Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
// returns the given error code, and emits a diagnostic if that error
// code is not SPV_FAILED_MATCH.
// Integers up to 64 bits are supported.
spv_result_t binaryEncodeIntegerLiteral(const char* numeric_literal,
spv_result_t error_code,
const IdType& type,
spv_instruction_t* pInst);
// Returns SPV_SUCCESS if the given value fits within the target scalar
// integral type. The target type may have an unusual bit width.
// If the value was originally specified as a hexadecimal number, then
// the overflow bits should be zero. If it was hex and the target type is
// signed, then return the sign-extended value through the
// updated_value_for_hex pointer argument.
// On failure, return the given error code and emit a diagnostic if that error
// code is not SPV_FAILED_MATCH.
template <typename T>
spv_result_t checkRangeAndIfHexThenSignExtend(T value,
spv_result_t error_code,
const IdType& type, bool is_hex,
T* updated_value_for_hex);
// Writes the given 64-bit literal value into the instruction.
// return SPV_SUCCESS if the value could be written in the instruction.
spv_result_t binaryEncodeU64(const uint64_t value, spv_instruction_t* pInst);
// Maps ID names to their corresponding numerical ids.
using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
// Maps type-defining IDs to their IdType.
using spv_id_to_type_map = std::unordered_map<uint32_t, IdType>;
// Maps Ids to the id of their type.
using spv_id_to_type_id = std::unordered_map<uint32_t, uint32_t>;
spv_named_id_table named_ids_;
spv_id_to_type_map types_;
spv_id_to_type_id value_types_;
// Maps an extended instruction import Id to the extended instruction type.
std::unordered_map<uint32_t, spv_ext_inst_type_t> import_id_to_ext_inst_type_;
spv_position_t current_position_;
spv_diagnostic* pDiagnostic_;
spv_text text_;
uint32_t bound_;
};
}
#endif // _LIBSPIRV_TEXT_HANDLER_H_