Migrate to use unified grammar tables
Previously we keep a separate static grammar table for opcodes/
operands per SPIR-V version. This commit changes that to use a
single unified static grammar table for opcodes/operands.
This essentially changes how grammar facts are queried against
a certain target environment. There are only limited filtering
according to the desired target environment; a symbol is
considered as available as long as:
1. The target environment satisfies the minimal requirement of
the symbol; or
2. There is at least one extension enabling this symbol.
Note that the second rule assumes the extension enabling the
symbol is indeed requested in the SPIR-V code; checking that
should be the validator's work.
Also fixed a few grammar related issues:
* Rounding mode capability requirements are moved to client APIs.
* Reserved symbols not available in any extension is no longer
recognized by assembler.
diff --git a/source/opcode.cpp b/source/opcode.cpp
index 871c337..d9190cf 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -32,24 +32,10 @@
uint32_t len;
};
-// For now, assume unified1 contains up to SPIR-V 1.3 and no later
-// SPIR-V version.
-// TODO(dneto): Make one set of tables, but with version tags on a
-// per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195
-
-#include "core.insts-1.0.inc" // defines kOpcodeTableEntries_1_0
-#include "core.insts-1.1.inc" // defines kOpcodeTableEntries_1_1
-#include "core.insts-1.2.inc" // defines kOpcodeTableEntries_1_2
#include "core.insts-unified1.inc" // defines kOpcodeTableEntries_1_3
-static const spv_opcode_table_t kTable_1_0 = {
- ARRAY_SIZE(kOpcodeTableEntries_1_0), kOpcodeTableEntries_1_0};
-static const spv_opcode_table_t kTable_1_1 = {
- ARRAY_SIZE(kOpcodeTableEntries_1_1), kOpcodeTableEntries_1_1};
-static const spv_opcode_table_t kTable_1_2 = {
- ARRAY_SIZE(kOpcodeTableEntries_1_2), kOpcodeTableEntries_1_2};
-static const spv_opcode_table_t kTable_1_3 = {
- ARRAY_SIZE(kOpcodeTableEntries_1_3), kOpcodeTableEntries_1_3};
+static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries),
+ kOpcodeTableEntries};
// Represents a vendor tool entry in the SPIR-V XML Regsitry.
struct VendorTool {
@@ -89,47 +75,18 @@
}
}
-spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable,
- spv_target_env env) {
+spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) {
if (!pInstTable) return SPV_ERROR_INVALID_POINTER;
// Descriptions of each opcode. Each entry describes the format of the
// instruction that follows a particular opcode.
- switch (env) {
- case SPV_ENV_UNIVERSAL_1_0:
- case SPV_ENV_VULKAN_1_0:
- case SPV_ENV_OPENCL_1_2:
- case SPV_ENV_OPENCL_EMBEDDED_1_2:
- case SPV_ENV_OPENCL_2_0:
- case SPV_ENV_OPENCL_EMBEDDED_2_0:
- case SPV_ENV_OPENCL_2_1:
- case SPV_ENV_OPENCL_EMBEDDED_2_1:
- case SPV_ENV_OPENGL_4_0:
- case SPV_ENV_OPENGL_4_1:
- case SPV_ENV_OPENGL_4_2:
- case SPV_ENV_OPENGL_4_3:
- case SPV_ENV_OPENGL_4_5:
- *pInstTable = &kTable_1_0;
- return SPV_SUCCESS;
- case SPV_ENV_UNIVERSAL_1_1:
- *pInstTable = &kTable_1_1;
- return SPV_SUCCESS;
- case SPV_ENV_UNIVERSAL_1_2:
- case SPV_ENV_OPENCL_2_2:
- case SPV_ENV_OPENCL_EMBEDDED_2_2:
- *pInstTable = &kTable_1_2;
- return SPV_SUCCESS;
- case SPV_ENV_UNIVERSAL_1_3:
- case SPV_ENV_VULKAN_1_1:
- *pInstTable = &kTable_1_3;
- return SPV_SUCCESS;
- }
- assert(0 && "Unknown spv_target_env in spvOpcodeTableGet()");
- return SPV_ERROR_INVALID_TABLE;
+ *pInstTable = &kOpcodeTable;
+ return SPV_SUCCESS;
}
-spv_result_t spvOpcodeTableNameLookup(const spv_opcode_table table,
+spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
+ const spv_opcode_table table,
const char* name,
spv_opcode_desc* pEntry) {
if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
@@ -137,14 +94,24 @@
// TODO: This lookup of the Opcode table is suboptimal! Binary sort would be
// preferable but the table requires sorting on the Opcode name, but it's
- // static
- // const initialized and matches the order of the spec.
+ // static const initialized and matches the order of the spec.
const size_t nameLength = strlen(name);
for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) {
- if (nameLength == strlen(table->entries[opcodeIndex].name) &&
- !strncmp(name, table->entries[opcodeIndex].name, nameLength)) {
+ const spv_opcode_desc_t& entry = table->entries[opcodeIndex];
+ // We considers the current opcode as available as long as
+ // 1. The target environment satisfies the minimal requirement of the
+ // opcode; or
+ // 2. There is at least one extension enabling this opcode.
+ //
+ // Note that the second rule assumes the extension enabling this instruction
+ // is indeed requested in the SPIR-V code; checking that should be
+ // validator's work.
+ if ((static_cast<uint32_t>(env) >= entry.minVersion ||
+ entry.numExtensions > 0u) &&
+ nameLength == strlen(entry.name) &&
+ !strncmp(name, entry.name, nameLength)) {
// NOTE: Found out Opcode!
- *pEntry = &table->entries[opcodeIndex];
+ *pEntry = &entry;
return SPV_SUCCESS;
}
}
@@ -152,7 +119,8 @@
return SPV_ERROR_INVALID_LOOKUP;
}
-spv_result_t spvOpcodeTableValueLookup(const spv_opcode_table table,
+spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
+ const spv_opcode_table table,
const SpvOp opcode,
spv_opcode_desc* pEntry) {
if (!table) return SPV_ERROR_INVALID_TABLE;
@@ -160,14 +128,34 @@
const auto beg = table->entries;
const auto end = table->entries + table->count;
- spv_opcode_desc_t value{"", opcode, 0, nullptr, 0, {}, 0, 0};
+
+ spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
+ false, false, 0, nullptr, ~0u};
+
auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
return lhs.opcode < rhs.opcode;
};
- auto it = std::lower_bound(beg, end, value, comp);
- if (it != end && it->opcode == opcode) {
- *pEntry = it;
- return SPV_SUCCESS;
+
+ // We need to loop here because there can exist multiple symbols for the same
+ // opcode value, and they can be introduced in different target environments,
+ // which means they can have different minimal version requirements.
+ // Assumes the underlying table is already sorted ascendingly according to
+ // opcode value.
+ for (auto it = std::lower_bound(beg, end, needle, comp);
+ it != end && it->opcode == opcode; ++it) {
+ // We considers the current opcode as available as long as
+ // 1. The target environment satisfies the minimal requirement of the
+ // opcode; or
+ // 2. There is at least one extension enabling this opcode.
+ //
+ // Note that the second rule assumes the extension enabling this instruction
+ // is indeed requested in the SPIR-V code; checking that should be
+ // validator's work.
+ if (static_cast<uint32_t>(env) >= it->minVersion ||
+ it->numExtensions > 0u) {
+ *pEntry = it;
+ return SPV_SUCCESS;
+ }
}
return SPV_ERROR_INVALID_LOOKUP;
@@ -191,17 +179,14 @@
}
const char* spvOpcodeString(const SpvOp opcode) {
- // Use the latest SPIR-V version, which should be backward-compatible with all
- // previous ones.
-
- const auto beg = kOpcodeTableEntries_1_3;
- const auto end =
- kOpcodeTableEntries_1_3 + ARRAY_SIZE(kOpcodeTableEntries_1_3);
- spv_opcode_desc_t value{"", opcode, 0, nullptr, 0, {}, 0, 0};
+ const auto beg = kOpcodeTableEntries;
+ const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
+ spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
+ false, false, 0, nullptr, ~0u};
auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
return lhs.opcode < rhs.opcode;
};
- auto it = std::lower_bound(beg, end, value, comp);
+ auto it = std::lower_bound(beg, end, needle, comp);
if (it != end && it->opcode == opcode) {
return it->name;
}