mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
376 lines
14 KiB
C++
376 lines
14 KiB
C++
// Copyright (c) 2025 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Compressed grammar tables.
|
|
|
|
#include "source/table2.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstring>
|
|
|
|
#include "source/extensions.h"
|
|
#include "source/latest_version_spirv_header.h"
|
|
#include "source/spirv_constant.h"
|
|
#include "source/spirv_target_env.h"
|
|
#include "spirv-tools/libspirv.hpp"
|
|
|
|
namespace spvtools {
|
|
namespace {
|
|
|
|
// This is used in the source for the generated tables.
|
|
constexpr inline IndexRange IR(uint32_t first, uint32_t count) {
|
|
return IndexRange{first, count};
|
|
}
|
|
|
|
struct NameIndex {
|
|
// Location of the null-terminated name in the global string table kStrings.
|
|
IndexRange name;
|
|
// Index of this name's entry in the corresponding by-value table.
|
|
uint32_t index;
|
|
};
|
|
|
|
struct NameValue {
|
|
// Location of the null-terminated name in the global string table kStrings.
|
|
IndexRange name;
|
|
// Enum value in the binary format.
|
|
uint32_t value;
|
|
};
|
|
|
|
// The generated include file contains variables:
|
|
//
|
|
// std::array<NameIndex,...> kOperandNames:
|
|
// Operand names and index, ordered by (operand kind, name)
|
|
// The index part is the named entry's index in kOperandsByValue array.
|
|
// Aliases are included as their own entries.
|
|
//
|
|
// std::array<OperandDesc, ...> kOperandsByValue:
|
|
// Operand descriptions, ordered by (operand kind, operand enum value).
|
|
//
|
|
// std::array<NameIndex,...> kInstructionNames:
|
|
// Instruction names and index, ordered by (name, value)
|
|
// The index part is the named entry's index in kInstructionDesc array.
|
|
// Aliases are included as their own entries.
|
|
//
|
|
// std::array<InstructionDesc, ...> kInstructionDesc
|
|
// Instruction descriptions, ordered by opcode.
|
|
//
|
|
// const char kStrings[]
|
|
// Array of characters, referenced by IndexRanges elsewhere.
|
|
// Each IndexRange denotes a string.
|
|
//
|
|
// const IndexRange kAliasSpans[]
|
|
// Array of IndexRanges, where each represents a string by referencing
|
|
// the kStrings table.
|
|
// This array contains all sequences of alias strings used in the grammar.
|
|
// This table is referenced by an IndexRange elsewhere, i.e. by the
|
|
// 'aliases' field of an instruction or operand description.
|
|
//
|
|
// const spv::Capability kCapabilitySpans[]
|
|
// Array of capabilities, referenced by IndexRanges elsewhere.
|
|
// Contains all sequences of capabilities used in the grammar.
|
|
//
|
|
// const spvtools::Extension kExtensionSpans[] = {
|
|
// Array of extensions, referenced by IndexRanges elsewhere.
|
|
// Contains all sequences of extensions used in the grammar.
|
|
//
|
|
// const spv_operand_type_t kOperandSpans[] = {
|
|
// Array of operand types, referenced by IndexRanges elsewhere.
|
|
// Contains all sequences of operand types used in the grammar.
|
|
|
|
// Maps an operand kind to NameValue entries for that kind.
|
|
// The result is an IndexRange into kOperandNames, and are sorted
|
|
// by string name within that span.
|
|
IndexRange OperandNameRangeForKind(spv_operand_type_t type);
|
|
|
|
// Maps an operand kind to possible operands for that kind.
|
|
// The result is an IndexRange into kOperandsByValue, and the operands
|
|
// are sorted by value within that span.
|
|
IndexRange OperandByValueRangeForKind(spv_operand_type_t type);
|
|
|
|
// Maps an extended instruction set kind to NameValue entries for that kind.
|
|
// The result is an IndexRange into kExtIntNames, and are sorted
|
|
// by string name within that span.
|
|
IndexRange ExtInstNameRangeForKind(spv_ext_inst_type_t type);
|
|
|
|
// Maps an extended instruction set kind to possible operands for that kind.
|
|
// The result is an IndexRange into kExtInstByValue, and the instructions
|
|
// are sorted by opcode value within that span.
|
|
IndexRange ExtInstByValueRangeForKind(spv_ext_inst_type_t type);
|
|
|
|
// Returns the name of an extension, as an index into kStrings
|
|
IndexRange ExtensionToIndexRange(Extension extension);
|
|
|
|
#include "core_tables_body.inc"
|
|
|
|
// Returns a pointer to the null-terminated C-style string in the global
|
|
// strings table, as referenced by 'ir'. Assumes the given range is valid.
|
|
const char* getChars(IndexRange ir) {
|
|
assert(ir.first() < sizeof(kStrings));
|
|
return ir.apply(kStrings).data();
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
utils::Span<const spv_operand_type_t> OperandDesc::operands() const {
|
|
return operands_range.apply(kOperandSpans);
|
|
}
|
|
utils::Span<const char> OperandDesc::name() const {
|
|
return name_range.apply(kStrings);
|
|
}
|
|
utils::Span<const IndexRange> OperandDesc::aliases() const {
|
|
return name_range.apply(kAliasSpans);
|
|
}
|
|
utils::Span<const spv::Capability> OperandDesc::capabilities() const {
|
|
return capabilities_range.apply(kCapabilitySpans);
|
|
}
|
|
utils::Span<const spvtools::Extension> OperandDesc::extensions() const {
|
|
return extensions_range.apply(kExtensionSpans);
|
|
}
|
|
|
|
utils::Span<const spv_operand_type_t> InstructionDesc::operands() const {
|
|
return operands_range.apply(kOperandSpans);
|
|
}
|
|
utils::Span<const char> InstructionDesc::name() const {
|
|
return name_range.apply(kStrings);
|
|
}
|
|
utils::Span<const IndexRange> InstructionDesc::aliases() const {
|
|
return name_range.apply(kAliasSpans);
|
|
}
|
|
utils::Span<const spv::Capability> InstructionDesc::capabilities() const {
|
|
return capabilities_range.apply(kCapabilitySpans);
|
|
}
|
|
utils::Span<const spvtools::Extension> InstructionDesc::extensions() const {
|
|
return extensions_range.apply(kExtensionSpans);
|
|
}
|
|
|
|
utils::Span<const spv_operand_type_t> ExtInstDesc::operands() const {
|
|
return operands_range.apply(kOperandSpans);
|
|
}
|
|
utils::Span<const char> ExtInstDesc::name() const {
|
|
return name_range.apply(kStrings);
|
|
}
|
|
utils::Span<const spv::Capability> ExtInstDesc::capabilities() const {
|
|
return capabilities_range.apply(kCapabilitySpans);
|
|
}
|
|
|
|
spv_result_t LookupOpcode(spv::Op opcode, const InstructionDesc** desc) {
|
|
// Metaphor: Look for the needle in the haystack.
|
|
const InstructionDesc needle(opcode);
|
|
auto where = std::lower_bound(
|
|
kInstructionDesc.begin(), kInstructionDesc.end(), needle,
|
|
[&](const InstructionDesc& lhs, const InstructionDesc& rhs) {
|
|
return uint32_t(lhs.opcode) < uint32_t(rhs.opcode);
|
|
});
|
|
if (where != kInstructionDesc.end() && where->opcode == opcode) {
|
|
*desc = &*where;
|
|
return SPV_SUCCESS;
|
|
}
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
spv_result_t LookupOpcode(const char* name, const InstructionDesc** desc) {
|
|
// The comparison function knows to use 'name' string to compare against
|
|
// when the value is kSentinel.
|
|
const auto kSentinel = uint32_t(-1);
|
|
const NameIndex needle{{}, kSentinel};
|
|
auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
|
|
const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
|
|
const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
|
|
return std::strcmp(lhs_chars, rhs_chars) < 0;
|
|
};
|
|
|
|
auto where = std::lower_bound(kInstructionNames.begin(),
|
|
kInstructionNames.end(), needle, less);
|
|
if (where != kInstructionNames.end() &&
|
|
std::strcmp(getChars(where->name), name) == 0) {
|
|
*desc = &kInstructionDesc[where->index];
|
|
return SPV_SUCCESS;
|
|
}
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
namespace {
|
|
template <typename KEY_TYPE>
|
|
spv_result_t LookupOpcodeForEnvInternal(spv_target_env env, KEY_TYPE key,
|
|
const InstructionDesc** desc) {
|
|
const InstructionDesc* desc_proxy;
|
|
auto status = LookupOpcode(key, &desc_proxy);
|
|
if (status != SPV_SUCCESS) {
|
|
return status;
|
|
}
|
|
const auto& entry = *desc_proxy;
|
|
const auto version = spvVersionForTargetEnv(env);
|
|
if ((version >= entry.minVersion && version <= entry.lastVersion) ||
|
|
entry.extensions_range.count() > 0 ||
|
|
entry.capabilities_range.count() > 0) {
|
|
*desc = desc_proxy;
|
|
return SPV_SUCCESS;
|
|
}
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
} // namespace
|
|
|
|
spv_result_t LookupOpcodeForEnv(spv_target_env env, const char* name,
|
|
const InstructionDesc** desc) {
|
|
return LookupOpcodeForEnvInternal(env, name, desc);
|
|
}
|
|
|
|
spv_result_t LookupOpcodeForEnv(spv_target_env env, spv::Op opcode,
|
|
const InstructionDesc** desc) {
|
|
return LookupOpcodeForEnvInternal(env, opcode, desc);
|
|
}
|
|
|
|
spv_result_t LookupOperand(spv_operand_type_t type, uint32_t value,
|
|
const OperandDesc** desc) {
|
|
auto ir = OperandByValueRangeForKind(type);
|
|
if (ir.empty()) {
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
auto span = ir.apply(kOperandsByValue.data());
|
|
|
|
// Metaphor: Look for the needle in the haystack.
|
|
// The operand value is the first member.
|
|
const OperandDesc needle{value};
|
|
auto where =
|
|
std::lower_bound(span.begin(), span.end(), needle,
|
|
[&](const OperandDesc& lhs, const OperandDesc& rhs) {
|
|
return lhs.value < rhs.value;
|
|
});
|
|
if (where != span.end() && where->value == value) {
|
|
*desc = &*where;
|
|
return SPV_SUCCESS;
|
|
}
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
spv_result_t LookupOperand(spv_operand_type_t type, const char* name,
|
|
size_t name_len, const OperandDesc** desc) {
|
|
auto ir = OperandNameRangeForKind(type);
|
|
if (ir.empty()) {
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
auto span = ir.apply(kOperandNames.data());
|
|
|
|
// The comparison function knows to use (name, name_len) as the
|
|
// string to compare against when the value is kSentinel.
|
|
const auto kSentinel = uint32_t(-1);
|
|
const NameIndex needle{{}, kSentinel};
|
|
// The strings in the global string table are null-terminated, and the count
|
|
// reflects that. So always deduct 1 from its length.
|
|
auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
|
|
const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
|
|
const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
|
|
const auto content_cmp = std::strncmp(lhs_chars, rhs_chars, name_len);
|
|
if (content_cmp != 0) {
|
|
return content_cmp < 0;
|
|
}
|
|
const auto lhs_len =
|
|
lhs.index == kSentinel ? name_len : lhs.name.count() - 1;
|
|
const auto rhs_len =
|
|
rhs.index == kSentinel ? name_len : rhs.name.count() - 1;
|
|
return lhs_len < rhs_len;
|
|
};
|
|
|
|
auto where = std::lower_bound(span.begin(), span.end(), needle, less);
|
|
if (where != span.end() && where->name.count() - 1 == name_len &&
|
|
std::strncmp(getChars(where->name), name, name_len) == 0) {
|
|
*desc = &kOperandsByValue[where->index];
|
|
return SPV_SUCCESS;
|
|
}
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
spv_result_t LookupExtInst(spv_ext_inst_type_t type, const char* name,
|
|
const ExtInstDesc** desc) {
|
|
auto ir = ExtInstNameRangeForKind(type);
|
|
if (ir.empty()) {
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
auto span = ir.apply(kExtInstNames.data());
|
|
|
|
// The comparison function knows to use 'name' string to compare against
|
|
// when the value is kSentinel.
|
|
const auto kSentinel = uint32_t(-1);
|
|
const NameIndex needle{{}, kSentinel};
|
|
auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
|
|
const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
|
|
const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
|
|
return std::strcmp(lhs_chars, rhs_chars) < 0;
|
|
};
|
|
|
|
auto where = std::lower_bound(span.begin(), span.end(), needle, less);
|
|
if (where != span.end() && std::strcmp(getChars(where->name), name) == 0) {
|
|
*desc = &kExtInstByValue[where->index];
|
|
return SPV_SUCCESS;
|
|
}
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
// Finds the extended instruction description by opcode value.
|
|
// On success, returns SPV_SUCCESS and updates *desc.
|
|
spv_result_t LookupExtInst(spv_ext_inst_type_t type, uint32_t value,
|
|
const ExtInstDesc** desc) {
|
|
auto ir = ExtInstByValueRangeForKind(type);
|
|
if (ir.empty()) {
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
auto span = ir.apply(kExtInstByValue.data());
|
|
|
|
// Metaphor: Look for the needle in the haystack.
|
|
// The operand value is the first member.
|
|
const ExtInstDesc needle(value);
|
|
auto where =
|
|
std::lower_bound(span.begin(), span.end(), needle,
|
|
[&](const ExtInstDesc& lhs, const ExtInstDesc& rhs) {
|
|
return lhs.value < rhs.value;
|
|
});
|
|
if (where != span.end() && where->value == value) {
|
|
*desc = &*where;
|
|
return SPV_SUCCESS;
|
|
}
|
|
return SPV_ERROR_INVALID_LOOKUP;
|
|
}
|
|
|
|
const char* ExtensionToString(Extension extension) {
|
|
return getChars(ExtensionToIndexRange(extension));
|
|
}
|
|
|
|
bool GetExtensionFromString(const char* name, Extension* extension) {
|
|
// The comparison function knows to use 'name' string to compare against
|
|
// when the value is kSentinel.
|
|
const auto kSentinel = uint32_t(-1);
|
|
const NameValue needle{{}, kSentinel};
|
|
auto less = [&](const NameValue& lhs, const NameValue& rhs) {
|
|
const char* lhs_chars = lhs.value == kSentinel ? name : getChars(lhs.name);
|
|
const char* rhs_chars = rhs.value == kSentinel ? name : getChars(rhs.name);
|
|
return std::strcmp(lhs_chars, rhs_chars) < 0;
|
|
};
|
|
|
|
auto where = std::lower_bound(kExtensionNames.begin(), kExtensionNames.end(),
|
|
needle, less);
|
|
if (where != kExtensionNames.end() &&
|
|
std::strcmp(getChars(where->name), name) == 0) {
|
|
*extension = static_cast<Extension>(where->value);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace spvtools
|